From f8caf43cd00aa57bbbb5b6e50f941929944cb005 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 18 Apr 2024 16:11:55 +0200 Subject: [PATCH] amendmends according to review --- .../server/HsHostingServerController.java | 18 +- .../hosting/server/HsHostingServerEntity.java | 42 +-- .../server/HsHostingServerEntityPatcher.java | 3 - .../hs-booking/hs-booking-item-schemas.yaml | 4 +- .../hs-hosting/hs-hosting-server-schemas.yaml | 26 -- .../7010-hs-hosting-server.sql | 1 - .../7013-hs-hosting-server-rbac.md | 305 ++++++++++++++++++ .../7013-hs-hosting-server-rbac.sql | 172 ++++++++++ .../7018-hs-hosting-server-test-data.sql | 8 +- ...ostingServerControllerAcceptanceTest.java} | 38 +-- .../HsHostingServerEntityPatcherUnitTest.java | 12 +- .../server/HsHostingServerEntityUnitTest.java | 23 +- ...ostingServerRepositoryIntegrationTest.java | 78 +++-- 13 files changed, 550 insertions(+), 180 deletions(-) create mode 100644 src/main/resources/db/changelog/7-hs-hosting/701-hosting-server/7013-hs-hosting-server-rbac.md create mode 100644 src/main/resources/db/changelog/7-hs-hosting/701-hosting-server/7013-hs-hosting-server-rbac.sql rename src/test/java/net/hostsharing/hsadminng/hs/hosting/server/{HsServerControllerAcceptanceTest.java => HsHostingServerControllerAcceptanceTest.java} (88%) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerController.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerController.java index f3236f7f..85ce8d4b 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerController.java @@ -18,8 +18,6 @@ import java.util.List; import java.util.UUID; import java.util.function.BiConsumer; -import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange; - @RestController public class HsHostingServerController implements HsHostingServersApi { @@ -42,7 +40,7 @@ public class HsHostingServerController implements HsHostingServersApi { final var entities = serverRepo.findAllByDebitorUuid(debitorUuid); - final var resources = mapper.mapList(entities, HsServerResource.class, ENTITY_TO_RESOURCE_POSTMAPPER); + final var resources = mapper.mapList(entities, HsServerResource.class); return ResponseEntity.ok(resources); } @@ -65,7 +63,7 @@ public class HsHostingServerController implements HsHostingServersApi { .path("/api/hs/hosting/servers/{id}") .buildAndExpand(saved.getUuid()) .toUri(); - final var mapped = mapper.map(saved, HsServerResource.class, ENTITY_TO_RESOURCE_POSTMAPPER); + final var mapped = mapper.map(saved, HsServerResource.class); return ResponseEntity.created(uri).body(mapped); } @@ -81,7 +79,7 @@ public class HsHostingServerController implements HsHostingServersApi { final var result = serverRepo.findByUuid(serverUuid); return result .map(serverEntity -> ResponseEntity.ok( - mapper.map(serverEntity, HsServerResource.class, ENTITY_TO_RESOURCE_POSTMAPPER))) + mapper.map(serverEntity, HsServerResource.class))) .orElseGet(() -> ResponseEntity.notFound().build()); } @@ -114,20 +112,12 @@ public class HsHostingServerController implements HsHostingServersApi { new HsHostingServerEntityPatcher(current).apply(body); final var saved = serverRepo.save(current); - final var mapped = mapper.map(saved, HsServerResource.class, ENTITY_TO_RESOURCE_POSTMAPPER); + final var mapped = mapper.map(saved, HsServerResource.class); return ResponseEntity.ok(mapped); } - final BiConsumer ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> { - resource.setValidFrom(entity.getValidity().lower()); - if (entity.getValidity().hasUpperBound()) { - resource.setValidTo(entity.getValidity().upper().minusDays(1)); - } - }; - @SuppressWarnings("unchecked") final BiConsumer RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> { - entity.setValidity(toPostgresDateRange(resource.getValidFrom(), resource.getValidTo())); entity.putConfig(KeyValueMap.from(resource.getConfig())); }; } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerEntity.java index 147a505d..f46b2f04 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerEntity.java @@ -1,8 +1,6 @@ package net.hostsharing.hsadminng.hs.hosting.server; import io.hypersistence.utils.hibernate.type.json.JsonType; -import io.hypersistence.utils.hibernate.type.range.PostgreSQLRangeType; -import io.hypersistence.utils.hibernate.type.range.Range; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; @@ -28,15 +26,11 @@ import jakarta.persistence.Table; import jakarta.persistence.Transient; import jakarta.persistence.Version; import java.io.IOException; -import java.time.LocalDate; import java.util.HashMap; import java.util.Map; import java.util.UUID; import static java.util.Optional.ofNullable; -import static net.hostsharing.hsadminng.mapper.PostgresDateRange.lowerInclusiveFromPostgresDateRange; -import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange; -import static net.hostsharing.hsadminng.mapper.PostgresDateRange.upperInclusiveFromPostgresDateRange; 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.DELETE; @@ -44,6 +38,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN; +import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.OWNER; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.TENANT; import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.SQL.directlyFetchedByDependsOnColumn; @@ -61,7 +56,6 @@ public class HsHostingServerEntity implements Stringifyable, RbacObject { private static Stringify stringify = stringify(HsHostingServerEntity.class) .withProp(e -> e.getBookingItem().toShortString()) - .withProp(e -> e.getValidity().asString()) .withProp(HsHostingServerEntity::getCaption) .withProp(HsHostingServerEntity::getConfig) .quotedValues(false); @@ -77,11 +71,6 @@ public class HsHostingServerEntity implements Stringifyable, RbacObject { @JoinColumn(name = "bookingitemuuid") private HsBookingItemEntity bookingItem; - @Builder.Default - @Type(PostgreSQLRangeType.class) - @Column(name = "validity", columnDefinition = "daterange") - private Range validity = Range.emptyRange(LocalDate.class); - @Column(name = "caption") private String caption; @@ -94,22 +83,6 @@ public class HsHostingServerEntity implements Stringifyable, RbacObject { @Transient private PatchableMapWrapper configWrapper; - public void setValidFrom(final LocalDate validFrom) { - setValidity(toPostgresDateRange(validFrom, getValidTo())); - } - - public void setValidTo(final LocalDate validTo) { - setValidity(toPostgresDateRange(getValidFrom(), validTo)); - } - - public LocalDate getValidFrom() { - return lowerInclusiveFromPostgresDateRange(getValidity()); - } - - public LocalDate getValidTo() { - return upperInclusiveFromPostgresDateRange(getValidity()); - } - public PatchableMapWrapper getConfig() { if ( configWrapper == null ) { configWrapper = new PatchableMapWrapper(config); @@ -138,25 +111,26 @@ public class HsHostingServerEntity implements Stringifyable, RbacObject { public static RbacView rbac() { return rbacViewFor("server", HsHostingServerEntity.class) .withIdentityView(SQL.query(""" - SELECT server.uuid as uuid, bookingItemIV.idName || ':' || cleanIdentifier(server.caption) as idName + SELECT server.uuid as uuid, bookingItemIV.idName || '-' || cleanIdentifier(server.caption) as idName FROM hs_hosting_server server JOIN hs_booking_item_iv bookingItemIV ON bookingItemIV.uuid = server.bookingItemUuid """)) - .withRestrictedViewOrderBy(SQL.expression("validity")) - .withUpdatableColumns("version", "caption", "validity", "config") + .withRestrictedViewOrderBy(SQL.expression("caption")) + .withUpdatableColumns("version", "caption", "config") .importEntityAlias("bookingItem", HsBookingItemEntity.class, dependsOnColumn("bookingItemUuid"), directlyFetchedByDependsOnColumn(), NOT_NULL) - .toRole("bookingItem", ADMIN).grantPermission(INSERT) - .toRole("global", ADMIN).grantPermission(DELETE) + .toRole("bookingItem", AGENT).grantPermission(INSERT) .createRole(OWNER, (with) -> { with.incomingSuperRole("bookingItem", ADMIN); + with.permission(DELETE); + }) + .createSubRole(ADMIN, (with) -> { with.permission(UPDATE); }) - .createSubRole(ADMIN) .createSubRole(TENANT, (with) -> { with.outgoingSubRole("bookingItem", TENANT); with.permission(SELECT); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerEntityPatcher.java b/src/main/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerEntityPatcher.java index 4eaed76f..19266950 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerEntityPatcher.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerEntityPatcher.java @@ -7,7 +7,6 @@ import net.hostsharing.hsadminng.mapper.OptionalFromJson; import java.util.Optional; - public class HsHostingServerEntityPatcher implements EntityPatcher { private final HsHostingServerEntity entity; @@ -22,7 +21,5 @@ public class HsHostingServerEntityPatcher implements EntityPatcher entity.getConfig().patch(KeyValueMap.from(resource.getConfig()))); - OptionalFromJson.of(resource.getValidTo()) - .ifPresent(entity::setValidTo); } } diff --git a/src/main/resources/api-definition/hs-booking/hs-booking-item-schemas.yaml b/src/main/resources/api-definition/hs-booking/hs-booking-item-schemas.yaml index 867a0d30..4d146683 100644 --- a/src/main/resources/api-definition/hs-booking/hs-booking-item-schemas.yaml +++ b/src/main/resources/api-definition/hs-booking/hs-booking-item-schemas.yaml @@ -69,10 +69,10 @@ components: BookingResources: anyOf: - - $ref: '#/components/schemas/ServerBookingResources' + - $ref: '#/components/schemas/ManagedServerBookingResources' - $ref: '#/components/schemas/ManagedWebspaceBookingResources' - ServerBookingResources: + ManagedServerBookingResources: type: object properties: CPU: diff --git a/src/main/resources/api-definition/hs-hosting/hs-hosting-server-schemas.yaml b/src/main/resources/api-definition/hs-hosting/hs-hosting-server-schemas.yaml index c559531e..20e5d19b 100644 --- a/src/main/resources/api-definition/hs-hosting/hs-hosting-server-schemas.yaml +++ b/src/main/resources/api-definition/hs-hosting/hs-hosting-server-schemas.yaml @@ -11,18 +11,10 @@ components: format: uuid caption: type: string - validFrom: - type: string - format: date - validTo: - type: string - format: date config: $ref: '#/components/schemas/ServerConfiguration' required: - uuid - - validFrom - - validTo - config HsServerPatch: @@ -31,10 +23,6 @@ components: caption: type: string nullable: true - validTo: - type: string - format: date - nullable: true config: $ref: '#/components/schemas/ServerConfiguration' @@ -50,20 +38,11 @@ components: minLength: 3 maxLength: 80 nullable: false - validFrom: - type: string - format: date - nullable: false - validTo: - type: string - format: date - nullable: true config: $ref: '#/components/schemas/ServerConfiguration' required: - caption - debitorUuid - - validFrom - config additionalProperties: false @@ -72,11 +51,6 @@ components: anyOf: - type: object properties: - caption: - type: string - minLength: 3 - maxLength: 80 - nullable: false CPU: type: integer minimum: 1 diff --git a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-server/7010-hs-hosting-server.sql b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-server/7010-hs-hosting-server.sql index 13ec12a0..64250c95 100644 --- a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-server/7010-hs-hosting-server.sql +++ b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-server/7010-hs-hosting-server.sql @@ -9,7 +9,6 @@ create table if not exists hs_hosting_server uuid uuid unique references RbacObject (uuid), version int not null default 0, bookingItemUuid uuid not null references hs_booking_item(uuid), - validity daterange not null, caption varchar(80) not null, config jsonb not null ); diff --git a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-server/7013-hs-hosting-server-rbac.md b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-server/7013-hs-hosting-server-rbac.md new file mode 100644 index 00000000..ce381bb8 --- /dev/null +++ b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-server/7013-hs-hosting-server-rbac.md @@ -0,0 +1,305 @@ +### rbac server + +This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually. + +```mermaid +%%{init:{'flowchart':{'htmlLabels':false}}}%% +flowchart TB + +subgraph server["`**server**`"] + direction TB + style server fill:#dd4901,stroke:#274d6e,stroke-width:8px + + subgraph server:roles[ ] + style server:roles fill:#dd4901,stroke:white + + role:server:OWNER[[server:OWNER]] + role:server:ADMIN[[server:ADMIN]] + role:server:TENANT[[server:TENANT]] + end + + subgraph server:permissions[ ] + style server:permissions fill:#dd4901,stroke:white + + perm:server:INSERT{{server:INSERT}} + perm:server:DELETE{{server:DELETE}} + perm:server:UPDATE{{server:UPDATE}} + perm:server:SELECT{{server:SELECT}} + end +end + +subgraph bookingItem.debitor.debitorRel.anchorPerson["`**bookingItem.debitor.debitorRel.anchorPerson**`"] + direction TB + style bookingItem.debitor.debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph bookingItem.debitor.debitorRel.anchorPerson:roles[ ] + style bookingItem.debitor.debitorRel.anchorPerson:roles fill:#99bcdb,stroke:white + + role:bookingItem.debitor.debitorRel.anchorPerson:OWNER[[bookingItem.debitor.debitorRel.anchorPerson:OWNER]] + role:bookingItem.debitor.debitorRel.anchorPerson:ADMIN[[bookingItem.debitor.debitorRel.anchorPerson:ADMIN]] + role:bookingItem.debitor.debitorRel.anchorPerson:REFERRER[[bookingItem.debitor.debitorRel.anchorPerson:REFERRER]] + end +end + +subgraph bookingItem.debitor.partnerRel.contact["`**bookingItem.debitor.partnerRel.contact**`"] + direction TB + style bookingItem.debitor.partnerRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph bookingItem.debitor.partnerRel.contact:roles[ ] + style bookingItem.debitor.partnerRel.contact:roles fill:#99bcdb,stroke:white + + role:bookingItem.debitor.partnerRel.contact:OWNER[[bookingItem.debitor.partnerRel.contact:OWNER]] + role:bookingItem.debitor.partnerRel.contact:ADMIN[[bookingItem.debitor.partnerRel.contact:ADMIN]] + role:bookingItem.debitor.partnerRel.contact:REFERRER[[bookingItem.debitor.partnerRel.contact:REFERRER]] + end +end + +subgraph bookingItem["`**bookingItem**`"] + direction TB + style bookingItem fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph bookingItem:roles[ ] + style bookingItem:roles fill:#99bcdb,stroke:white + + role:bookingItem:OWNER[[bookingItem:OWNER]] + role:bookingItem:ADMIN[[bookingItem:ADMIN]] + role:bookingItem:AGENT[[bookingItem:AGENT]] + role:bookingItem:TENANT[[bookingItem:TENANT]] + end +end + +subgraph bookingItem.debitor.partnerRel["`**bookingItem.debitor.partnerRel**`"] + direction TB + style bookingItem.debitor.partnerRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph bookingItem.debitor.partnerRel:roles[ ] + style bookingItem.debitor.partnerRel:roles fill:#99bcdb,stroke:white + + role:bookingItem.debitor.partnerRel:OWNER[[bookingItem.debitor.partnerRel:OWNER]] + role:bookingItem.debitor.partnerRel:ADMIN[[bookingItem.debitor.partnerRel:ADMIN]] + role:bookingItem.debitor.partnerRel:AGENT[[bookingItem.debitor.partnerRel:AGENT]] + role:bookingItem.debitor.partnerRel:TENANT[[bookingItem.debitor.partnerRel:TENANT]] + end +end + +subgraph bookingItem.debitor.partnerRel.anchorPerson["`**bookingItem.debitor.partnerRel.anchorPerson**`"] + direction TB + style bookingItem.debitor.partnerRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph bookingItem.debitor.partnerRel.anchorPerson:roles[ ] + style bookingItem.debitor.partnerRel.anchorPerson:roles fill:#99bcdb,stroke:white + + role:bookingItem.debitor.partnerRel.anchorPerson:OWNER[[bookingItem.debitor.partnerRel.anchorPerson:OWNER]] + role:bookingItem.debitor.partnerRel.anchorPerson:ADMIN[[bookingItem.debitor.partnerRel.anchorPerson:ADMIN]] + role:bookingItem.debitor.partnerRel.anchorPerson:REFERRER[[bookingItem.debitor.partnerRel.anchorPerson:REFERRER]] + end +end + +subgraph bookingItem.debitorRel["`**bookingItem.debitorRel**`"] + direction TB + style bookingItem.debitorRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph bookingItem.debitorRel:roles[ ] + style bookingItem.debitorRel:roles fill:#99bcdb,stroke:white + + role:bookingItem.debitorRel:OWNER[[bookingItem.debitorRel:OWNER]] + role:bookingItem.debitorRel:ADMIN[[bookingItem.debitorRel:ADMIN]] + role:bookingItem.debitorRel:AGENT[[bookingItem.debitorRel:AGENT]] + role:bookingItem.debitorRel:TENANT[[bookingItem.debitorRel:TENANT]] + end +end + +subgraph bookingItem.debitorRel.anchorPerson["`**bookingItem.debitorRel.anchorPerson**`"] + direction TB + style bookingItem.debitorRel.anchorPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph bookingItem.debitorRel.anchorPerson:roles[ ] + style bookingItem.debitorRel.anchorPerson:roles fill:#99bcdb,stroke:white + + role:bookingItem.debitorRel.anchorPerson:OWNER[[bookingItem.debitorRel.anchorPerson:OWNER]] + role:bookingItem.debitorRel.anchorPerson:ADMIN[[bookingItem.debitorRel.anchorPerson:ADMIN]] + role:bookingItem.debitorRel.anchorPerson:REFERRER[[bookingItem.debitorRel.anchorPerson:REFERRER]] + end +end + +subgraph bookingItem.debitor.partnerRel.holderPerson["`**bookingItem.debitor.partnerRel.holderPerson**`"] + direction TB + style bookingItem.debitor.partnerRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph bookingItem.debitor.partnerRel.holderPerson:roles[ ] + style bookingItem.debitor.partnerRel.holderPerson:roles fill:#99bcdb,stroke:white + + role:bookingItem.debitor.partnerRel.holderPerson:OWNER[[bookingItem.debitor.partnerRel.holderPerson:OWNER]] + role:bookingItem.debitor.partnerRel.holderPerson:ADMIN[[bookingItem.debitor.partnerRel.holderPerson:ADMIN]] + role:bookingItem.debitor.partnerRel.holderPerson:REFERRER[[bookingItem.debitor.partnerRel.holderPerson:REFERRER]] + end +end + +subgraph bookingItem.debitorRel.contact["`**bookingItem.debitorRel.contact**`"] + direction TB + style bookingItem.debitorRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph bookingItem.debitorRel.contact:roles[ ] + style bookingItem.debitorRel.contact:roles fill:#99bcdb,stroke:white + + role:bookingItem.debitorRel.contact:OWNER[[bookingItem.debitorRel.contact:OWNER]] + role:bookingItem.debitorRel.contact:ADMIN[[bookingItem.debitorRel.contact:ADMIN]] + role:bookingItem.debitorRel.contact:REFERRER[[bookingItem.debitorRel.contact:REFERRER]] + end +end + +subgraph bookingItem.debitorRel.holderPerson["`**bookingItem.debitorRel.holderPerson**`"] + direction TB + style bookingItem.debitorRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph bookingItem.debitorRel.holderPerson:roles[ ] + style bookingItem.debitorRel.holderPerson:roles fill:#99bcdb,stroke:white + + role:bookingItem.debitorRel.holderPerson:OWNER[[bookingItem.debitorRel.holderPerson:OWNER]] + role:bookingItem.debitorRel.holderPerson:ADMIN[[bookingItem.debitorRel.holderPerson:ADMIN]] + role:bookingItem.debitorRel.holderPerson:REFERRER[[bookingItem.debitorRel.holderPerson:REFERRER]] + end +end + +subgraph bookingItem.debitor.refundBankAccount["`**bookingItem.debitor.refundBankAccount**`"] + direction TB + style bookingItem.debitor.refundBankAccount fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph bookingItem.debitor.refundBankAccount:roles[ ] + style bookingItem.debitor.refundBankAccount:roles fill:#99bcdb,stroke:white + + role:bookingItem.debitor.refundBankAccount:OWNER[[bookingItem.debitor.refundBankAccount:OWNER]] + role:bookingItem.debitor.refundBankAccount:ADMIN[[bookingItem.debitor.refundBankAccount:ADMIN]] + role:bookingItem.debitor.refundBankAccount:REFERRER[[bookingItem.debitor.refundBankAccount:REFERRER]] + end +end + +subgraph bookingItem.debitor.debitorRel.contact["`**bookingItem.debitor.debitorRel.contact**`"] + direction TB + style bookingItem.debitor.debitorRel.contact fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph bookingItem.debitor.debitorRel.contact:roles[ ] + style bookingItem.debitor.debitorRel.contact:roles fill:#99bcdb,stroke:white + + role:bookingItem.debitor.debitorRel.contact:OWNER[[bookingItem.debitor.debitorRel.contact:OWNER]] + role:bookingItem.debitor.debitorRel.contact:ADMIN[[bookingItem.debitor.debitorRel.contact:ADMIN]] + role:bookingItem.debitor.debitorRel.contact:REFERRER[[bookingItem.debitor.debitorRel.contact:REFERRER]] + end +end + +subgraph bookingItem.debitor["`**bookingItem.debitor**`"] + direction TB + style bookingItem.debitor fill:#99bcdb,stroke:#274d6e,stroke-width:8px +end + +subgraph bookingItem.debitor.debitorRel.holderPerson["`**bookingItem.debitor.debitorRel.holderPerson**`"] + direction TB + style bookingItem.debitor.debitorRel.holderPerson fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph bookingItem.debitor.debitorRel.holderPerson:roles[ ] + style bookingItem.debitor.debitorRel.holderPerson:roles fill:#99bcdb,stroke:white + + role:bookingItem.debitor.debitorRel.holderPerson:OWNER[[bookingItem.debitor.debitorRel.holderPerson:OWNER]] + role:bookingItem.debitor.debitorRel.holderPerson:ADMIN[[bookingItem.debitor.debitorRel.holderPerson:ADMIN]] + role:bookingItem.debitor.debitorRel.holderPerson:REFERRER[[bookingItem.debitor.debitorRel.holderPerson:REFERRER]] + end +end + +subgraph bookingItem.debitor.debitorRel["`**bookingItem.debitor.debitorRel**`"] + direction TB + style bookingItem.debitor.debitorRel fill:#99bcdb,stroke:#274d6e,stroke-width:8px + + subgraph bookingItem.debitor.debitorRel:roles[ ] + style bookingItem.debitor.debitorRel:roles fill:#99bcdb,stroke:white + + role:bookingItem.debitor.debitorRel:OWNER[[bookingItem.debitor.debitorRel:OWNER]] + role:bookingItem.debitor.debitorRel:ADMIN[[bookingItem.debitor.debitorRel:ADMIN]] + role:bookingItem.debitor.debitorRel:AGENT[[bookingItem.debitor.debitorRel:AGENT]] + role:bookingItem.debitor.debitorRel:TENANT[[bookingItem.debitor.debitorRel:TENANT]] + end +end + +%% granting roles to roles +role:global:ADMIN -.-> role:bookingItem.debitor.debitorRel.anchorPerson:OWNER +role:bookingItem.debitor.debitorRel.anchorPerson:OWNER -.-> role:bookingItem.debitor.debitorRel.anchorPerson:ADMIN +role:bookingItem.debitor.debitorRel.anchorPerson:ADMIN -.-> role:bookingItem.debitor.debitorRel.anchorPerson:REFERRER +role:global:ADMIN -.-> role:bookingItem.debitor.debitorRel.holderPerson:OWNER +role:bookingItem.debitor.debitorRel.holderPerson:OWNER -.-> role:bookingItem.debitor.debitorRel.holderPerson:ADMIN +role:bookingItem.debitor.debitorRel.holderPerson:ADMIN -.-> role:bookingItem.debitor.debitorRel.holderPerson:REFERRER +role:global:ADMIN -.-> role:bookingItem.debitor.debitorRel.contact:OWNER +role:bookingItem.debitor.debitorRel.contact:OWNER -.-> role:bookingItem.debitor.debitorRel.contact:ADMIN +role:bookingItem.debitor.debitorRel.contact:ADMIN -.-> role:bookingItem.debitor.debitorRel.contact:REFERRER +role:global:ADMIN -.-> role:bookingItem.debitor.debitorRel:OWNER +role:bookingItem.debitor.debitorRel:OWNER -.-> role:bookingItem.debitor.debitorRel:ADMIN +role:bookingItem.debitor.debitorRel:ADMIN -.-> role:bookingItem.debitor.debitorRel:AGENT +role:bookingItem.debitor.debitorRel:AGENT -.-> role:bookingItem.debitor.debitorRel:TENANT +role:bookingItem.debitor.debitorRel.contact:ADMIN -.-> role:bookingItem.debitor.debitorRel:TENANT +role:bookingItem.debitor.debitorRel:TENANT -.-> role:bookingItem.debitor.debitorRel.anchorPerson:REFERRER +role:bookingItem.debitor.debitorRel:TENANT -.-> role:bookingItem.debitor.debitorRel.holderPerson:REFERRER +role:bookingItem.debitor.debitorRel:TENANT -.-> role:bookingItem.debitor.debitorRel.contact:REFERRER +role:bookingItem.debitor.debitorRel.anchorPerson:ADMIN -.-> role:bookingItem.debitor.debitorRel:OWNER +role:bookingItem.debitor.debitorRel.holderPerson:ADMIN -.-> role:bookingItem.debitor.debitorRel:AGENT +role:global:ADMIN -.-> role:bookingItem.debitor.refundBankAccount:OWNER +role:bookingItem.debitor.refundBankAccount:OWNER -.-> role:bookingItem.debitor.refundBankAccount:ADMIN +role:bookingItem.debitor.refundBankAccount:ADMIN -.-> role:bookingItem.debitor.refundBankAccount:REFERRER +role:bookingItem.debitor.refundBankAccount:ADMIN -.-> role:bookingItem.debitor.debitorRel:AGENT +role:bookingItem.debitor.debitorRel:AGENT -.-> role:bookingItem.debitor.refundBankAccount:REFERRER +role:global:ADMIN -.-> role:bookingItem.debitor.partnerRel.anchorPerson:OWNER +role:bookingItem.debitor.partnerRel.anchorPerson:OWNER -.-> role:bookingItem.debitor.partnerRel.anchorPerson:ADMIN +role:bookingItem.debitor.partnerRel.anchorPerson:ADMIN -.-> role:bookingItem.debitor.partnerRel.anchorPerson:REFERRER +role:global:ADMIN -.-> role:bookingItem.debitor.partnerRel.holderPerson:OWNER +role:bookingItem.debitor.partnerRel.holderPerson:OWNER -.-> role:bookingItem.debitor.partnerRel.holderPerson:ADMIN +role:bookingItem.debitor.partnerRel.holderPerson:ADMIN -.-> role:bookingItem.debitor.partnerRel.holderPerson:REFERRER +role:global:ADMIN -.-> role:bookingItem.debitor.partnerRel.contact:OWNER +role:bookingItem.debitor.partnerRel.contact:OWNER -.-> role:bookingItem.debitor.partnerRel.contact:ADMIN +role:bookingItem.debitor.partnerRel.contact:ADMIN -.-> role:bookingItem.debitor.partnerRel.contact:REFERRER +role:global:ADMIN -.-> role:bookingItem.debitor.partnerRel:OWNER +role:bookingItem.debitor.partnerRel:OWNER -.-> role:bookingItem.debitor.partnerRel:ADMIN +role:bookingItem.debitor.partnerRel:ADMIN -.-> role:bookingItem.debitor.partnerRel:AGENT +role:bookingItem.debitor.partnerRel:AGENT -.-> role:bookingItem.debitor.partnerRel:TENANT +role:bookingItem.debitor.partnerRel.contact:ADMIN -.-> role:bookingItem.debitor.partnerRel:TENANT +role:bookingItem.debitor.partnerRel:TENANT -.-> role:bookingItem.debitor.partnerRel.anchorPerson:REFERRER +role:bookingItem.debitor.partnerRel:TENANT -.-> role:bookingItem.debitor.partnerRel.holderPerson:REFERRER +role:bookingItem.debitor.partnerRel:TENANT -.-> role:bookingItem.debitor.partnerRel.contact:REFERRER +role:bookingItem.debitor.partnerRel.anchorPerson:ADMIN -.-> role:bookingItem.debitor.partnerRel:OWNER +role:bookingItem.debitor.partnerRel.holderPerson:ADMIN -.-> role:bookingItem.debitor.partnerRel:AGENT +role:bookingItem.debitor.partnerRel:ADMIN -.-> role:bookingItem.debitor.debitorRel:ADMIN +role:bookingItem.debitor.partnerRel:AGENT -.-> role:bookingItem.debitor.debitorRel:AGENT +role:bookingItem.debitor.debitorRel:AGENT -.-> role:bookingItem.debitor.partnerRel:TENANT +role:global:ADMIN -.-> role:bookingItem.debitorRel.anchorPerson:OWNER +role:bookingItem.debitorRel.anchorPerson:OWNER -.-> role:bookingItem.debitorRel.anchorPerson:ADMIN +role:bookingItem.debitorRel.anchorPerson:ADMIN -.-> role:bookingItem.debitorRel.anchorPerson:REFERRER +role:global:ADMIN -.-> role:bookingItem.debitorRel.holderPerson:OWNER +role:bookingItem.debitorRel.holderPerson:OWNER -.-> role:bookingItem.debitorRel.holderPerson:ADMIN +role:bookingItem.debitorRel.holderPerson:ADMIN -.-> role:bookingItem.debitorRel.holderPerson:REFERRER +role:global:ADMIN -.-> role:bookingItem.debitorRel.contact:OWNER +role:bookingItem.debitorRel.contact:OWNER -.-> role:bookingItem.debitorRel.contact:ADMIN +role:bookingItem.debitorRel.contact:ADMIN -.-> role:bookingItem.debitorRel.contact:REFERRER +role:global:ADMIN -.-> role:bookingItem.debitorRel:OWNER +role:bookingItem.debitorRel:OWNER -.-> role:bookingItem.debitorRel:ADMIN +role:bookingItem.debitorRel:ADMIN -.-> role:bookingItem.debitorRel:AGENT +role:bookingItem.debitorRel:AGENT -.-> role:bookingItem.debitorRel:TENANT +role:bookingItem.debitorRel.contact:ADMIN -.-> role:bookingItem.debitorRel:TENANT +role:bookingItem.debitorRel:TENANT -.-> role:bookingItem.debitorRel.anchorPerson:REFERRER +role:bookingItem.debitorRel:TENANT -.-> role:bookingItem.debitorRel.holderPerson:REFERRER +role:bookingItem.debitorRel:TENANT -.-> role:bookingItem.debitorRel.contact:REFERRER +role:bookingItem.debitorRel.anchorPerson:ADMIN -.-> role:bookingItem.debitorRel:OWNER +role:bookingItem.debitorRel.holderPerson:ADMIN -.-> role:bookingItem.debitorRel:AGENT +role:bookingItem.debitorRel:AGENT -.-> role:bookingItem:OWNER +role:bookingItem:OWNER -.-> role:bookingItem:ADMIN +role:bookingItem.debitorRel:AGENT -.-> role:bookingItem:ADMIN +role:bookingItem:ADMIN -.-> role:bookingItem:AGENT +role:bookingItem:AGENT -.-> role:bookingItem:TENANT +role:bookingItem:TENANT -.-> role:bookingItem.debitorRel:TENANT +role:bookingItem:ADMIN ==> role:server:OWNER +role:server:OWNER ==> role:server:ADMIN +role:server:ADMIN ==> role:server:TENANT +role:server:TENANT ==> role:bookingItem:TENANT + +%% granting permissions to roles +role:bookingItem:AGENT ==> perm:server:INSERT +role:server:OWNER ==> perm:server:DELETE +role:server:ADMIN ==> perm:server:UPDATE +role:server:TENANT ==> perm:server:SELECT + +``` diff --git a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-server/7013-hs-hosting-server-rbac.sql b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-server/7013-hs-hosting-server-rbac.sql new file mode 100644 index 00000000..01919118 --- /dev/null +++ b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-server/7013-hs-hosting-server-rbac.sql @@ -0,0 +1,172 @@ +--liquibase formatted sql +-- This code generated was by RbacViewPostgresGenerator, do not amend manually. + + +-- ============================================================================ +--changeset hs-hosting-server-rbac-OBJECT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRelatedRbacObject('hs_hosting_server'); +--// + + +-- ============================================================================ +--changeset hs-hosting-server-rbac-ROLE-DESCRIPTORS:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRbacRoleDescriptors('hsHostingServer', 'hs_hosting_server'); +--// + + +-- ============================================================================ +--changeset hs-hosting-server-rbac-insert-trigger:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Creates the roles, grants and permission for the AFTER INSERT TRIGGER. + */ + +create or replace procedure buildRbacSystemForHsHostingServer( + NEW hs_hosting_server +) + language plpgsql as $$ + +declare + newBookingItem hs_booking_item; + +begin + call enterTriggerForObjectUuid(NEW.uuid); + + SELECT * FROM hs_booking_item WHERE uuid = NEW.bookingItemUuid INTO newBookingItem; + assert newBookingItem.uuid is not null, format('newBookingItem must not be null for NEW.bookingItemUuid = %s', NEW.bookingItemUuid); + + + perform createRoleWithGrants( + hsHostingServerOWNER(NEW), + permissions => array['DELETE'], + incomingSuperRoles => array[hsBookingItemADMIN(newBookingItem)] + ); + + perform createRoleWithGrants( + hsHostingServerADMIN(NEW), + permissions => array['UPDATE'], + incomingSuperRoles => array[hsHostingServerOWNER(NEW)] + ); + + perform createRoleWithGrants( + hsHostingServerTENANT(NEW), + permissions => array['SELECT'], + incomingSuperRoles => array[hsHostingServerADMIN(NEW)], + outgoingSubRoles => array[hsBookingItemTENANT(newBookingItem)] + ); + + call leaveTriggerForObjectUuid(NEW.uuid); +end; $$; + +/* + AFTER INSERT TRIGGER to create the role+grant structure for a new hs_hosting_server row. + */ + +create or replace function insertTriggerForHsHostingServer_tf() + returns trigger + language plpgsql + strict as $$ +begin + call buildRbacSystemForHsHostingServer(NEW); + return NEW; +end; $$; + +create trigger insertTriggerForHsHostingServer_tg + after insert on hs_hosting_server + for each row +execute procedure insertTriggerForHsHostingServer_tf(); +--// + + +-- ============================================================================ +--changeset hs-hosting-server-rbac-INSERT:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + +/* + Creates INSERT INTO hs_hosting_server permissions for the related hs_booking_item rows. + */ +do language plpgsql $$ + declare + row hs_booking_item; + begin + call defineContext('create INSERT INTO hs_hosting_server permissions for the related hs_booking_item rows'); + + FOR row IN SELECT * FROM hs_booking_item + LOOP + call grantPermissionToRole( + createPermission(row.uuid, 'INSERT', 'hs_hosting_server'), + hsBookingItemAGENT(row)); + END LOOP; + END; +$$; + +/** + Adds hs_hosting_server INSERT permission to specified role of new hs_booking_item rows. +*/ +create or replace function hs_hosting_server_hs_booking_item_insert_tf() + returns trigger + language plpgsql + strict as $$ +begin + call grantPermissionToRole( + createPermission(NEW.uuid, 'INSERT', 'hs_hosting_server'), + hsBookingItemAGENT(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_hosting_server_hs_booking_item_insert_tg + after insert on hs_booking_item + for each row +execute procedure hs_hosting_server_hs_booking_item_insert_tf(); + +/** + Checks if the user or assumed roles are allowed to insert a row to hs_hosting_server, + 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_hosting_server_insert_permission_missing_tf() + returns trigger + language plpgsql as $$ +begin + raise exception '[403] insert into hs_hosting_server not allowed for current subjects % (%)', + currentSubjects(), currentSubjectsUuids(); +end; $$; + +create trigger hs_hosting_server_insert_permission_check_tg + before insert on hs_hosting_server + for each row + when ( not hasInsertPermission(NEW.bookingItemUuid, 'INSERT', 'hs_hosting_server') ) + execute procedure hs_hosting_server_insert_permission_missing_tf(); +--// + +-- ============================================================================ +--changeset hs-hosting-server-rbac-IDENTITY-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- + + call generateRbacIdentityViewFromQuery('hs_hosting_server', + $idName$ + SELECT server.uuid as uuid, bookingItemIV.idName || '-' || cleanIdentifier(server.caption) as idName + FROM hs_hosting_server server + JOIN hs_booking_item_iv bookingItemIV ON bookingItemIV.uuid = server.bookingItemUuid + $idName$); +--// + +-- ============================================================================ +--changeset hs-hosting-server-rbac-RESTRICTED-VIEW:1 endDelimiter:--// +-- ---------------------------------------------------------------------------- +call generateRbacRestrictedView('hs_hosting_server', + $orderBy$ + caption + $orderBy$, + $updates$ + version = new.version, + caption = new.caption, + config = new.config + $updates$); +--// + diff --git a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-server/7018-hs-hosting-server-test-data.sql b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-server/7018-hs-hosting-server-test-data.sql index ba22dabd..bf1ac243 100644 --- a/src/main/resources/db/changelog/7-hs-hosting/701-hosting-server/7018-hs-hosting-server-test-data.sql +++ b/src/main/resources/db/changelog/7-hs-hosting/701-hosting-server/7018-hs-hosting-server-test-data.sql @@ -36,10 +36,10 @@ begin raise notice 'creating test hosting-server: %', givenPartnerNumber::text || givenDebitorSuffix::text; raise notice '- using debitor (%): %', relatedDebitor.uuid, relatedDebitor; insert - into hs_hosting_server (uuid, bookingitemuuid, caption, validity, config) - values (uuid_generate_v4(), relatedBookingItem.uuid, 'some ManagedServer', daterange('20221001', null, '[]'), '{ "CPU": 2, "SDD": 512, "extra": 42 }'::jsonb), - (uuid_generate_v4(), relatedBookingItem.uuid, 'another CloudServer', daterange('20230115', '20240415', '[)'), '{ "CPU": 2, "HDD": 1024, "extra": 42 }'::jsonb), - (uuid_generate_v4(), relatedBookingItem.uuid, 'some Whatever', daterange('20240401', null, '[]'), '{ "CPU": 1, "SDD": 512, "HDD": 2048, "extra": 42 }'::jsonb); + into hs_hosting_server (uuid, bookingitemuuid, caption, config) + values (uuid_generate_v4(), relatedBookingItem.uuid, 'some ManagedServer', '{ "CPU": 2, "SDD": 512, "extra": 42 }'::jsonb), + (uuid_generate_v4(), relatedBookingItem.uuid, 'another CloudServer', '{ "CPU": 2, "HDD": 1024, "extra": 42 }'::jsonb), + (uuid_generate_v4(), relatedBookingItem.uuid, 'some Whatever', '{ "CPU": 1, "SDD": 512, "HDD": 2048, "extra": 42 }'::jsonb); end; $$; --// diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/server/HsServerControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerControllerAcceptanceTest.java similarity index 88% rename from src/test/java/net/hostsharing/hsadminng/hs/hosting/server/HsServerControllerAcceptanceTest.java rename to src/test/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerControllerAcceptanceTest.java index 1221def3..04ab39fb 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/server/HsServerControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerControllerAcceptanceTest.java @@ -1,6 +1,5 @@ package net.hostsharing.hsadminng.hs.hosting.server; -import io.hypersistence.utils.hibernate.type.range.Range; import io.restassured.RestAssured; import io.restassured.http.ContentType; import net.hostsharing.hsadminng.HsadminNgApplication; @@ -16,7 +15,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDate; import java.util.Map; import java.util.UUID; @@ -30,7 +28,7 @@ import static org.hamcrest.Matchers.matchesRegex; classes = { HsadminNgApplication.class, JpaAttempt.class } ) @Transactional -class HsServerControllerAcceptanceTest extends ContextBasedTestWithCleanup { +class HsHostingServerControllerAcceptanceTest extends ContextBasedTestWithCleanup { @LocalServerPort private Integer port; @@ -70,8 +68,6 @@ class HsServerControllerAcceptanceTest extends ContextBasedTestWithCleanup { [ { "caption": "some ManagedServer", - "validFrom": "2022-10-01", - "validTo": null, "config": { "CPU": 2, "SDD": 512, @@ -80,8 +76,6 @@ class HsServerControllerAcceptanceTest extends ContextBasedTestWithCleanup { }, { "caption": "another CloudServer", - "validFrom": "2023-01-15", - "validTo": "2024-04-14", "config": { "CPU": 2, "HDD": 1024, @@ -90,8 +84,6 @@ class HsServerControllerAcceptanceTest extends ContextBasedTestWithCleanup { }, { "caption": "some Whatever", - "validFrom": "2024-04-01", - "validTo": null, "config": { "CPU": 1, "HDD": 2048, @@ -122,8 +114,7 @@ class HsServerControllerAcceptanceTest extends ContextBasedTestWithCleanup { { "bookingItemUuid": "%s", "caption": "some new CloudServer", - "config": { "CPU": 3, "extra": 42 }, - "validFrom": "2024-04-17" + "config": { "CPU": 3, "extra": 42 } } """.formatted(givenBookingItem.getUuid())) .port(port) @@ -135,8 +126,7 @@ class HsServerControllerAcceptanceTest extends ContextBasedTestWithCleanup { .body("", lenientlyEquals(""" { "caption": "some new CloudServer", - "config": { "CPU": 3, "extra": 42 }, - "validFrom": "2024-04-17" + "config": { "CPU": 3, "extra": 42 } } """)) .header("Location", matchesRegex("http://localhost:[1-9][0-9]*/api/hs/hosting/servers/[^/]*")) @@ -172,9 +162,7 @@ class HsServerControllerAcceptanceTest extends ContextBasedTestWithCleanup { .body("", lenientlyEquals(""" { "caption": "some ManagedServer", - "validFrom": "2022-10-01", - "validTo": null, - "config": { + "config": { "CPU": 2, "SDD": 512, "extra": 42 @@ -221,8 +209,6 @@ class HsServerControllerAcceptanceTest extends ContextBasedTestWithCleanup { .body("", lenientlyEquals(""" { "caption": "some ManagedServer", - "validFrom": "2022-10-01", - "validTo": null, "config": { "CPU": 2, "SDD": 512, @@ -247,8 +233,6 @@ class HsServerControllerAcceptanceTest extends ContextBasedTestWithCleanup { .contentType(ContentType.JSON) .body(""" { - "validFrom": "2020-06-05", - "validTo": "2022-12-31", "config": { "CPU": "4", "HDD": null, @@ -264,9 +248,7 @@ class HsServerControllerAcceptanceTest extends ContextBasedTestWithCleanup { .contentType(ContentType.JSON) .body("", lenientlyEquals(""" { - "caption": "some test-booking", - "validFrom": "2022-11-01", - "validTo": "2022-12-31", + "caption": "some test-server", "config": { "CPU": "4", "SSD": "4096", @@ -278,10 +260,8 @@ class HsServerControllerAcceptanceTest extends ContextBasedTestWithCleanup { // finally, the server is actually updated context.define("superuser-alex@hostsharing.net"); assertThat(serverRepo.findByUuid(givenServer.getUuid())).isPresent().get() - .matches(mandate -> { - assertThat(mandate.getBookingItem().toShortString()).isEqualTo("D-1000111:some CloudServer"); - assertThat(mandate.getValidFrom()).isEqualTo("2022-11-01"); - assertThat(mandate.getValidTo()).isEqualTo("2022-12-31"); + .matches(server -> { + assertThat(server.toString()).isEqualTo("HsHostingServerEntity(D-1000111:some CloudServer, some test-server, { CPU: 4, SSD: 4096, something: 1 })"); return true; }); } @@ -341,10 +321,8 @@ class HsServerControllerAcceptanceTest extends ContextBasedTestWithCleanup { final var newServer = HsHostingServerEntity.builder() .uuid(UUID.randomUUID()) .bookingItem(givenBookingItem("First", "some CloudServer")) - .caption("some test-booking") + .caption("some test-server") .config(Map.ofEntries(resources)) - .validity(Range.closedOpen( - LocalDate.parse("2022-11-01"), LocalDate.parse("2023-03-31"))) .build(); return serverRepo.save(newServer); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerEntityPatcherUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerEntityPatcherUnitTest.java index 052c3e9d..d186946b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerEntityPatcherUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerEntityPatcherUnitTest.java @@ -1,6 +1,5 @@ package net.hostsharing.hsadminng.hs.hosting.server; -import io.hypersistence.utils.hibernate.type.range.Range; import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsServerPatchResource; import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorEntity; import net.hostsharing.hsadminng.mapper.KeyValueMap; @@ -12,7 +11,6 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import jakarta.persistence.EntityManager; -import java.time.LocalDate; import java.util.Map; import java.util.UUID; import java.util.stream.Stream; @@ -33,8 +31,6 @@ class HsHostingServerEntityPatcherUnitTest extends PatchUnitTestBase< > { private static final UUID INITIAL_BOOKING_ITEM_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"); private static final Map INITIAL_CONFIG = patchMap( entry("CPU", 1), @@ -73,7 +69,6 @@ class HsHostingServerEntityPatcherUnitTest extends PatchUnitTestBase< entity.setBookingItem(TEST_BOOKING_ITEM); entity.getConfig().putAll(KeyValueMap.from(INITIAL_CONFIG)); entity.setCaption(INITIAL_CAPTION); - entity.setValidity(Range.closedInfinite(GIVEN_VALID_FROM)); return entity; } @@ -101,12 +96,7 @@ class HsHostingServerEntityPatcherUnitTest extends PatchUnitTestBase< PATCH_CONFIG, HsHostingServerEntity::putConfig, PATCHED_CONFIG) - .notNullable(), - new JsonNullableProperty<>( - "validto", - HsServerPatchResource::setValidTo, - PATCHED_VALID_TO, - HsHostingServerEntity::setValidTo) + .notNullable() ); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerEntityUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerEntityUnitTest.java index c1a94286..3cae06e2 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerEntityUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerEntityUnitTest.java @@ -2,17 +2,13 @@ package net.hostsharing.hsadminng.hs.hosting.server; import org.junit.jupiter.api.Test; -import java.time.LocalDate; import java.util.Map; import static java.util.Map.entry; import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_BOOKING_ITEM; -import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange; import static org.assertj.core.api.Assertions.assertThat; class HsHostingServerEntityUnitTest { - public static final LocalDate GIVEN_VALID_FROM = LocalDate.parse("2020-01-01"); - public static final LocalDate GIVEN_VALID_TO = LocalDate.parse("2030-12-31"); final HsHostingServerEntity givenServer = HsHostingServerEntity.builder() .bookingItem(TEST_BOOKING_ITEM) @@ -21,7 +17,6 @@ class HsHostingServerEntityUnitTest { entry("CPUs", 2), entry("SSD-storage", 512), entry("HDD-storage", 2048))) - .validity(toPostgresDateRange(GIVEN_VALID_FROM, GIVEN_VALID_TO)) .build(); @Test @@ -29,7 +24,7 @@ class HsHostingServerEntityUnitTest { final var result = givenServer.toString(); assertThat(result).isEqualTo( - "HsServerEntity(D-1000100:test booking item, [2020-01-01,2031-01-01), some caption, { CPUs: 2, HDD-storage: 2048, SSD-storage: 512 })"); + "HsHostingServerEntity(D-1000100:test booking item, some caption, { CPUs: 2, HDD-storage: 2048, SSD-storage: 512 })"); } @Test @@ -38,20 +33,4 @@ class HsHostingServerEntityUnitTest { assertThat(result).isEqualTo("D-1000100:test booking item:some caption"); } - - @Test - void settingValidFromKeepsValidTo() { - givenServer.setValidFrom(LocalDate.parse("2023-12-31")); - assertThat(givenServer.getValidFrom()).isEqualTo(LocalDate.parse("2023-12-31")); - assertThat(givenServer.getValidTo()).isEqualTo(GIVEN_VALID_TO); - - } - - @Test - void settingValidToKeepsValidFrom() { - givenServer.setValidTo(LocalDate.parse("2024-12-31")); - assertThat(givenServer.getValidFrom()).isEqualTo(GIVEN_VALID_FROM); - assertThat(givenServer.getValidTo()).isEqualTo(LocalDate.parse("2024-12-31")); - } - } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerRepositoryIntegrationTest.java index 6814a717..b2ca1b5e 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hosting/server/HsHostingServerRepositoryIntegrationTest.java @@ -1,6 +1,5 @@ package net.hostsharing.hsadminng.hs.hosting.server; -import io.hypersistence.utils.hibernate.type.range.Range; import net.hostsharing.hsadminng.context.Context; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity; import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRepository; @@ -21,7 +20,6 @@ import org.springframework.orm.jpa.JpaSystemException; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import jakarta.servlet.http.HttpServletRequest; -import java.time.LocalDate; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -77,8 +75,6 @@ class HsHostingServerRepositoryIntegrationTest extends ContextBasedTestWithClean final var newServer = HsHostingServerEntity.builder() .bookingItem(givenBookingItem) .caption("some new booking server") - .validity(Range.closedOpen( - LocalDate.parse("2020-01-01"), LocalDate.parse("2023-01-01"))) .build(); return toCleanup(serverRepo.save(newServer)); }); @@ -105,8 +101,6 @@ class HsHostingServerRepositoryIntegrationTest extends ContextBasedTestWithClean final var newServer = HsHostingServerEntity.builder() .bookingItem(givenBookingItem) .caption("some new booking server") - .validity(Range.closedOpen( - LocalDate.parse("2020-01-01"), LocalDate.parse("2023-01-01"))) .build(); return toCleanup(serverRepo.save(newServer)); }); @@ -115,27 +109,27 @@ class HsHostingServerRepositoryIntegrationTest extends ContextBasedTestWithClean final var all = rawRoleRepo.findAll(); assertThat(distinctRoleNamesOf(all)).containsExactlyInAnyOrder(Array.from( initialRoleNames, - "hs_hosting_server#D-1000111:someCloudServer:somenewbookingserver:ADMIN", - "hs_hosting_server#D-1000111:someCloudServer:somenewbookingserver:OWNER", - "hs_hosting_server#D-1000111:someCloudServer:somenewbookingserver:TENANT")); + "hs_hosting_server#D-1000111-someCloudServer-somenewbookingserver:ADMIN", + "hs_hosting_server#D-1000111-someCloudServer-somenewbookingserver:OWNER", + "hs_hosting_server#D-1000111-someCloudServer-somenewbookingserver:TENANT")); assertThat(distinctGrantDisplaysOf(rawGrantRepo.findAll())) .map(s -> s.replace("hs_office_", "")) .containsExactlyInAnyOrder(fromFormatted( initialGrantNames, // global-admin - "{ grant perm:hs_hosting_server#D-1000111:someCloudServer:somenewbookingserver:DELETE to role:global#global:ADMIN by system and assume }", + "{ grant perm:hs_hosting_server#D-1000111-someCloudServer-somenewbookingserver:DELETE to role:hs_hosting_server#D-1000111-someCloudServer-somenewbookingserver:OWNER by system and assume }", // owner - "{ grant perm:hs_hosting_server#D-1000111:someCloudServer:somenewbookingserver:UPDATE to role:hs_hosting_server#D-1000111:someCloudServer:somenewbookingserver:OWNER by system and assume }", - "{ grant role:hs_hosting_server#D-1000111:someCloudServer:somenewbookingserver:OWNER to role:hs_booking_item#D-1000111:someCloudServer:ADMIN by system and assume }", + "{ grant perm:hs_hosting_server#D-1000111-someCloudServer-somenewbookingserver:UPDATE to role:hs_hosting_server#D-1000111-someCloudServer-somenewbookingserver:ADMIN by system and assume }", // admin - "{ grant role:hs_hosting_server#D-1000111:someCloudServer:somenewbookingserver:ADMIN to role:hs_hosting_server#D-1000111:someCloudServer:somenewbookingserver:OWNER by system and assume }", + "{ grant role:hs_hosting_server#D-1000111-someCloudServer-somenewbookingserver:ADMIN to role:hs_hosting_server#D-1000111-someCloudServer-somenewbookingserver:OWNER by system and assume }", + "{ grant role:hs_hosting_server#D-1000111-someCloudServer-somenewbookingserver:OWNER to role:hs_booking_item#D-1000111-someCloudServer:ADMIN by system and assume }", // tenant - "{ grant role:hs_hosting_server#D-1000111:someCloudServer:somenewbookingserver:TENANT to role:hs_hosting_server#D-1000111:someCloudServer:somenewbookingserver:ADMIN by system and assume }", - "{ grant perm:hs_hosting_server#D-1000111:someCloudServer:somenewbookingserver:SELECT to role:hs_hosting_server#D-1000111:someCloudServer:somenewbookingserver:TENANT by system and assume }", - "{ grant role:hs_booking_item#D-1000111:someCloudServer:TENANT to role:hs_hosting_server#D-1000111:someCloudServer:somenewbookingserver:TENANT by system and assume }", + "{ grant role:hs_hosting_server#D-1000111-someCloudServer-somenewbookingserver:TENANT to role:hs_hosting_server#D-1000111-someCloudServer-somenewbookingserver:ADMIN by system and assume }", + "{ grant perm:hs_hosting_server#D-1000111-someCloudServer-somenewbookingserver:SELECT to role:hs_hosting_server#D-1000111-someCloudServer-somenewbookingserver:TENANT by system and assume }", + "{ grant role:hs_booking_item#D-1000111-someCloudServer:TENANT to role:hs_hosting_server#D-1000111-someCloudServer-somenewbookingserver:TENANT by system and assume }", null)); } @@ -162,9 +156,9 @@ class HsHostingServerRepositoryIntegrationTest extends ContextBasedTestWithClean // then allTheseServersAreReturned( result, - "HsServerEntity(D-1000212:some PrivateCloud, [2022-10-01,), some ManagedServer, { CPU: 2, SDD: 512, extra: 42 })", - "HsServerEntity(D-1000212:some PrivateCloud, [2023-01-15,2024-04-15), another CloudServer, { CPU: 2, HDD: 1024, extra: 42 })", - "HsServerEntity(D-1000212:some PrivateCloud, [2024-04-01,), some Whatever, { CPU: 1, HDD: 2048, SDD: 512, extra: 42 })"); + "HsHostingServerEntity(D-1000212:some PrivateCloud, another CloudServer, { CPU: 2, HDD: 1024, extra: 42 })", + "HsHostingServerEntity(D-1000212:some PrivateCloud, some ManagedServer, { CPU: 2, SDD: 512, extra: 42 })", + "HsHostingServerEntity(D-1000212:some PrivateCloud, some Whatever, { CPU: 1, HDD: 2048, SDD: 512, extra: 42 })"); } @Test @@ -179,9 +173,9 @@ class HsHostingServerRepositoryIntegrationTest extends ContextBasedTestWithClean // then: exactlyTheseServersAreReturned( result, - "HsServerEntity(D-1000111:some PrivateCloud, [2022-10-01,), some ManagedServer, { CPU: 2, SDD: 512, extra: 42 })", - "HsServerEntity(D-1000111:some PrivateCloud, [2023-01-15,2024-04-15), another CloudServer, { CPU: 2, HDD: 1024, extra: 42 })", - "HsServerEntity(D-1000111:some PrivateCloud, [2024-04-01,), some Whatever, { CPU: 1, HDD: 2048, SDD: 512, extra: 42 })"); + "HsHostingServerEntity(D-1000111:some PrivateCloud, another CloudServer, { CPU: 2, HDD: 1024, extra: 42 })", + "HsHostingServerEntity(D-1000111:some PrivateCloud, some ManagedServer, { CPU: 2, SDD: 512, extra: 42 })", + "HsHostingServerEntity(D-1000111:some PrivateCloud, some Whatever, { CPU: 1, HDD: 2048, SDD: 512, extra: 42 })"); } } @@ -191,7 +185,7 @@ class HsHostingServerRepositoryIntegrationTest extends ContextBasedTestWithClean @Test public void hostsharingAdmin_canUpdateArbitraryServer() { // given - final var givenServerUuid = givenSomeTemporaryServer(1000111).getUuid(); + final var givenServerUuid = givenSomeTemporaryServer("First").getUuid(); // when final var result = jpaAttempt.transacted(() -> { @@ -200,8 +194,6 @@ class HsHostingServerRepositoryIntegrationTest extends ContextBasedTestWithClean foundServer.getConfig().put("CPUs", 2); foundServer.getConfig().remove("SSD-storage"); foundServer.getConfig().put("HSD-storage", 2048); - foundServer.setValidity(Range.closedOpen( - LocalDate.parse("2019-05-17"), LocalDate.parse("2023-01-01"))); return toCleanup(serverRepo.save(foundServer)); }); @@ -227,7 +219,7 @@ class HsHostingServerRepositoryIntegrationTest extends ContextBasedTestWithClean public void globalAdmin_withoutAssumedRole_canDeleteAnyServer() { // given context("superuser-alex@hostsharing.net", null); - final var givenServer = givenSomeTemporaryServer(1000111); + final var givenServer = givenSomeTemporaryServer("First"); // when final var result = jpaAttempt.transacted(() -> { @@ -244,10 +236,10 @@ class HsHostingServerRepositoryIntegrationTest extends ContextBasedTestWithClean } @Test - public void nonGlobalAdmin_canNotDeleteTheirRelatedServer() { + public void relatedOwner_canDeleteTheirRelatedServer() { // given context("superuser-alex@hostsharing.net", null); - final var givenServer = givenSomeTemporaryServer(1000111); + final var givenServer = givenSomeTemporaryServer("First"); // when final var result = jpaAttempt.transacted(() -> { @@ -257,6 +249,28 @@ class HsHostingServerRepositoryIntegrationTest extends ContextBasedTestWithClean serverRepo.deleteByUuid(givenServer.getUuid()); }); + // then + result.assertSuccessful(); + assertThat(jpaAttempt.transacted(() -> { + context("superuser-fran@hostsharing.net", null); + return serverRepo.findByUuid(givenServer.getUuid()); + }).assertSuccessful().returnedValue()).isEmpty(); + } + + @Test + public void relatedAdmin_canNotDeleteTheirRelatedServer() { + // given + context("superuser-alex@hostsharing.net", null); + final var givenServer = givenSomeTemporaryServer("First"); + + // when + final var result = jpaAttempt.transacted(() -> { + context("person-FirbySusan@example.com", "hs_hosting_server#D-1000111-someCloudServer-sometempbookingserver:ADMIN"); + assertThat(serverRepo.findByUuid(givenServer.getUuid())).isPresent(); + + serverRepo.deleteByUuid(givenServer.getUuid()); + }); + // then result.assertExceptionWithRootCauseMessage( JpaSystemException.class, @@ -273,7 +287,7 @@ class HsHostingServerRepositoryIntegrationTest extends ContextBasedTestWithClean context("superuser-alex@hostsharing.net"); final var initialRoleNames = Array.from(distinctRoleNamesOf(rawRoleRepo.findAll())); final var initialGrantNames = Array.from(distinctGrantDisplaysOf(rawGrantRepo.findAll())); - final var givenServer = givenSomeTemporaryServer(1000111); + final var givenServer = givenSomeTemporaryServer("First"); // when final var result = jpaAttempt.transacted(() -> { @@ -308,15 +322,13 @@ class HsHostingServerRepositoryIntegrationTest extends ContextBasedTestWithClean "[creating hosting-server test-data 1000313, hs_hosting_server, INSERT]"); } - private HsHostingServerEntity givenSomeTemporaryServer(final int debitorNumber) { + private HsHostingServerEntity givenSomeTemporaryServer(final String debitorName) { return jpaAttempt.transacted(() -> { context("superuser-alex@hostsharing.net"); - final var givenBookingItem = givenBookingItem("First", "some CloudServer"); + final var givenBookingItem = givenBookingItem(debitorName, "some CloudServer"); final var newServer = HsHostingServerEntity.builder() .bookingItem(givenBookingItem) .caption("some temp booking server") - .validity(Range.closedOpen( - LocalDate.parse("2020-01-01"), LocalDate.parse("2023-01-01"))) .config(Map.ofEntries( entry("CPUs", 1), entry("SSD-storage", 256)))