From d47928066c31677c7bdc857b91f9e7d4d95ccf1e Mon Sep 17 00:00:00 2001 From: Alex Kuleshov Date: Thu, 7 Dec 2023 10:28:12 -0400 Subject: [PATCH 1/3] draft --- .../authserver/controller/AclController.java | 9 +++++-- .../example/authserver/entity/PageView.java | 21 +++++++++++++++ .../authserver/repo/AclRepository.java | 17 ++++++++++++ .../authserver/service/AclService.java | 26 +++++++++++++++++-- .../example/authserver/ZanzibarImplTest.java | 16 ++++++------ 5 files changed, 77 insertions(+), 12 deletions(-) create mode 100644 auth/src/main/java/org/example/authserver/entity/PageView.java diff --git a/auth/src/main/java/org/example/authserver/controller/AclController.java b/auth/src/main/java/org/example/authserver/controller/AclController.java index 937b8ff..2470fd2 100644 --- a/auth/src/main/java/org/example/authserver/controller/AclController.java +++ b/auth/src/main/java/org/example/authserver/controller/AclController.java @@ -6,11 +6,16 @@ import com.google.common.base.Stopwatch; import jakarta.validation.Valid; import java.util.HashSet; +import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; import lombok.extern.slf4j.Slf4j; import org.example.authserver.config.AppProperties; import org.example.authserver.entity.AclsRequestDTO; +import org.example.authserver.entity.PageView; import org.example.authserver.repo.SubscriptionRepository; import org.example.authserver.service.AclService; import org.example.authserver.service.CacheService; @@ -42,8 +47,8 @@ public AclController( } @GetMapping("/list") - public Set listAcl() { - return repository.findAll(); + public PageView listAcl(@RequestParam String namespace, @RequestParam String object, @RequestParam List relations, @Min(value = 1, message = "min: 1") @RequestParam(value = "page", defaultValue = "1") Integer page, @Max(value = 100, message = "max: 100") @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) { + return repository.findAll(namespace, object, relations, page, pageSize); } @PostMapping("/create") diff --git a/auth/src/main/java/org/example/authserver/entity/PageView.java b/auth/src/main/java/org/example/authserver/entity/PageView.java new file mode 100644 index 0000000..a2fade2 --- /dev/null +++ b/auth/src/main/java/org/example/authserver/entity/PageView.java @@ -0,0 +1,21 @@ +package org.example.authserver.entity; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.util.Collection; + +@SuperBuilder +@Data +@NoArgsConstructor +public class PageView { + private long total; + private long pages; + private long currentPage; + private Collection data; + + public static long calculateTotalPages(long totalRecords, long pageSize) { + return (totalRecords + pageSize - 1) / pageSize; + } +} \ No newline at end of file diff --git a/auth/src/main/java/org/example/authserver/repo/AclRepository.java b/auth/src/main/java/org/example/authserver/repo/AclRepository.java index 1b2d61c..3c70ab7 100644 --- a/auth/src/main/java/org/example/authserver/repo/AclRepository.java +++ b/auth/src/main/java/org/example/authserver/repo/AclRepository.java @@ -3,13 +3,29 @@ import java.util.List; import java.util.Set; import org.example.authserver.entity.AclEntity; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; +import javax.annotation.Nullable; + @Repository public interface AclRepository extends CrudRepository { + Page findByNamespaceLikeAndObjectLikeAndRelationIn( + String namespace, String object, List relations, @Nullable Pageable pageable); + + Page findByNamespaceLike(String namespace, PageRequest pageRequest); + + Page findByNamespaceLikeAndObjectLike(String namespace, String object, PageRequest pageRequest); + + Page findByNsobjectLike(String nsobject, PageRequest pageRequest); + + long countByNamespaceLikeAndObjectLikeAndRelationIn(String namespace, String object, List relations); + List findAll(); Set findAllByNsobjectAndUser(String nsobject, String user); @@ -42,4 +58,5 @@ Set findAllByUsersetNamespaceAndUsersetObjectAndUsersetRelationAndUse String usNamespace, String usObject, String usRel); + } diff --git a/auth/src/main/java/org/example/authserver/service/AclService.java b/auth/src/main/java/org/example/authserver/service/AclService.java index 1187fc4..7317aec 100644 --- a/auth/src/main/java/org/example/authserver/service/AclService.java +++ b/auth/src/main/java/org/example/authserver/service/AclService.java @@ -2,12 +2,20 @@ import authserver.acl.Acl; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; import lombok.extern.slf4j.Slf4j; import org.example.authserver.entity.AclEntity; +import org.example.authserver.entity.PageView; import org.example.authserver.repo.AclRepository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -21,8 +29,22 @@ public AclService(AclRepository repository) { this.repository = repository; } - public Set findAll() { - return repository.findAll().stream().map(AclEntity::toAcl).collect(Collectors.toSet()); + public PageView findAll(String namespace, String object, List relations, Integer pageNum, Integer pageSize) { + PageRequest pageRequest = PageRequest.of(pageNum, pageSize); + //String nsobject = String.format("%s:%s", namespace, object); + //Page page = repository.findByNamespaceLikeAndObjectLikeAndRelationIn(namespace, object, relations, pageRequest); + Page page = repository.findByNsobjectLike(namespace, pageRequest); + Set acls = page.getContent() + .stream().map(AclEntity::toAcl).collect(Collectors.toSet()); + + long count = repository.countByNamespaceLikeAndObjectLikeAndRelationIn(namespace, object, relations); + + return PageView.builder() + .data(acls) + .currentPage(pageNum) + .pages(PageView.calculateTotalPages(count, pageSize)) + .total(count) + .build(); } public Acl findOneById(String id) { diff --git a/auth/src/test/java/org/example/authserver/ZanzibarImplTest.java b/auth/src/test/java/org/example/authserver/ZanzibarImplTest.java index 8787b84..aae8f47 100644 --- a/auth/src/test/java/org/example/authserver/ZanzibarImplTest.java +++ b/auth/src/test/java/org/example/authserver/ZanzibarImplTest.java @@ -63,7 +63,7 @@ void check() { .collect(Collectors.toSet()); AclRelationConfig config = prepConfig(); - Mockito.doReturn(acls).when(aclService).findAll(); + Mockito.doReturn(acls).when(aclService).findAll(namespace, object, relations, page, pageSize); Mockito.doReturn(aclsDocReadme) .when(aclService) .findAllByNamespaceAndObjectAndUser(eq("doc"), eq("readme"), eq(principal)); @@ -106,7 +106,7 @@ void relations() { .collect(Collectors.toSet()); AclRelationConfig config = prepConfig(); - Mockito.doReturn(acls).when(aclService).findAll(); + Mockito.doReturn(acls).when(aclService).findAll(namespace, object, relations, page, pageSize); Mockito.doReturn(aclsDocReadme) .when(aclService) .findAllByNamespaceAndObjectAndUser(eq("doc"), eq("readme"), eq(principal)); @@ -142,7 +142,7 @@ void relationsSimple() { AclRelationConfig config = new AclRelationConfig(); - Mockito.doReturn(acls).when(aclService).findAll(); + Mockito.doReturn(acls).when(aclService).findAll(namespace, object, relations, page, pageSize); Mockito.doReturn(acls) .when(aclService) .findAllByNamespaceAndObjectAndUser(eq("namespace"), eq("object"), eq(principal)); @@ -185,7 +185,7 @@ void relationsContacts() { Set configs = prepConfig2(); - Mockito.doReturn(acls).when(aclService).findAll(); + Mockito.doReturn(acls).when(aclService).findAll(namespace, object, relations, page, pageSize); Mockito.doReturn(acls) .when(aclService) .findAllByNamespaceAndObjectAndUser(eq("contact"), eq("uuid1"), eq(principal)); @@ -240,7 +240,7 @@ void checkContactUsersTest() { .filter(acl -> acl.getNamespace().equals("api") && acl.getObject().equals("contact")) .collect(Collectors.toSet()); - Mockito.doReturn(acls).when(aclService).findAll(); + Mockito.doReturn(acls).when(aclService).findAll(namespace, object, relations, page, pageSize); Mockito.doReturn(aclsGroupContactusers) .when(aclService) .findAllByNamespaceAndObjectAndUser(eq("group"), eq("contactusers"), eq(principal)); @@ -319,7 +319,7 @@ void wildcardRelationsTest() { acl -> acl.getNamespace().equals("contact") && acl.getObject().equals(uuid)) .collect(Collectors.toSet()); - Mockito.doReturn(acls).when(aclService).findAll(); + Mockito.doReturn(acls).when(aclService).findAll(namespace, object, relations, page, pageSize); Mockito.doReturn(aclsGroupContactusers) .when(aclService) .findAllByNamespaceAndObjectAndUser(eq("group"), eq("contactusers"), eq(principal)); @@ -379,7 +379,7 @@ void exclusionTest() { String principal = entry.getKey(); boolean expected = entry.getValue(); - Mockito.doReturn(acls).when(aclService).findAll(); + Mockito.doReturn(acls).when(aclService).findAll(namespace, object, relations, page, pageSize); Mockito.doReturn(acls) .when(aclService) .findAllByNamespaceAndObjectAndUser(eq("group"), eq("object"), anyString()); @@ -433,7 +433,7 @@ void intersectionTest() { String principal = entry.getKey(); boolean expected = entry.getValue(); - Mockito.doReturn(acls).when(aclService).findAll(); + Mockito.doReturn(acls).when(aclService).findAll(namespace, object, relations, page, pageSize); Mockito.doReturn(acls) .when(aclService) .findAllByNamespaceAndObjectAndUser(eq("group"), eq("object"), anyString()); From cf2121b9e836778383e1d84a4a0836a77a40d4fc Mon Sep 17 00:00:00 2001 From: Alex Kuleshov Date: Fri, 6 Dec 2024 11:03:31 -0400 Subject: [PATCH 2/3] spring boot 3.4 --- auth/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth/pom.xml b/auth/pom.xml index 94996d3..f528d0e 100644 --- a/auth/pom.xml +++ b/auth/pom.xml @@ -10,7 +10,7 @@ org.springframework.boot spring-boot-starter-parent - 3.1.0 + 3.4.0 From 521dcbe9c3c99bf8a07d0323a9c87b537935c4ff Mon Sep 17 00:00:00 2001 From: Alex Kuleshov Date: Fri, 6 Dec 2024 12:56:54 -0400 Subject: [PATCH 3/3] acl lookup api added acl duplication check shutdown hook added spring boot 3.4 --- acl/pom.xml | 7 +- acl/src/main/java/authserver/acl/Acl.java | 18 ++ auth/pom.xml | 38 +-- .../authserver/config/ApiKeyFilter.java | 1 - .../authserver/config/ProtectionConfig.java | 4 +- .../config/UrlFilteringInterceptor.java | 2 +- .../example/authserver/config/WebConfig.java | 10 +- .../authserver/controller/AclController.java | 58 ++++- .../controller/MappingController.java | 3 +- .../example/authserver/entity/PageView.java | 5 +- .../exception/NotSortableFieldException.java | 3 + .../authserver/repo/AclRepository.java | 16 +- .../authserver/repo/filter/AclFilter.java | 217 ++++++++++++++++++ .../authserver/repo/filter/Condition.java | 37 +++ .../authserver/repo/filter/Filter.java | 99 ++++++++ .../repo/filter/FilterComparison.java | 10 + .../repo/filter/FilterFieldTypes.java | 23 ++ .../authserver/service/AclService.java | 95 ++++++-- .../authserver/service/AuthService.java | 3 +- .../service/zanzibar/MappingService.java | 4 +- .../util/shutdown/ShutDownHandler.java | 5 +- .../main/resources/application-dev.properties | 4 +- .../resources/application-docker.properties | 4 +- .../application-splittest.properties | 4 +- .../src/main/resources/application.properties | 4 +- .../example/authserver/ZanzibarImplTest.java | 48 ++-- 26 files changed, 617 insertions(+), 105 deletions(-) create mode 100644 auth/src/main/java/org/example/authserver/exception/NotSortableFieldException.java create mode 100644 auth/src/main/java/org/example/authserver/repo/filter/AclFilter.java create mode 100644 auth/src/main/java/org/example/authserver/repo/filter/Condition.java create mode 100644 auth/src/main/java/org/example/authserver/repo/filter/Filter.java create mode 100644 auth/src/main/java/org/example/authserver/repo/filter/FilterComparison.java create mode 100644 auth/src/main/java/org/example/authserver/repo/filter/FilterFieldTypes.java diff --git a/acl/pom.xml b/acl/pom.xml index 8965fe5..9ca66c5 100644 --- a/acl/pom.xml +++ b/acl/pom.xml @@ -15,10 +15,15 @@ + + org.json + json + 20231013 + org.projectlombok lombok - 1.18.22 + 1.18.34 compile diff --git a/acl/src/main/java/authserver/acl/Acl.java b/acl/src/main/java/authserver/acl/Acl.java index fc42580..48489b5 100644 --- a/acl/src/main/java/authserver/acl/Acl.java +++ b/acl/src/main/java/authserver/acl/Acl.java @@ -13,6 +13,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import lombok.extern.java.Log; +import org.json.JSONObject; @Log @Data @@ -91,6 +92,23 @@ public int hashCode() { namespace, object, relation, user, usersetNamespace, usersetObject, usersetRelation); } + @Override + public String toString() { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("id", id.toString()); + jsonObject.put("namespace", namespace); + jsonObject.put("object", object); + jsonObject.put("relation", relation); + jsonObject.put("user", user); + jsonObject.put("usersetNamespace", usersetNamespace); + jsonObject.put("usersetObject", usersetObject); + jsonObject.put("usersetRelation", usersetRelation); + jsonObject.put("created", created); + jsonObject.put("updated", updated); + + return jsonObject.toString(); + } + private static Acl parseAcl(String aclExpr) { Matcher m = aclExprPattern.matcher(aclExpr); if (!m.find()) { diff --git a/auth/pom.xml b/auth/pom.xml index 178744e..b5a21ce 100644 --- a/auth/pom.xml +++ b/auth/pom.xml @@ -70,7 +70,7 @@ org.springdoc springdoc-openapi-starter-webmvc-ui - 2.1.0 + 2.3.0 io.micrometer @@ -93,12 +93,12 @@ com.jayway.jsonpath json-path - 2.8.0 + 2.9.0 org.apache.commons commons-text - 1.10.0 + 1.11.0 io.jsonwebtoken @@ -108,50 +108,50 @@ io.envoyproxy.controlplane api - 1.0.37 + 1.0.46 io.grpc grpc-netty-shaded - 1.55.1 + 1.68.2 io.grpc grpc-protobuf - 1.55.1 + 1.68.2 io.grpc grpc-stub - 1.55.1 + 1.68.2 io.grpc grpc-services - 1.55.1 + 1.68.2 commons-codec commons-codec - 1.15 + 1.17.0 javax.xml.bind jaxb-api - 2.3.1 + 2.4.0-b180830.0359 com.newrelic.agent.java newrelic-api - 8.3.0 + 8.11.1 org.projectlombok lombok - 1.18.26 + 1.18.34 @@ -169,12 +169,12 @@ net.logstash.logback logstash-logback-encoder - 7.3 + 8.0 ch.qos.logback.contrib logback-json-classic - 0.1.2 + 0.1.5 runtime @@ -183,11 +183,6 @@ 0.1.5 runtime - - org.json - json - 20230227 - @@ -199,6 +194,11 @@ false + + maven_central + Maven Central + https://repo.maven.apache.org/maven2/ + diff --git a/auth/src/main/java/org/example/authserver/config/ApiKeyFilter.java b/auth/src/main/java/org/example/authserver/config/ApiKeyFilter.java index 19a03a4..4a8336a 100644 --- a/auth/src/main/java/org/example/authserver/config/ApiKeyFilter.java +++ b/auth/src/main/java/org/example/authserver/config/ApiKeyFilter.java @@ -6,7 +6,6 @@ import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Objects; - import org.example.authserver.controller.MappingController; import org.springframework.http.HttpStatus; diff --git a/auth/src/main/java/org/example/authserver/config/ProtectionConfig.java b/auth/src/main/java/org/example/authserver/config/ProtectionConfig.java index 25f058c..0e0f1d5 100644 --- a/auth/src/main/java/org/example/authserver/config/ProtectionConfig.java +++ b/auth/src/main/java/org/example/authserver/config/ProtectionConfig.java @@ -17,9 +17,7 @@ FilterRegistrationBean apiKeyFilter() { final FilterRegistrationBean filterFilterRegistrationBean = new FilterRegistrationBean<>(); filterFilterRegistrationBean.setFilter(new ApiKeyFilter(properties)); - filterFilterRegistrationBean.addUrlPatterns( - "/mapping/clear", - "/mapping/delete/**"); + filterFilterRegistrationBean.addUrlPatterns("/mapping/clear", "/mapping/delete/**"); return filterFilterRegistrationBean; } diff --git a/auth/src/main/java/org/example/authserver/config/UrlFilteringInterceptor.java b/auth/src/main/java/org/example/authserver/config/UrlFilteringInterceptor.java index bf4b96a..643d36f 100644 --- a/auth/src/main/java/org/example/authserver/config/UrlFilteringInterceptor.java +++ b/auth/src/main/java/org/example/authserver/config/UrlFilteringInterceptor.java @@ -12,7 +12,7 @@ public class UrlFilteringInterceptor implements HandlerInterceptor { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { StringBuffer url = request.getRequestURL(); - url.delete(0,7); // http:// + url.delete(0, 7); // http:// if (url.indexOf("//") != -1) { log.error("Double slash detected, blocking url {}", request.getRequestURL()); response.sendError(400, "Double slash in URL is prohibited"); diff --git a/auth/src/main/java/org/example/authserver/config/WebConfig.java b/auth/src/main/java/org/example/authserver/config/WebConfig.java index 102e2b1..5c06a3e 100644 --- a/auth/src/main/java/org/example/authserver/config/WebConfig.java +++ b/auth/src/main/java/org/example/authserver/config/WebConfig.java @@ -6,8 +6,8 @@ @Configuration public class WebConfig implements WebMvcConfigurer { - @Override - public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(new UrlFilteringInterceptor()); - } -} \ No newline at end of file + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new UrlFilteringInterceptor()); + } +} diff --git a/auth/src/main/java/org/example/authserver/controller/AclController.java b/auth/src/main/java/org/example/authserver/controller/AclController.java index 2470fd2..e0d18fc 100644 --- a/auth/src/main/java/org/example/authserver/controller/AclController.java +++ b/auth/src/main/java/org/example/authserver/controller/AclController.java @@ -5,13 +5,13 @@ import authserver.common.AclOperationDto; import com.google.common.base.Stopwatch; import jakarta.validation.Valid; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; - -import jakarta.validation.constraints.Max; -import jakarta.validation.constraints.Min; import lombok.extern.slf4j.Slf4j; import org.example.authserver.config.AppProperties; import org.example.authserver.entity.AclsRequestDTO; @@ -20,6 +20,7 @@ import org.example.authserver.service.AclService; import org.example.authserver.service.CacheService; import org.example.authserver.service.SplitTestService; +import org.springframework.data.domain.Sort; import org.springframework.web.bind.annotation.*; @Slf4j @@ -47,8 +48,32 @@ public AclController( } @GetMapping("/list") - public PageView listAcl(@RequestParam String namespace, @RequestParam String object, @RequestParam List relations, @Min(value = 1, message = "min: 1") @RequestParam(value = "page", defaultValue = "1") Integer page, @Max(value = 100, message = "max: 100") @RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) { - return repository.findAll(namespace, object, relations, page, pageSize); + public PageView listAcl( + @RequestParam(required = false) String namespace, + @RequestParam(required = false) String object, + @RequestParam(required = false) List relations, + @RequestParam(required = false) String user, + @RequestParam(name = "userset_namespace", required = false) String usersetNamespace, + @RequestParam(name = "userset_object", required = false) String usersetObject, + @RequestParam(name = "userset_relation", required = false) List usersetRelations, + @RequestParam(value = "sort", required = false) String sort, + @RequestParam(value = "direction", required = false) Sort.Direction direction, + @Min(value = 1, message = "min: 1") @RequestParam(value = "page", defaultValue = "1") + Integer page, + @Max(value = 100, message = "max: 100") @RequestParam(value = "pageSize", defaultValue = "50") + Integer pageSize) { + return repository.findAll( + namespace, + object, + relations, + user, + usersetNamespace, + usersetObject, + usersetRelations, + sort, + direction, + page, + pageSize); } @PostMapping("/create") @@ -72,6 +97,29 @@ public void createMultiAcl(@Valid @RequestBody AclsRequestDTO multiAcl) { private void createAcl_(Acl acl) { Stopwatch stopwatch = Stopwatch.createStarted(); log.info("Creating ACL: {}", acl); + List relations = acl.getRelation() != null ? List.of(acl.getRelation()) : null; + List usersetRelations = + acl.getUsersetRelation() != null ? List.of(acl.getUsersetRelation()) : null; + PageView existingAcls = + repository.findAll( + acl.getNamespace(), + acl.getObject(), + relations, + acl.getUser(), + acl.getUsersetNamespace(), + acl.getUsersetObject(), + usersetRelations, + null, + Sort.Direction.ASC, + 1, + 1); + if (existingAcls.getTotal() > 0) { + log.info( + "Skipping acl {}, existing aclId {}", + acl, + new ArrayList<>(existingAcls.getData()).get(0).getId()); + return; + } repository.save(acl); subscriptionRepository.publish(acl); if (appProperties.isCopyModeEnabled()) { diff --git a/auth/src/main/java/org/example/authserver/controller/MappingController.java b/auth/src/main/java/org/example/authserver/controller/MappingController.java index e3ba337..e15ee58 100644 --- a/auth/src/main/java/org/example/authserver/controller/MappingController.java +++ b/auth/src/main/java/org/example/authserver/controller/MappingController.java @@ -54,8 +54,7 @@ public void clearMappings(@RequestHeader(value = API_KEY) String apiKey) { } @DeleteMapping("/delete/{id}") - public void deleteAcl( - @PathVariable String id, @RequestHeader(value = API_KEY) String apiKey) { + public void deleteAcl(@PathVariable String id, @RequestHeader(value = API_KEY) String apiKey) { log.info("Delete Mapping by id: {}", id); repository.deleteById(id); } diff --git a/auth/src/main/java/org/example/authserver/entity/PageView.java b/auth/src/main/java/org/example/authserver/entity/PageView.java index a2fade2..cc2a5a1 100644 --- a/auth/src/main/java/org/example/authserver/entity/PageView.java +++ b/auth/src/main/java/org/example/authserver/entity/PageView.java @@ -1,11 +1,10 @@ package org.example.authserver.entity; +import java.util.Collection; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.SuperBuilder; -import java.util.Collection; - @SuperBuilder @Data @NoArgsConstructor @@ -18,4 +17,4 @@ public class PageView { public static long calculateTotalPages(long totalRecords, long pageSize) { return (totalRecords + pageSize - 1) / pageSize; } -} \ No newline at end of file +} diff --git a/auth/src/main/java/org/example/authserver/exception/NotSortableFieldException.java b/auth/src/main/java/org/example/authserver/exception/NotSortableFieldException.java new file mode 100644 index 0000000..5c9df9a --- /dev/null +++ b/auth/src/main/java/org/example/authserver/exception/NotSortableFieldException.java @@ -0,0 +1,3 @@ +package org.example.authserver.exception; + +public class NotSortableFieldException extends RuntimeException {} diff --git a/auth/src/main/java/org/example/authserver/repo/AclRepository.java b/auth/src/main/java/org/example/authserver/repo/AclRepository.java index 3c70ab7..1dcff17 100644 --- a/auth/src/main/java/org/example/authserver/repo/AclRepository.java +++ b/auth/src/main/java/org/example/authserver/repo/AclRepository.java @@ -1,30 +1,35 @@ package org.example.authserver.repo; +import jakarta.annotation.Nullable; +import jakarta.validation.constraints.NotNull; import java.util.List; import java.util.Set; import org.example.authserver.entity.AclEntity; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; -import javax.annotation.Nullable; - @Repository public interface AclRepository extends CrudRepository { + Page findAll(@NotNull Specification spec, @Nullable Pageable pageable); + Page findByNamespaceLikeAndObjectLikeAndRelationIn( - String namespace, String object, List relations, @Nullable Pageable pageable); + String namespace, String object, List relations, @Nullable Pageable pageable); Page findByNamespaceLike(String namespace, PageRequest pageRequest); - Page findByNamespaceLikeAndObjectLike(String namespace, String object, PageRequest pageRequest); + Page findByNamespaceLikeAndObjectLike( + String namespace, String object, PageRequest pageRequest); Page findByNsobjectLike(String nsobject, PageRequest pageRequest); - long countByNamespaceLikeAndObjectLikeAndRelationIn(String namespace, String object, List relations); + long countByNamespaceLikeAndObjectLikeAndRelationIn( + String namespace, String object, List relations); List findAll(); @@ -58,5 +63,4 @@ Set findAllByUsersetNamespaceAndUsersetObjectAndUsersetRelationAndUse String usNamespace, String usObject, String usRel); - } diff --git a/auth/src/main/java/org/example/authserver/repo/filter/AclFilter.java b/auth/src/main/java/org/example/authserver/repo/filter/AclFilter.java new file mode 100644 index 0000000..c875f4e --- /dev/null +++ b/auth/src/main/java/org/example/authserver/repo/filter/AclFilter.java @@ -0,0 +1,217 @@ +package org.example.authserver.repo.filter; + +import java.time.Instant; +import java.util.Arrays; +import java.util.Date; +import java.util.Map; +import org.example.authserver.entity.AclEntity; + +public class AclFilter { + + public static String getSortableField(String field) { + switch (field) { + case "id": + return "id"; + case "namespace": + return "namespace"; + case "object": + return "object"; + case "relation": + return "relation"; + case "user": + return "user"; + case "created": + return "created"; + case "updated": + return "updated"; + default: + } + + return null; + } + + public static void applyFilterForUsers( + Map filterParams, Filter filter) { + for (Map.Entry entry : filterParams.entrySet()) { + switch (entry.getKey().toLowerCase()) { + case "id": + filter.addCondition( + Condition.builder() + .comparison(FilterComparison.EQ) + .field("id") + .value(entry.getValue()) + .type(FilterFieldTypes.STRING) + .build()); + break; + + case "namespace": + filter.addCondition( + Condition.builder() + .comparison(FilterComparison.EQ) + .field("namespace") + .value(entry.getValue()) + .type(FilterFieldTypes.STRING) + .build()); + break; + + case "object": + filter.addCondition( + Condition.builder() + .comparison(FilterComparison.EQ) + .field("object") + .value(entry.getValue()) + .type(FilterFieldTypes.STRING) + .build()); + break; + + case "nsobject": + filter.addCondition( + Condition.builder() + .comparison(FilterComparison.EQ) + .field("nsobject") + .value(entry.getValue()) + .type(FilterFieldTypes.STRING) + .build()); + break; + + case "relation": + if (entry.getValue().contains(",")) { + final String[] relations = + Arrays.stream(entry.getValue().split(",")).toArray(String[]::new); + + filter.addCondition( + Condition.builder() + .comparison(FilterComparison.EQ) + .field("relation") + .value(relations) + .type(FilterFieldTypes.STRING) + .build()); + } else { + filter.addCondition( + Condition.builder() + .comparison(FilterComparison.EQ) + .field("relation") + .value(entry.getValue()) + .type(FilterFieldTypes.STRING) + .build()); + } + break; + + case "user": + filter.addCondition( + Condition.builder() + .comparison(FilterComparison.EQ) + .field("user") + .value(entry.getValue()) + .type(FilterFieldTypes.STRING) + .build()); + break; + + case "userset_namespace": + filter.addCondition( + Condition.builder() + .comparison(FilterComparison.EQ) + .field("usersetNamespace") + .value(entry.getValue()) + .type(FilterFieldTypes.STRING) + .build()); + break; + + case "userset_object": + filter.addCondition( + Condition.builder() + .comparison(FilterComparison.EQ) + .field("usersetObject") + .value(entry.getValue()) + .type(FilterFieldTypes.STRING) + .build()); + break; + + case "userset_relation": + if (entry.getValue().contains(",")) { + final String[] usersetRelations = + Arrays.stream(entry.getValue().split(",")).toArray(String[]::new); + + filter.addCondition( + Condition.builder() + .comparison(FilterComparison.EQ) + .field("usersetRelation") + .value(usersetRelations) + .type(FilterFieldTypes.STRING) + .build()); + } else { + filter.addCondition( + Condition.builder() + .comparison(FilterComparison.EQ) + .field("usersetRelation") + .value(entry.getValue()) + .type(FilterFieldTypes.STRING) + .build()); + } + break; + + case "created_eq": + filter.addCondition( + Condition.builder() + .comparison(FilterComparison.EQ) + .field("created") + .value(Date.from(Instant.parse(entry.getValue()))) + .type(FilterFieldTypes.DATE) + .build()); + break; + + case "created_gte": + filter.addCondition( + Condition.builder() + .comparison(FilterComparison.GTE) + .field("created") + .value(Date.from(Instant.parse(entry.getValue()))) + .type(FilterFieldTypes.DATE) + .build()); + break; + + case "created_lte": + filter.addCondition( + Condition.builder() + .comparison(FilterComparison.LTE) + .field("created") + .value(Date.from(Instant.parse(entry.getValue()))) + .type(FilterFieldTypes.DATE) + .build()); + break; + + case "updated_eq": + filter.addCondition( + Condition.builder() + .comparison(FilterComparison.EQ) + .field("updated") + .value(Date.from(Instant.parse(entry.getValue()))) + .type(FilterFieldTypes.DATE) + .build()); + break; + + case "updated_gte": + filter.addCondition( + Condition.builder() + .comparison(FilterComparison.GTE) + .field("updated") + .value(Date.from(Instant.parse(entry.getValue()))) + .type(FilterFieldTypes.DATE) + .build()); + break; + + case "updated_lte": + filter.addCondition( + Condition.builder() + .comparison(FilterComparison.LTE) + .field("invitupdatedeAcceptedDate") + .value(Date.from(Instant.parse(entry.getValue()))) + .type(FilterFieldTypes.DATE) + .build()); + break; + + default: + } + } + } +} diff --git a/auth/src/main/java/org/example/authserver/repo/filter/Condition.java b/auth/src/main/java/org/example/authserver/repo/filter/Condition.java new file mode 100644 index 0000000..bbafed8 --- /dev/null +++ b/auth/src/main/java/org/example/authserver/repo/filter/Condition.java @@ -0,0 +1,37 @@ +package org.example.authserver.repo.filter; + +import static jakarta.persistence.criteria.Predicate.BooleanOperator.AND; + +import jakarta.persistence.criteria.Predicate.BooleanOperator; +import java.math.BigDecimal; +import java.util.Date; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Condition { + + private FilterFieldTypes type; + private FilterComparison comparison; + private String field; + private T value; + @Builder.Default private BooleanOperator operator = AND; + + public Number getValueAsNumber() { + if (value instanceof Date) { + return ((Date) value).getTime(); + } else if (value instanceof Integer) { + return (Integer) value; + } else if (value instanceof Long) { + return (Long) value; + } else if (value instanceof BigDecimal) { + return (BigDecimal) value; + } + return 0; + } +} diff --git a/auth/src/main/java/org/example/authserver/repo/filter/Filter.java b/auth/src/main/java/org/example/authserver/repo/filter/Filter.java new file mode 100644 index 0000000..b2ce5be --- /dev/null +++ b/auth/src/main/java/org/example/authserver/repo/filter/Filter.java @@ -0,0 +1,99 @@ +package org.example.authserver.repo.filter; + +import jakarta.persistence.criteria.*; +import jakarta.persistence.criteria.Predicate.BooleanOperator; +import java.util.*; +import org.springframework.data.jpa.domain.Specification; + +public class Filter implements Specification { + + private final List conditions = new ArrayList<>(); + + public void addCondition(Condition condition) { + this.conditions.add(condition); + } + + private List buildPredicates(Root root, CriteriaQuery query, CriteriaBuilder cb) { + List predicates = new ArrayList<>(); + for (Condition condition : conditions) { + predicates.add(buildPredicate(condition, root, query, cb)); + } + return predicates; + } + + public Predicate buildPredicate( + Condition condition, Root root, CriteriaQuery query, CriteriaBuilder cb) { + if (!condition.getType().getValue().contains(condition.getComparison())) { + throw new RuntimeException("UNSUPPORTED_COMPARISON_FOR_SPECIFIED_TYPE"); + } + switch (condition.getComparison()) { + case EQ: + if (condition.getValue().getClass().isArray()) { + final Predicate[] predicatesList = + Arrays.stream((Object[]) condition.getValue()) + .map(conditionValue -> cb.equal(root.get(condition.getField()), conditionValue)) + .toArray(Predicate[]::new); + return cb.or(predicatesList); + } + return cb.equal(root.get(condition.getField()), condition.getValue()); + case GT: + if (condition.getType().equals(FilterFieldTypes.DATE)) { + return cb.greaterThan( + root.get(condition.getField()), + new Date(condition.getValueAsNumber().longValue())); + } else { + return cb.gt(root.get(condition.getField()), condition.getValueAsNumber()); + } + case GTE: + if (condition.getType().equals(FilterFieldTypes.DATE)) { + return cb.greaterThanOrEqualTo( + root.get(condition.getField()), + new Date(condition.getValueAsNumber().longValue())); + } else { + return cb.ge(root.get(condition.getField()), condition.getValueAsNumber()); + } + case LT: + if (condition.getType().equals(FilterFieldTypes.DATE)) { + return cb.lessThan( + root.get(condition.getField()), + new Date(condition.getValueAsNumber().longValue())); + } else { + return cb.lt(root.get(condition.getField()), condition.getValueAsNumber()); + } + case LTE: + if (condition.getType().equals(FilterFieldTypes.DATE)) { + return cb.lessThanOrEqualTo( + root.get(condition.getField()), + new Date(condition.getValueAsNumber().longValue())); + } else { + return cb.le(root.get(condition.getField()), condition.getValueAsNumber()); + } + case EXISTS: + if (condition.getValue() == null || Boolean.parseBoolean(condition.getValue().toString())) { + return cb.isNotNull(root.get(condition.getField())); + } else { + return cb.isNull(root.get(condition.getField())); + } + default: + throw new RuntimeException("UNSUPPORTED_COMPARISON"); + } + } + + public int getConditionsCount() { + return conditions.size(); + } + + @Override + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + List predicates = buildPredicates(root, query, cb); + if (predicates.size() == 1) { + return predicates.get(0); + } + boolean allOrs = + conditions.stream() + .allMatch(condition -> condition.getOperator().equals(BooleanOperator.OR)); + return allOrs + ? cb.or(predicates.toArray(Predicate[]::new)) + : cb.and(predicates.toArray(Predicate[]::new)); + } +} diff --git a/auth/src/main/java/org/example/authserver/repo/filter/FilterComparison.java b/auth/src/main/java/org/example/authserver/repo/filter/FilterComparison.java new file mode 100644 index 0000000..165b24d --- /dev/null +++ b/auth/src/main/java/org/example/authserver/repo/filter/FilterComparison.java @@ -0,0 +1,10 @@ +package org.example.authserver.repo.filter; + +public enum FilterComparison { + GT, + LT, + GTE, + LTE, + EQ, + EXISTS +} diff --git a/auth/src/main/java/org/example/authserver/repo/filter/FilterFieldTypes.java b/auth/src/main/java/org/example/authserver/repo/filter/FilterFieldTypes.java new file mode 100644 index 0000000..5c59b54 --- /dev/null +++ b/auth/src/main/java/org/example/authserver/repo/filter/FilterFieldTypes.java @@ -0,0 +1,23 @@ +package org.example.authserver.repo.filter; + +import static org.example.authserver.repo.filter.FilterComparison.*; + +import java.util.List; + +public enum FilterFieldTypes { + STRING(List.of(EQ, EXISTS)), + ENUM(List.of(EQ)), + DATE(List.of(EQ, LT, GT, LTE, GTE)), + NUMBER(List.of(EQ, LT, GT, LTE, GTE)), + BOOLEAN(List.of(EQ)); + + FilterFieldTypes(List value) { + this.value = value; + } + + private final List value; + + public List getValue() { + return value; + } +} diff --git a/auth/src/main/java/org/example/authserver/service/AclService.java b/auth/src/main/java/org/example/authserver/service/AclService.java index 7317aec..4b5fd52 100644 --- a/auth/src/main/java/org/example/authserver/service/AclService.java +++ b/auth/src/main/java/org/example/authserver/service/AclService.java @@ -1,21 +1,19 @@ package org.example.authserver.service; import authserver.acl.Acl; -import java.util.List; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; - -import jakarta.validation.constraints.Max; -import jakarta.validation.constraints.Min; import lombok.extern.slf4j.Slf4j; import org.example.authserver.entity.AclEntity; import org.example.authserver.entity.PageView; +import org.example.authserver.exception.NotSortableFieldException; import org.example.authserver.repo.AclRepository; +import org.example.authserver.repo.filter.AclFilter; +import org.example.authserver.repo.filter.Filter; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -29,22 +27,79 @@ public AclService(AclRepository repository) { this.repository = repository; } - public PageView findAll(String namespace, String object, List relations, Integer pageNum, Integer pageSize) { - PageRequest pageRequest = PageRequest.of(pageNum, pageSize); - //String nsobject = String.format("%s:%s", namespace, object); - //Page page = repository.findByNamespaceLikeAndObjectLikeAndRelationIn(namespace, object, relations, pageRequest); - Page page = repository.findByNsobjectLike(namespace, pageRequest); - Set acls = page.getContent() - .stream().map(AclEntity::toAcl).collect(Collectors.toSet()); + public Set findAll() { + return repository.findAll().stream().map(AclEntity::toAcl).collect(Collectors.toSet()); + } - long count = repository.countByNamespaceLikeAndObjectLikeAndRelationIn(namespace, object, relations); + public PageView findAll( + String namespace, + String object, + List relations, + String user, + String usersetNamespace, + String usersetObject, + List usersetRelations, + String sort, + Sort.Direction direction, + Integer page, + Integer pageSize) { + PageRequest pageRequest; + if (sort != null) { + String sortField = AclFilter.getSortableField(sort); + if (sortField == null) { + throw new NotSortableFieldException(); + } + pageRequest = + PageRequest.of( + page - 1, + pageSize, + Sort.by(direction != null ? direction : Sort.Direction.ASC, sortField)); + } else { + pageRequest = PageRequest.of(page - 1, pageSize); + } + + Map filterParams = new HashMap<>(); + Filter filter = new Filter<>(); + if (namespace != null && !namespace.isEmpty() && object != null && !object.isEmpty()) { + filterParams.put("nsobject", namespace + ":" + object); + } else { + if (namespace != null && !namespace.isEmpty()) { + filterParams.put("namespace", namespace); + } + if (object != null && !object.isEmpty()) { + filterParams.put("object", object); + } + } + if (relations != null && !relations.isEmpty()) { + filterParams.put("relation", String.join(",", relations)); + } + if (user != null && !user.isEmpty()) { + filterParams.put("user", String.join(",", user)); + } + + if (usersetNamespace != null && !usersetNamespace.isEmpty()) { + filterParams.put("userset_namespace", usersetNamespace); + } + + if (usersetObject != null && !usersetObject.isEmpty()) { + filterParams.put("userset_object", usersetObject); + } + + if (usersetRelations != null && !usersetRelations.isEmpty()) { + filterParams.put("userset_relation", String.join(",", usersetRelations)); + } + + AclFilter.applyFilterForUsers(filterParams, filter); + + Page data = repository.findAll(filter, pageRequest); + List acls = data.getContent().stream().map(AclEntity::toAcl).collect(Collectors.toList()); return PageView.builder() - .data(acls) - .currentPage(pageNum) - .pages(PageView.calculateTotalPages(count, pageSize)) - .total(count) - .build(); + .data(acls) + .currentPage(page) + .total(data.getTotalElements()) + .pages(data.getTotalPages()) + .build(); } public Acl findOneById(String id) { diff --git a/auth/src/main/java/org/example/authserver/service/AuthService.java b/auth/src/main/java/org/example/authserver/service/AuthService.java index 78a28d3..f9c8c34 100644 --- a/auth/src/main/java/org/example/authserver/service/AuthService.java +++ b/auth/src/main/java/org/example/authserver/service/AuthService.java @@ -129,8 +129,7 @@ public CheckResult check(CheckRequestDTO dto, String traceId) { CheckResult result; try { if (userId != null && tenantId != null) { - if (redisService.exists( - String.format(Constants.USER_SIGNOUT_REDIS_KEY, userId))) { + if (redisService.exists(String.format(Constants.USER_SIGNOUT_REDIS_KEY, userId))) { log.debug("User {} did signout, unauthorized", userId); result = CheckResult.builder().jwtPresent(false).result(false).events(new HashMap<>()).build(); diff --git a/auth/src/main/java/org/example/authserver/service/zanzibar/MappingService.java b/auth/src/main/java/org/example/authserver/service/zanzibar/MappingService.java index 1c74259..52ed070 100644 --- a/auth/src/main/java/org/example/authserver/service/zanzibar/MappingService.java +++ b/auth/src/main/java/org/example/authserver/service/zanzibar/MappingService.java @@ -27,9 +27,7 @@ public MappingService( this.mappingRepository = mappingRepository; } - /** - * @return mapping variables, or {@code null} for no match - */ + /** @return mapping variables, or {@code null} for no match */ public List processRequest( String requestMethod, String requestPath, diff --git a/auth/src/main/java/org/example/authserver/util/shutdown/ShutDownHandler.java b/auth/src/main/java/org/example/authserver/util/shutdown/ShutDownHandler.java index 2f85f40..ebd8ef0 100644 --- a/auth/src/main/java/org/example/authserver/util/shutdown/ShutDownHandler.java +++ b/auth/src/main/java/org/example/authserver/util/shutdown/ShutDownHandler.java @@ -1,13 +1,12 @@ package org.example.authserver.util.shutdown; import io.micrometer.core.instrument.util.StringUtils; +import java.io.BufferedReader; +import java.io.InputStreamReader; import lombok.extern.slf4j.Slf4j; import sun.misc.Signal; import sun.misc.SignalHandler; -import java.io.BufferedReader; -import java.io.InputStreamReader; - @Slf4j public class ShutDownHandler implements SignalHandler { @Override diff --git a/auth/src/main/resources/application-dev.properties b/auth/src/main/resources/application-dev.properties index ad49020..c959644 100644 --- a/auth/src/main/resources/application-dev.properties +++ b/auth/src/main/resources/application-dev.properties @@ -24,9 +24,9 @@ spring.jpa.hibernate.ddl-auto=update spring.mvc.pathmatch.matching-strategy=ANT_PATH_MATCHER -management.endpoint.metrics.enabled=true +management.endpoint.metrics.access=read_only management.endpoints.web.exposure.include=health,prometheus -management.endpoint.prometheus.enabled=true +management.endpoint.prometheus.access=read_only management.prometheus.metrics.export.enabled=true redis.hostname=127.0.0.1 diff --git a/auth/src/main/resources/application-docker.properties b/auth/src/main/resources/application-docker.properties index e656a96..e0e0df2 100644 --- a/auth/src/main/resources/application-docker.properties +++ b/auth/src/main/resources/application-docker.properties @@ -26,9 +26,9 @@ spring.jpa.hibernate.ddl-auto=none spring.jpa.properties.hibernate.default_schema=${DBSCHEMA:authz} spring.mvc.pathmatch.matching-strategy=ANT_PATH_MATCHER -management.endpoint.metrics.enabled=true +management.endpoint.metrics.access=read_only management.endpoints.web.exposure.include=health,prometheus -management.endpoint.prometheus.enabled=true +management.endpoint.prometheus.access=read_only management.prometheus.metrics.export.enabled=true server.port=8081 diff --git a/auth/src/main/resources/application-splittest.properties b/auth/src/main/resources/application-splittest.properties index 48aa501..d73f8d3 100644 --- a/auth/src/main/resources/application-splittest.properties +++ b/auth/src/main/resources/application-splittest.properties @@ -22,9 +22,9 @@ spring.jpa.hibernate.ddl-auto=update spring.mvc.pathmatch.matching-strategy=ANT_PATH_MATCHER -management.endpoint.metrics.enabled=true +management.endpoint.metrics.access=read_only management.endpoints.web.exposure.include=health,prometheus -management.endpoint.prometheus.enabled=true +management.endpoint.prometheus.access=read_only management.prometheus.metrics.export.enabled=true redis.hostname=127.0.0.1 diff --git a/auth/src/main/resources/application.properties b/auth/src/main/resources/application.properties index 7bf93cc..72cf5c8 100644 --- a/auth/src/main/resources/application.properties +++ b/auth/src/main/resources/application.properties @@ -27,9 +27,9 @@ spring.jpa.hibernate.ddl-auto=update spring.mvc.pathmatch.matching-strategy=ANT_PATH_MATCHER springdoc.swagger-ui.path=/swagger-ui.html -management.endpoint.metrics.enabled=true +management.endpoint.metrics.access=read_only management.endpoints.web.exposure.include=health,prometheus -management.endpoint.prometheus.enabled=true +management.endpoint.prometheus.access=read_only management.prometheus.metrics.export.enabled=true redis.hostname=127.0.0.1 diff --git a/auth/src/test/java/org/example/authserver/ZanzibarImplTest.java b/auth/src/test/java/org/example/authserver/ZanzibarImplTest.java index aae8f47..bb3d6f6 100644 --- a/auth/src/test/java/org/example/authserver/ZanzibarImplTest.java +++ b/auth/src/test/java/org/example/authserver/ZanzibarImplTest.java @@ -26,7 +26,6 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -@Disabled class ZanzibarImplTest { @Mock private AclService aclService; @@ -45,7 +44,7 @@ void setUp() { zanzibar = new ZanzibarImpl(aclService, aclRelationConfigService); } - @Test + @Disabled // temp broken void check() { Map usersToTest = Map.of("user1_viewer", false, "user2_editor", true, "user3_owner", true); @@ -63,7 +62,7 @@ void check() { .collect(Collectors.toSet()); AclRelationConfig config = prepConfig(); - Mockito.doReturn(acls).when(aclService).findAll(namespace, object, relations, page, pageSize); + Mockito.doReturn(acls).when(aclService).findAll(); Mockito.doReturn(aclsDocReadme) .when(aclService) .findAllByNamespaceAndObjectAndUser(eq("doc"), eq("readme"), eq(principal)); @@ -76,7 +75,7 @@ void check() { Mockito.doReturn(aclsGroupDocument) .when(aclService) .findAllByPrincipalAndNsObjectIn(eq(principal), eq(List.of("group:document"))); - Mockito.doReturn(Set.of(config)).when(configRepository).findAll(); + Mockito.doReturn(List.of(config)).when(configRepository).findAll(); Mockito.doReturn(Map.of(config.getNamespace(), config)).when(cacheService).getConfigs(); aclRelationConfigService.update(); @@ -106,14 +105,14 @@ void relations() { .collect(Collectors.toSet()); AclRelationConfig config = prepConfig(); - Mockito.doReturn(acls).when(aclService).findAll(namespace, object, relations, page, pageSize); + Mockito.doReturn(acls).when(aclService).findAll(); Mockito.doReturn(aclsDocReadme) .when(aclService) .findAllByNamespaceAndObjectAndUser(eq("doc"), eq("readme"), eq(principal)); Mockito.doReturn(aclsGroupDocument) .when(aclService) .findAllByNamespaceAndObjectAndUser(eq("group"), eq("document"), eq(principal)); - Mockito.doReturn(Set.of(config)).when(configRepository).findAll(); + Mockito.doReturn(List.of(config)).when(configRepository).findAll(); Set result = zanzibar.getRelations("doc", "readme", principal, new LocalCache()); System.out.printf("user %s => %s %n", principal, result); @@ -142,7 +141,7 @@ void relationsSimple() { AclRelationConfig config = new AclRelationConfig(); - Mockito.doReturn(acls).when(aclService).findAll(namespace, object, relations, page, pageSize); + Mockito.doReturn(acls).when(aclService).findAll(); Mockito.doReturn(acls) .when(aclService) .findAllByNamespaceAndObjectAndUser(eq("namespace"), eq("object"), eq(principal)); @@ -150,7 +149,7 @@ void relationsSimple() { .when(aclService) .findAllByPrincipalAndNsObjectIn(eq(principal), eq(List.of("namespace:object"))); Mockito.doReturn(acls).when(aclService).findAllByNsObjectIn(eq(List.of("namespace:object"))); - Mockito.doReturn(Set.of(config)).when(configRepository).findAll(); + Mockito.doReturn(List.of(config)).when(configRepository).findAll(); Mockito.doReturn(Map.of("namespace:object", config)).when(cacheService).getConfigs(); Set result = @@ -183,9 +182,9 @@ void relationsContacts() { .filter(acl -> acl.getNamespace().equals("group") && acl.getObject().equals("user2")) .collect(Collectors.toSet()); - Set configs = prepConfig2(); + List configs = prepConfig2(); - Mockito.doReturn(acls).when(aclService).findAll(namespace, object, relations, page, pageSize); + Mockito.doReturn(acls).when(aclService).findAll(); Mockito.doReturn(acls) .when(aclService) .findAllByNamespaceAndObjectAndUser(eq("contact"), eq("uuid1"), eq(principal)); @@ -208,6 +207,7 @@ void relationsContacts() { } } + @Disabled // temp broken @Test void checkContactUsersTest() { Set acls = new HashSet<>(); @@ -240,7 +240,7 @@ void checkContactUsersTest() { .filter(acl -> acl.getNamespace().equals("api") && acl.getObject().equals("contact")) .collect(Collectors.toSet()); - Mockito.doReturn(acls).when(aclService).findAll(namespace, object, relations, page, pageSize); + Mockito.doReturn(acls).when(aclService).findAll(); Mockito.doReturn(aclsGroupContactusers) .when(aclService) .findAllByNamespaceAndObjectAndUser(eq("group"), eq("contactusers"), eq(principal)); @@ -254,15 +254,17 @@ void checkContactUsersTest() { .when(aclService) .findAllByPrincipalAndNsObjectIn(eq(principal), eq(List.of("api:contact"))); - Mockito.doReturn(Set.of(new AclRelationConfig())).when(configRepository).findAll(); + Mockito.doReturn(List.of(new AclRelationConfig())).when(configRepository).findAll(); + CheckResult checkResult = + zanzibar.check("api", "contact", "enable", principal, new LocalCache()); + boolean result = checkResult.isResult(); // user1 and user2 are have access. And user3 is not. - assertEquals( - expected, - zanzibar.check("api", "contact", "enable", principal, new LocalCache()).isResult()); + assertEquals(expected, result); } } + @Disabled // temp broken @Test void wildcardRelationsTest() { Set acls = new HashSet<>(); @@ -290,7 +292,7 @@ void wildcardRelationsTest() { Map configMap = Map.of(relationConfigContact.getNamespace(), relationConfigContact); - Mockito.doReturn(Set.of(relationConfigContact)).when(configRepository).findAll(); + Mockito.doReturn(List.of(relationConfigContact)).when(configRepository).findAll(); Mockito.doReturn(configMap).when(cacheService).getConfigs(); aclRelationConfigService.update(); @@ -319,7 +321,7 @@ void wildcardRelationsTest() { acl -> acl.getNamespace().equals("contact") && acl.getObject().equals(uuid)) .collect(Collectors.toSet()); - Mockito.doReturn(acls).when(aclService).findAll(namespace, object, relations, page, pageSize); + Mockito.doReturn(acls).when(aclService).findAll(); Mockito.doReturn(aclsGroupContactusers) .when(aclService) .findAllByNamespaceAndObjectAndUser(eq("group"), eq("contactusers"), eq(principal)); @@ -369,7 +371,7 @@ void exclusionTest() { Map configMap = Map.of(relationConfigContact.getNamespace(), relationConfigContact); - Mockito.doReturn(Set.of(relationConfigContact)).when(configRepository).findAll(); + Mockito.doReturn(List.of(relationConfigContact)).when(configRepository).findAll(); Mockito.doReturn(configMap).when(cacheService).getConfigs(); aclRelationConfigService.update(); @@ -379,7 +381,7 @@ void exclusionTest() { String principal = entry.getKey(); boolean expected = entry.getValue(); - Mockito.doReturn(acls).when(aclService).findAll(namespace, object, relations, page, pageSize); + Mockito.doReturn(acls).when(aclService).findAll(); Mockito.doReturn(acls) .when(aclService) .findAllByNamespaceAndObjectAndUser(eq("group"), eq("object"), anyString()); @@ -423,7 +425,7 @@ void intersectionTest() { Map configMap = Map.of(relationConfigContact.getNamespace(), relationConfigContact); - Mockito.doReturn(Set.of(relationConfigContact)).when(configRepository).findAll(); + Mockito.doReturn(List.of(relationConfigContact)).when(configRepository).findAll(); Mockito.doReturn(configMap).when(cacheService).getConfigs(); aclRelationConfigService.update(); @@ -433,7 +435,7 @@ void intersectionTest() { String principal = entry.getKey(); boolean expected = entry.getValue(); - Mockito.doReturn(acls).when(aclService).findAll(namespace, object, relations, page, pageSize); + Mockito.doReturn(acls).when(aclService).findAll(); Mockito.doReturn(acls) .when(aclService) .findAllByNamespaceAndObjectAndUser(eq("group"), eq("object"), anyString()); @@ -524,7 +526,7 @@ private Set prepAcls() { return acls; } - private Set prepConfig2() { + private List prepConfig2() { AclRelationConfig relationConfig = new AclRelationConfig(); relationConfig.setNamespace("group:user1"); relationConfig.setRelations( @@ -557,6 +559,6 @@ private Set prepConfig2() { .parents(Set.of(AclRelationParent.builder().relation("editor").build())) .build())); - return Set.of(relationConfig, relationConfig2); + return List.of(relationConfig, relationConfig2); } }