From 57cf316c00833a4630b5c308359574d7a85f6509 Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Thu, 4 Aug 2022 09:09:06 +0200 Subject: [PATCH] for customer findAll()->findCustomerByOptionalPrefix(...) --- .../hs/hscustomer/CustomerController.java | 6 +- .../hs/hscustomer/CustomerRepository.java | 16 ++++- .../db/changelog/2022-07-28-005-rbac-base.sql | 22 ++++--- .../CustomerControllerRestTest.java | 27 ++++++-- .../CustomerControllerUnitTest.java | 8 +-- .../CustomerRepositoryIntegrationTest.java | 62 ++++++++++++++----- 6 files changed, 104 insertions(+), 37 deletions(-) diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerController.java b/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerController.java index ec13941b..e3b2362a 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerController.java @@ -22,13 +22,14 @@ public class CustomerController { @Transactional public List listCustomers( @RequestHeader(value = "current-user") String userName, - @RequestHeader(value = "assumed-roles", required = false) String assumedRoles + @RequestHeader(value = "assumed-roles", required = false) String assumedRoles, + @RequestParam(required = false) String prefix ) { context.setCurrentUser(userName); if (assumedRoles != null && !assumedRoles.isBlank()) { context.assumeRoles(assumedRoles); } - return customerRepository.findAll(); + return customerRepository.findCustomerByOptionalPrefix(prefix); } @PostMapping(value = "/api/customers") @@ -48,5 +49,4 @@ public class CustomerController { } return customerRepository.save(customer); } - } diff --git a/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepository.java b/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepository.java index d9f954aa..51ed90d9 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepository.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepository.java @@ -1,11 +1,21 @@ package net.hostsharing.hsadminng.hs.hscustomer; -import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.Repository; +import org.springframework.data.repository.query.Param; import java.util.List; +import java.util.Optional; import java.util.UUID; -public interface CustomerRepository extends JpaRepository { +public interface CustomerRepository extends Repository { + + + Optional findByUuid(UUID id); + + @Query("SELECT c FROM CustomerEntity c WHERE :prefix is null or c.prefix like concat(:prefix, '%')") + List findCustomerByOptionalPrefix(@Param("prefix") String prefix); + + CustomerEntity save(final CustomerEntity entity); - List findByPrefixLike(final String prefix); } diff --git a/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql index 3666b4b6..6b5819fd 100644 --- a/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql +++ b/src/main/resources/db/changelog/2022-07-28-005-rbac-base.sql @@ -111,11 +111,18 @@ declare objectUuid uuid; begin if TG_OP = 'INSERT' then - insert - into RbacObject (objectTable) - values (TG_TABLE_NAME) - returning uuid into objectUuid; - NEW.uuid = objectUuid; + if NEW.uuid is null then + insert + into RbacObject (objectTable) + values (TG_TABLE_NAME) + returning uuid into objectUuid; + NEW.uuid = objectUuid; + else + insert + into RbacObject (uuid, objectTable) + values (NEW.uuid, TG_TABLE_NAME) + returning uuid into objectUuid; + end if; return NEW; else raise exception 'invalid usage of TRIGGER AFTER INSERT'; @@ -138,7 +145,8 @@ create table RbacRole ( uuid uuid primary key references RbacReference (uuid) on delete cascade, objectUuid uuid references RbacObject (uuid) not null, - roleType RbacRoleType not null + roleType RbacRoleType not null, + unique (objectUuid, roleType) ); create type RbacRoleDescriptor as @@ -753,7 +761,7 @@ grant all privileges on all tables in schema public to restricted; */ drop view if exists rbacrole_rv; create or replace view rbacrole_rv as -select r.*, o.objectTable, +select DISTINCT r.*, o.objectTable, findIdNameByObjectUuid(o.objectTable, o.uuid) as objectIdName from rbacrole as r join rbacobject as o on o.uuid=r.objectuuid diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerRestTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerRestTest.java index ed1f1196..c646d389 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerRestTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerRestTest.java @@ -12,7 +12,7 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import static java.util.Arrays.asList; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -27,13 +27,13 @@ class CustomerControllerRestTest { CustomerRepository customerRepositoryMock; @Test - void apiCustomersWillReturnCustomersFromRepository() throws Exception { + void apiCustomersWillReturnAllCustomersFromRepositoryIfNoCriteriaGiven() throws Exception { // given - when(customerRepositoryMock.findAll()).thenReturn(asList(TestCustomer.xxx, TestCustomer.yyy)); + when(customerRepositoryMock.findCustomerByOptionalPrefix(null)).thenReturn(asList(TestCustomer.xxx, TestCustomer.yyy)); // when - final var pacs = mockMvc.perform(MockMvcRequestBuilders + mockMvc.perform(MockMvcRequestBuilders .get("/api/customers") .header("current-user", "mike@hostsharing.net") .accept(MediaType.APPLICATION_JSON)) @@ -44,4 +44,23 @@ class CustomerControllerRestTest { .andExpect(jsonPath("$[0].prefix", is(TestCustomer.xxx.getPrefix()))) .andExpect(jsonPath("$[1].reference", is(TestCustomer.yyy.getReference()))); } + + @Test + void apiCustomersWillReturnMatchingCustomersFromRepositoryIfCriteriaGiven() throws Exception { + + // given + when(customerRepositoryMock.findCustomerByOptionalPrefix("x")).thenReturn(asList(TestCustomer.xxx)); + + // when + mockMvc.perform(MockMvcRequestBuilders + .get("/api/customers") + .header("current-user", "mike@hostsharing.net") + .param("prefix", "x") + .accept(MediaType.APPLICATION_JSON)) + + // then + .andExpect(status().isOk()) + .andExpect(jsonPath("$", hasSize(1))) + .andExpect(jsonPath("$[0].prefix", is(TestCustomer.xxx.getPrefix()))); + } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerUnitTest.java index 01ccd926..30541c50 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerControllerUnitTest.java @@ -27,10 +27,10 @@ class CustomerControllerUnitTest { void apiCustomersWillReturnCustomersFromRepository() throws Exception { // given - when(customerRepositoryMock.findAll()).thenReturn(asList(TestCustomer.xxx, TestCustomer.yyy)); + when(customerRepositoryMock.findCustomerByOptionalPrefix(null)).thenReturn(asList(TestCustomer.xxx, TestCustomer.yyy)); // when - final var pacs = customerController.listCustomers("mike@hostsharing.net", null); + final var pacs = customerController.listCustomers("mike@hostsharing.net", null, null); // then assertThat(pacs).hasSize(2); @@ -42,10 +42,10 @@ class CustomerControllerUnitTest { void findAllWithAssumedCustomerAdminRole() throws Exception { // given - when(customerRepositoryMock.findAll()).thenReturn(singletonList(TestCustomer.yyy)); + when(customerRepositoryMock.findCustomerByOptionalPrefix(null)).thenReturn(singletonList(TestCustomer.yyy)); // when - final var pacs = customerController.listCustomers("mike@hostsharing.net", "customer#yyy.admin"); + final var pacs = customerController.listCustomers("mike@hostsharing.net", "customer#yyy.admin", null); // then assertThat(pacs).hasSize(1); diff --git a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepositoryIntegrationTest.java b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepositoryIntegrationTest.java index 0e34c0e1..7c510b0d 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepositoryIntegrationTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/hscustomer/CustomerRepositoryIntegrationTest.java @@ -1,8 +1,6 @@ package net.hostsharing.hsadminng.hs.hscustomer; import net.hostsharing.hsadminng.context.Context; -import net.hostsharing.hsadminng.hs.hscustomer.CustomerEntity; -import net.hostsharing.hsadminng.hs.hscustomer.CustomerRepository; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -40,13 +38,17 @@ class CustomerRepositoryIntegrationTest { currentUser("mike@hostsharing.net"); // when - final var newCustomer = new CustomerEntity( - UUID.randomUUID(), "xxx", 90001, "admin@xxx.example.com"); - final var result = customerRepository.save(newCustomer); + + final var attempt = attempt(em, () -> { + final var newCustomer = new CustomerEntity( + UUID.randomUUID(), "xxx", 90001, "admin@xxx.example.com"); + return customerRepository.save(newCustomer); + }); // then - assertThat(result).isNotNull().extracting(CustomerEntity::getUuid).isNotNull(); - assertThatCustomerIsPersisted(result); + assertThat(attempt.wasSuccessful()).isTrue(); + assertThat(attempt.returnedResult()).isNotNull().extracting(CustomerEntity::getUuid).isNotNull(); + assertThatCustomerIsPersisted(attempt.returnedResult()); } @Test @@ -88,8 +90,8 @@ class CustomerRepositoryIntegrationTest { } private void assertThatCustomerIsPersisted(final CustomerEntity saved) { - final var found = customerRepository.findById(saved.getUuid()); - assertThat(found).hasValue(saved); + final var found = customerRepository.findByUuid(saved.getUuid()); + assertThat(found).isNotEmpty().get().usingRecursiveComparison().isEqualTo(saved); } } @@ -102,7 +104,7 @@ class CustomerRepositoryIntegrationTest { currentUser("mike@hostsharing.net"); // when - final var result = customerRepository.findAll(); + final var result = customerRepository.findCustomerByOptionalPrefix(null); // then exactlyTheseCustomersAreReturned(result, "aaa", "aab", "aac"); @@ -115,7 +117,7 @@ class CustomerRepositoryIntegrationTest { assumedRoles("global#hostsharing.admin"); // when - final var result = customerRepository.findAll(); + final var result = customerRepository.findCustomerByOptionalPrefix(null); then: exactlyTheseCustomersAreReturned(result, "aaa", "aab", "aac"); @@ -127,7 +129,7 @@ class CustomerRepositoryIntegrationTest { currentUser("admin@aaa.example.com"); // when: - final var result = customerRepository.findAll(); + final var result = customerRepository.findCustomerByOptionalPrefix(null); // then: exactlyTheseCustomersAreReturned(result, "aaa"); @@ -138,7 +140,7 @@ class CustomerRepositoryIntegrationTest { currentUser("admin@aaa.example.com"); assumedRoles("package#aaa00.admin"); - final var result = customerRepository.findAll(); + final var result = customerRepository.findCustomerByOptionalPrefix(null); exactlyTheseCustomersAreReturned(result, "aaa"); } @@ -152,7 +154,7 @@ class CustomerRepositoryIntegrationTest { // when final var attempt = attempt( em, - () -> customerRepository.findAll()); + () -> customerRepository.findCustomerByOptionalPrefix(null)); // then attempt.assertExceptionWithRootCauseMessage( @@ -166,7 +168,7 @@ class CustomerRepositoryIntegrationTest { final var attempt = attempt( em, - () -> customerRepository.findAll()); + () -> customerRepository.findCustomerByOptionalPrefix(null)); attempt.assertExceptionWithRootCauseMessage( JpaSystemException.class, @@ -181,7 +183,7 @@ class CustomerRepositoryIntegrationTest { final var attempt = attempt( em, - () -> customerRepository.findAll()); + () -> customerRepository.findCustomerByOptionalPrefix(null)); attempt.assertExceptionWithRootCauseMessage( JpaSystemException.class, @@ -190,6 +192,34 @@ class CustomerRepositoryIntegrationTest { } + @Nested + class FindByPrefixLike { + + @Test + public void hostsharingAdmin_withoutAssumedRole_canViewAllCustomers() { + // given + currentUser("mike@hostsharing.net"); + + // when + final var result = customerRepository.findCustomerByOptionalPrefix("aab"); + + // then + exactlyTheseCustomersAreReturned(result, "aab"); + } + + @Test + public void customerAdmin_withoutAssumedRole_canViewOnlyItsOwnCustomer() { + // given: + currentUser("admin@aaa.example.com"); + + // when: + final var result = customerRepository.findCustomerByOptionalPrefix("aab"); + + // then: + exactlyTheseCustomersAreReturned(result); + } + } + void currentUser(final String currentUser) { context.setCurrentUser(currentUser); assertThat(context.getCurrentUser()).as("precondition").isEqualTo(currentUser);