diff --git a/scm-it/src/test/java/sonia/scm/it/AutoCompleteITCase.java b/scm-it/src/test/java/sonia/scm/it/AutoCompleteITCase.java index a0579eee6f..f15acdb6f4 100644 --- a/scm-it/src/test/java/sonia/scm/it/AutoCompleteITCase.java +++ b/scm-it/src/test/java/sonia/scm/it/AutoCompleteITCase.java @@ -15,7 +15,7 @@ import static org.assertj.core.api.Assertions.assertThat; public class AutoCompleteITCase { - public static final String CREATED_USER_PREFIX = "user"; + public static final String CREATED_USER_PREFIX = "user_"; public static final String CREATED_GROUP_PREFIX = "group_"; @Before @@ -28,9 +28,10 @@ public class AutoCompleteITCase { createUsers(); ScmRequests.start() .given() - .url(TestData.getUsersAutoCompleteUrl("user*")) - .usernameAndPassword(TestData.USER_SCM_ADMIN, TestData.USER_SCM_ADMIN) - .getAutoCompleteResource() + .requestIndexResource(TestData.USER_SCM_ADMIN,TestData.USER_SCM_ADMIN) + .assertStatusCode(200) + .usingIndexResponse() + .requestAutoCompleteUsers("user*") .assertStatusCode(200) .usingAutoCompleteResponse() .assertAutoCompleteResults(assertAutoCompleteResult(CREATED_USER_PREFIX)); @@ -44,9 +45,10 @@ public class AutoCompleteITCase { createUsers(); ScmRequests.start() .given() - .url(TestData.getUsersAutoCompleteUrl("user*")) - .usernameAndPassword(username, password) - .getAutoCompleteResource() + .requestIndexResource(username,password) + .assertStatusCode(200) + .usingIndexResponse() + .requestAutoCompleteUsers("user*") .assertStatusCode(200) .usingAutoCompleteResponse() .assertAutoCompleteResults(assertAutoCompleteResult(CREATED_USER_PREFIX)); @@ -57,9 +59,10 @@ public class AutoCompleteITCase { createGroups(); ScmRequests.start() .given() - .url(TestData.getGroupsAutoCompleteUrl("group*")) - .usernameAndPassword(TestData.USER_SCM_ADMIN, TestData.USER_SCM_ADMIN) - .getAutoCompleteResource() + .requestIndexResource(TestData.USER_SCM_ADMIN,TestData.USER_SCM_ADMIN) + .assertStatusCode(200) + .usingIndexResponse() + .applyAutoCompleteGroups("group*") .assertStatusCode(200) .usingAutoCompleteResponse() .assertAutoCompleteResults(assertAutoCompleteResult(CREATED_GROUP_PREFIX)); @@ -73,9 +76,10 @@ public class AutoCompleteITCase { createGroups(); ScmRequests.start() .given() - .url(TestData.getGroupsAutoCompleteUrl("group*")) - .usernameAndPassword(username, password) - .getAutoCompleteResource() + .requestIndexResource(username,password) + .assertStatusCode(200) + .usingIndexResponse() + .applyAutoCompleteGroups("group*") .assertStatusCode(200) .usingAutoCompleteResponse() .assertAutoCompleteResults(assertAutoCompleteResult(CREATED_GROUP_PREFIX)); diff --git a/scm-it/src/test/java/sonia/scm/it/utils/ScmRequests.java b/scm-it/src/test/java/sonia/scm/it/utils/ScmRequests.java index 8b4eb34188..7ea52ba58d 100644 --- a/scm-it/src/test/java/sonia/scm/it/utils/ScmRequests.java +++ b/scm-it/src/test/java/sonia/scm/it/utils/ScmRequests.java @@ -46,24 +46,46 @@ public class ScmRequests { * @return the response of the GET request using the given link */ private Response applyGETRequestFromLink(Response response, String linkPropertyName) { - return applyGETRequest(response - .then() - .extract() - .path(linkPropertyName)); + return applyGETRequestFromLinkWithParams(response, linkPropertyName, ""); } + /** + * Apply a GET Request to the extracted url from the given link + * + * @param linkPropertyName the property name of link + * @param response the response containing the link + * @param queryParams query params eg. ?q=xyz&count=12 + * @return the response of the GET request using the given link + */ + private Response applyGETRequestFromLinkWithParams(Response response, String linkPropertyName, String queryParams) { + return applyGETRequestWithQueryParams(response + .then() + .extract() + .path(linkPropertyName), queryParams); + } + + /** + * Apply a GET Request to the given url and return the response. + * + * @param url the url of the GET request + * @param htmlQueryParams query params eg. ?q=xyz&count=12 + * @return the response of the GET request using the given url + */ + private Response applyGETRequestWithQueryParams(String url, String htmlQueryParams) { + return RestAssured.given() + .auth().preemptive().basic(username, password) + .when() + .get(url + htmlQueryParams); + } /** * Apply a GET Request to the given url and return the response. * * @param url the url of the GET request * @return the response of the GET request using the given url - */ + **/ private Response applyGETRequest(String url) { - return RestAssured.given() - .auth().preemptive().basic(username, password) - .when() - .get(url); + return applyGETRequestWithQueryParams(url, ""); } @@ -133,6 +155,12 @@ public class ScmRequests { return new GivenUrl(); } + public AppliedIndexResource requestIndexResource(String username, String password) { + setUsername(username); + setPassword(password); + return new AppliedIndexResource(applyGETRequest(RestUtil.REST_BASE_URL.toString())); + } + public GivenUrl url(URI url) { setUrl(url.toString()); return new GivenUrl(); @@ -480,17 +508,46 @@ public class ScmRequests { } - public class AutoCompleteResponse extends ModelResponse{ + public class AutoCompleteResponse extends ModelResponse { public AutoCompleteResponse(Response response) { super(response); } - public AutoCompleteResponse assertAutoCompleteResults(Consumer> checker){ + public AutoCompleteResponse assertAutoCompleteResults(Consumer> checker) { List result = response.then().extract().path(""); checker.accept(result); return this; } } + + public class AppliedIndexResource extends AppliedRequest { + public AppliedIndexResource(Response response) { + super(response); + } + + public IndexResponse usingIndexResponse() { + return new IndexResponse(super.response); + } + + } + + public class IndexResponse extends ModelResponse { + + public static final String AUTOCOMPLETE_USERS = "_links.autocompleteUsers.href"; + public static final String AUTOCOMPLETE_GROUPS = "_links.autocompleteGroups.href"; + + public IndexResponse(Response response) { + super(response); + } + + public AppliedAutoCompleteRequest requestAutoCompleteUsers(String q) { + return new AppliedAutoCompleteRequest(applyGETRequestFromLinkWithParams(response, AUTOCOMPLETE_USERS, "?q="+q)); + } + + public AppliedAutoCompleteRequest applyAutoCompleteGroups(String q) { + return new AppliedAutoCompleteRequest(applyGETRequestFromLinkWithParams(response, AUTOCOMPLETE_GROUPS, "?q="+q)); + } + } } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AutoCompleteResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AutoCompleteResource.java index 97a1fcccf9..005e29e9ea 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AutoCompleteResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AutoCompleteResource.java @@ -14,8 +14,8 @@ import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; -import javax.ws.rs.core.Response; import java.util.Collection; +import java.util.List; import java.util.stream.Collectors; @@ -50,7 +50,7 @@ public class AutoCompleteResource { @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"user:autocomplete\" privilege"), @ResponseCode(code = 500, condition = "internal server error") }) - public Response searchUser(@NotEmpty(message = PARAMETER_IS_REQUIRED) @Size(min = MIN_SEARCHED_CHARS, message = INVALID_PARAMETER_LENGTH) @QueryParam("q") String filter) { + public List searchUser(@NotEmpty(message = PARAMETER_IS_REQUIRED) @Size(min = MIN_SEARCHED_CHARS, message = INVALID_PARAMETER_LENGTH) @QueryParam("q") String filter) { return map(userManager.autocomplete(filter)); } @@ -64,16 +64,15 @@ public class AutoCompleteResource { @ResponseCode(code = 403, condition = "not authorized, the current user does not have the \"group:autocomplete\" privilege"), @ResponseCode(code = 500, condition = "internal server error") }) - public Response searchGroup(@NotEmpty(message = PARAMETER_IS_REQUIRED) @Size(min = MIN_SEARCHED_CHARS, message = INVALID_PARAMETER_LENGTH) @QueryParam("q") String filter) { + public List searchGroup(@NotEmpty(message = PARAMETER_IS_REQUIRED) @Size(min = MIN_SEARCHED_CHARS, message = INVALID_PARAMETER_LENGTH) @QueryParam("q") String filter) { return map(groupManager.autocomplete(filter)); } - private Response map(Collection autocomplete) { - return Response.ok(autocomplete + private List map(Collection autocomplete) { + return autocomplete .stream() .map(mapper::map) - .collect(Collectors.toList())) - .build(); + .collect(Collectors.toList()); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDtoGenerator.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDtoGenerator.java index c8159f072c..4b78c19cc0 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDtoGenerator.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDtoGenerator.java @@ -34,6 +34,12 @@ public class IndexDtoGenerator { if (UserPermissions.list().isPermitted()) { builder.single(link("users", resourceLinks.userCollection().self())); } + if (UserPermissions.autocomplete().isPermitted()) { + builder.single(link("autocompleteUsers", resourceLinks.autoComplete().users())); + } + if (GroupPermissions.autocomplete().isPermitted()) { + builder.single(link("autocompleteGroups", resourceLinks.autoComplete().groups())); + } if (GroupPermissions.list().isPermitted()) { builder.single(link("groups", resourceLinks.groupCollection().self())); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceLinks.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceLinks.java index 488577618c..820e722814 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceLinks.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceLinks.java @@ -142,6 +142,26 @@ class ResourceLinks { } } + AutoCompleteLinks autoComplete() { + return new AutoCompleteLinks (scmPathInfoStore.get()); + } + + static class AutoCompleteLinks { + private final LinkBuilder linkBuilder; + + AutoCompleteLinks (ScmPathInfo pathInfo) { + linkBuilder = new LinkBuilder(pathInfo, AutoCompleteResource.class); + } + + String users() { + return linkBuilder.method("searchUser").parameters().href(); + } + + String groups() { + return linkBuilder.method("searchGroup").parameters().href(); + } + } + ConfigLinks config() { return new ConfigLinks(scmPathInfoStore.get()); } diff --git a/scm-webapp/src/main/java/sonia/scm/group/DefaultGroupManager.java b/scm-webapp/src/main/java/sonia/scm/group/DefaultGroupManager.java index bdd51836b9..655e0001ce 100644 --- a/scm-webapp/src/main/java/sonia/scm/group/DefaultGroupManager.java +++ b/scm-webapp/src/main/java/sonia/scm/group/DefaultGroupManager.java @@ -50,7 +50,6 @@ import sonia.scm.SCMContextProvider; import sonia.scm.TransformFilter; import sonia.scm.search.SearchRequest; import sonia.scm.search.SearchUtil; -import sonia.scm.user.UserPermissions; import sonia.scm.util.CollectionAppender; import sonia.scm.util.Util; @@ -245,7 +244,7 @@ public class DefaultGroupManager extends AbstractGroupManager @Override public Collection autocomplete(String filter) { - UserPermissions.autocomplete().check(); + GroupPermissions.autocomplete().check(); SearchRequest searchRequest = new SearchRequest(filter, true, DEFAULT_LIMIT); return SearchUtil.search(searchRequest, groupDAO.getAll(), group -> matches(searchRequest,group)?group:null); } diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/IndexResourceTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/IndexResourceTest.java index 83697a3c4a..f2b1da057f 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/IndexResourceTest.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/IndexResourceTest.java @@ -13,7 +13,7 @@ import java.util.Optional; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -@SubjectAware(configuration = "classpath:sonia/scm/shiro-001.ini") +@SubjectAware(configuration = "classpath:sonia/scm/shiro-002.ini") public class IndexResourceTest { @Rule @@ -94,6 +94,24 @@ public class IndexResourceTest { Assertions.assertThat(index.getLinks().getLinkBy("config")).matches(o -> !o.isPresent()); } + @Test + @SubjectAware(username = "trillian", password = "secret") + public void shouldRenderAutoCompleteLinks() { + IndexDto index = indexResource.getIndex(); + + Assertions.assertThat(index.getLinks().getLinkBy("autocompleteUsers")).matches(Optional::isPresent); + Assertions.assertThat(index.getLinks().getLinkBy("autocompleteGroups")).matches(Optional::isPresent); + } + + @Test + @SubjectAware(username = "user_without_autocomplete_permission", password = "secret") + public void userWithoutAutocompletePermissionShouldNotSeeAutoCompleteLinks() { + IndexDto index = indexResource.getIndex(); + + Assertions.assertThat(index.getLinks().getLinkBy("autocompleteUsers")).matches(o -> !o.isPresent()); + Assertions.assertThat(index.getLinks().getLinkBy("autocompleteGroups")).matches(o -> !o.isPresent()); + } + @Test @SubjectAware(username = "dent", password = "secret") public void shouldRenderAdminLinksIfAuthorized() { diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java index eed46a1f6e..c2dc685306 100644 --- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/ResourceLinksMock.java @@ -16,6 +16,7 @@ public class ResourceLinksMock { when(resourceLinks.user()).thenReturn(userLinks); when(resourceLinks.me()).thenReturn(new ResourceLinks.MeLinks(uriInfo,userLinks)); when(resourceLinks.userCollection()).thenReturn(new ResourceLinks.UserCollectionLinks(uriInfo)); + when(resourceLinks.autoComplete()).thenReturn(new ResourceLinks.AutoCompleteLinks(uriInfo)); when(resourceLinks.group()).thenReturn(new ResourceLinks.GroupLinks(uriInfo)); when(resourceLinks.groupCollection()).thenReturn(new ResourceLinks.GroupCollectionLinks(uriInfo)); when(resourceLinks.repository()).thenReturn(new ResourceLinks.RepositoryLinks(uriInfo)); diff --git a/scm-webapp/src/test/resources/sonia/scm/shiro-002.ini b/scm-webapp/src/test/resources/sonia/scm/shiro-002.ini new file mode 100644 index 0000000000..58ad2aa813 --- /dev/null +++ b/scm-webapp/src/test/resources/sonia/scm/shiro-002.ini @@ -0,0 +1,9 @@ +[users] +user_without_autocomplete_permission = secret +trillian = secret, user_ac, group_ac +dent = secret, admin + +[roles] +admin = * +user_ac = user:autocomplete +group_ac = group:autocomplete