diff --git a/application/src/main/java/run/halo/app/core/extension/endpoint/UserEndpoint.java b/application/src/main/java/run/halo/app/core/extension/endpoint/UserEndpoint.java index b7243eb7ad..58463a269b 100644 --- a/application/src/main/java/run/halo/app/core/extension/endpoint/UserEndpoint.java +++ b/application/src/main/java/run/halo/app/core/extension/endpoint/UserEndpoint.java @@ -33,8 +33,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.function.Predicate; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.springdoc.core.fn.builders.requestbody.Builder; @@ -556,37 +554,39 @@ public Sort getSort() { return SortResolver.defaultInstance.resolve(exchange); } + /** + * Converts query parameters to user predicate. + * + * @return user predicate to filter users + */ public Predicate toPredicate() { - Predicate displayNamePredicate = user -> { + Predicate keywordPredicate = user -> { var keyword = getKeyword(); - if (!org.springframework.util.StringUtils.hasText(keyword)) { + if (StringUtils.isBlank(keyword)) { return true; } + var username = user.getMetadata().getName(); var displayName = user.getSpec().getDisplayName(); - if (!org.springframework.util.StringUtils.hasText(displayName)) { - return false; - } - return displayName.toLowerCase().contains(keyword.trim().toLowerCase()); + return StringUtils.containsIgnoreCase(displayName, keyword) + || keyword.equalsIgnoreCase(username); }; + Predicate rolePredicate = user -> { - var role = getRole(); - if (role == null) { + var roleName = getRole(); + if (StringUtils.isBlank(roleName)) { return true; } - var annotations = user.getMetadata().getAnnotations(); - if (annotations == null || !annotations.containsKey(User.ROLE_NAMES_ANNO)) { + var roleNamesAnno = MetadataUtil.nullSafeAnnotations(user) + .get(User.ROLE_NAMES_ANNO); + if (StringUtils.isBlank(roleNamesAnno)) { return false; - } else { - Pattern pattern = Pattern.compile("\\[\"([^\"]*)\"\\]"); - Matcher matcher = pattern.matcher(annotations.get(User.ROLE_NAMES_ANNO)); - if (matcher.find()) { - return matcher.group(1).equals(role); - } else { - return false; - } } + Set roleNames = JsonUtils.jsonToObject(roleNamesAnno, + new TypeReference<>() { + }); + return roleNames.contains(roleName); }; - return displayNamePredicate + return keywordPredicate .and(rolePredicate) .and(labelAndFieldSelectorToPredicate(getLabelSelector(), getFieldSelector())); } diff --git a/application/src/test/java/run/halo/app/core/extension/endpoint/UserEndpointIntegrationTest.java b/application/src/test/java/run/halo/app/core/extension/endpoint/UserEndpointIntegrationTest.java new file mode 100644 index 0000000000..2535800900 --- /dev/null +++ b/application/src/test/java/run/halo/app/core/extension/endpoint/UserEndpointIntegrationTest.java @@ -0,0 +1,117 @@ +package run.halo.app.core.extension.endpoint; + +import static org.mockito.ArgumentMatchers.anySet; +import static org.mockito.Mockito.when; +import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf; + +import java.time.Instant; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.web.reactive.server.WebTestClient; +import reactor.core.publisher.Flux; +import run.halo.app.core.extension.Role; +import run.halo.app.core.extension.User; +import run.halo.app.core.extension.service.RoleService; +import run.halo.app.extension.Metadata; +import run.halo.app.extension.ReactiveExtensionClient; + +@SpringBootTest +@AutoConfigureWebTestClient +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD) +@WithMockUser(username = "fake-user", password = "fake-password", roles = "fake-super-role") +public class UserEndpointIntegrationTest { + @Autowired + WebTestClient webClient; + + @Autowired + ReactiveExtensionClient client; + + @MockBean + RoleService roleService; + + @BeforeEach + void setUp() { + var rule = new Role.PolicyRule.Builder() + .apiGroups("*") + .resources("*") + .verbs("*") + .build(); + var role = new Role(); + role.setMetadata(new Metadata()); + role.getMetadata().setName("super-role"); + role.setRules(List.of(rule)); + when(roleService.listDependenciesFlux(anySet())).thenReturn(Flux.just(role)); + webClient = webClient.mutateWith(csrf()); + } + + @Nested + class UserListTest { + @Test + void shouldFilterUsersWhenDisplayNameKeywordProvided() { + var expectUser = + createUser("fake-user-2", "expected display name"); + var unexpectedUser1 = + createUser("fake-user-1", "first fake display name"); + var unexpectedUser2 = + createUser("fake-user-3", "second fake display name"); + + client.create(expectUser).block(); + client.create(unexpectedUser1).block(); + client.create(unexpectedUser2).block(); + + when(roleService.list(anySet())).thenReturn(Flux.empty()); + + webClient.get().uri("/apis/api.console.halo.run/v1alpha1/users?keyword=Expected") + .exchange() + .expectStatus().isOk() + .expectBody() + .jsonPath("$.items.length()").isEqualTo(1) + .jsonPath("$.items[0].user.metadata.name").isEqualTo("fake-user-2"); + + } + + @Test + void shouldFilterUsersWhenUserNameKeywordProvided() { + var expectUser = + createUser("fake-user", "expected display name"); + var unexpectedUser1 = + createUser("fake-user-1", "first fake display name"); + var unexpectedUser2 = + createUser("fake-user-3", "second fake display name"); + + client.create(expectUser).block(); + client.create(unexpectedUser1).block(); + client.create(unexpectedUser2).block(); + + when(roleService.list(anySet())).thenReturn(Flux.empty()); + + webClient.get().uri("/apis/api.console.halo.run/v1alpha1/users?keyword=fake-user") + .exchange() + .expectStatus().isOk() + .expectBody() + .jsonPath("$.items.length()").isEqualTo(1) + .jsonPath("$.items[0].user.metadata.name").isEqualTo("fake-user"); + } + } + + User createUser(String name, String displayName) { + var metadata = new Metadata(); + metadata.setName(name); + metadata.setCreationTimestamp(Instant.now()); + var spec = new User.UserSpec(); + spec.setEmail("fake-email"); + spec.setDisplayName(displayName); + var user = new User(); + user.setMetadata(metadata); + user.setSpec(spec); + return user; + } +} diff --git a/application/src/test/java/run/halo/app/core/extension/endpoint/UserEndpointTest.java b/application/src/test/java/run/halo/app/core/extension/endpoint/UserEndpointTest.java index 58a400c369..2d79aa9567 100644 --- a/application/src/test/java/run/halo/app/core/extension/endpoint/UserEndpointTest.java +++ b/application/src/test/java/run/halo/app/core/extension/endpoint/UserEndpointTest.java @@ -125,35 +125,6 @@ void shouldListUsersWhenUserPresent() { .jsonPath("$.total").isEqualTo(3); } - @Test - void shouldFilterUsersWhenKeywordProvided() { - var expectUser = - createUser("fake-user-2", "expected display name"); - var unexpectedUser1 = - createUser("fake-user-1", "first fake display name"); - var unexpectedUser2 = - createUser("fake-user-3", "second fake display name"); - var users = List.of( - expectUser - ); - var expectResult = new ListResult<>(users); - when(client.list(same(User.class), any(), any(), anyInt(), anyInt())) - .thenReturn(Mono.just(expectResult)); - when(roleService.list(anySet())).thenReturn(Flux.empty()); - - bindToRouterFunction(endpoint.endpoint()) - .build() - .get().uri("/users?keyword=Expected") - .exchange() - .expectStatus().isOk(); - - verify(client).list(same(User.class), argThat( - predicate -> predicate.test(expectUser) - && !predicate.test(unexpectedUser1) - && !predicate.test(unexpectedUser2)), - any(), anyInt(), anyInt()); - } - @Test void shouldFilterUsersWhenRoleProvided() { var expectUser =