From 357ccc7ddb1e3a918da4fb8d36c188bcf8ab78ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Wed, 26 Sep 2018 17:00:13 +0200 Subject: [PATCH 01/21] Introduce index resource with first links --- .../sonia/scm/api/v2/resources/IndexDto.java | 11 +++++ .../api/v2/resources/IndexDtoGenerator.java | 34 ++++++++++++++ .../scm/api/v2/resources/IndexResource.java | 22 ++++++++++ .../scm/api/v2/resources/ResourceLinks.java | 25 ++++++++++- .../DefaultAuthorizationCollector.java | 18 +++++--- .../api/v2/resources/IndexResourceTest.java | 44 +++++++++++++++++++ .../api/v2/resources/ResourceLinksMock.java | 1 + 7 files changed, 148 insertions(+), 7 deletions(-) create mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDto.java create mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDtoGenerator.java create mode 100644 scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexResource.java create mode 100644 scm-webapp/src/test/java/sonia/scm/api/v2/resources/IndexResourceTest.java diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDto.java new file mode 100644 index 0000000000..699ba1ea83 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDto.java @@ -0,0 +1,11 @@ +package sonia.scm.api.v2.resources; + +import de.otto.edison.hal.HalRepresentation; +import de.otto.edison.hal.Links; + +public class IndexDto extends HalRepresentation { + + IndexDto(Links links) { + super(links); + } +} 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 new file mode 100644 index 0000000000..1e969824bf --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDtoGenerator.java @@ -0,0 +1,34 @@ +package sonia.scm.api.v2.resources; + +import de.otto.edison.hal.Link; +import de.otto.edison.hal.Links; +import org.apache.shiro.SecurityUtils; + +import javax.inject.Inject; + +public class IndexDtoGenerator { + + private final ResourceLinks resourceLinks; + + @Inject + public IndexDtoGenerator(ResourceLinks resourceLinks) { + this.resourceLinks = resourceLinks; + } + + public IndexDto generate() { + Links.Builder builder = Links.linkingTo(); + if (SecurityUtils.getSubject().isAuthenticated()) { + builder.single( + Link.link("me", resourceLinks.me().self()), + Link.link("logout", resourceLinks.authentication().logout()) + ); + } else { + builder.single( + Link.link("formLogin", resourceLinks.authentication().formLogin()), + Link.link("jsonLogin", resourceLinks.authentication().jsonLogin()) + ); + } + + return new IndexDto(builder.build()); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexResource.java new file mode 100644 index 0000000000..a6e977d661 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexResource.java @@ -0,0 +1,22 @@ +package sonia.scm.api.v2.resources; + +import javax.inject.Inject; +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +@Path(IndexResource.INDEX_PATH_V2) +public class IndexResource { + public static final String INDEX_PATH_V2 = "v2/"; + + private final IndexDtoGenerator indexDtoGenerator; + + @Inject + public IndexResource(IndexDtoGenerator indexDtoGenerator) { + this.indexDtoGenerator = indexDtoGenerator; + } + + @GET + public IndexDto getIndex() { + return indexDtoGenerator.generate(); + } +} 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 e978de443a..02d7a3d53d 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 @@ -3,7 +3,6 @@ package sonia.scm.api.v2.resources; import sonia.scm.repository.NamespaceAndName; import javax.inject.Inject; -import javax.ws.rs.core.UriInfo; import java.net.URI; class ResourceLinks { @@ -473,4 +472,28 @@ class ResourceLinks { return uiPluginCollectionLinkBuilder.method("plugins").parameters().method("getInstalledPlugins").parameters().href(); } } + + public AuthenticationLinks authentication() { + return new AuthenticationLinks(scmPathInfoStore.get()); + } + + static class AuthenticationLinks { + private final LinkBuilder loginLinkBuilder; + + AuthenticationLinks(ScmPathInfo pathInfo) { + this.loginLinkBuilder = new LinkBuilder(pathInfo, AuthenticationResource.class); + } + + String formLogin() { + return loginLinkBuilder.method("authenticateViaForm").parameters().href(); + } + + String jsonLogin() { + return loginLinkBuilder.method("authenticateViaJSONBody").parameters().href(); + } + + String logout() { + return loginLinkBuilder.method("logout").parameters().href(); + } + } } diff --git a/scm-webapp/src/main/java/sonia/scm/security/DefaultAuthorizationCollector.java b/scm-webapp/src/main/java/sonia/scm/security/DefaultAuthorizationCollector.java index 1b9abb058b..cd22e22712 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/DefaultAuthorizationCollector.java +++ b/scm-webapp/src/main/java/sonia/scm/security/DefaultAuthorizationCollector.java @@ -56,6 +56,7 @@ import sonia.scm.plugin.Extension; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryDAO; import sonia.scm.user.User; +import sonia.scm.user.UserPermissions; import sonia.scm.util.Util; import java.util.List; @@ -74,7 +75,7 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector // TODO move to util class private static final String SEPARATOR = System.getProperty("line.separator", "\n"); - + /** Field description */ private static final String ADMIN_PERMISSION = "*"; @@ -88,7 +89,7 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector LoggerFactory.getLogger(DefaultAuthorizationCollector.class); //~--- constructors --------------------------------------------------------- - + /** * Constructs ... * @@ -209,7 +210,7 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector String perm = permission.getType().getPermissionPrefix().concat(repository.getId()); if (logger.isTraceEnabled()) { - logger.trace("add repository permission {} for user {} at repository {}", + logger.trace("add repository permission {} for user {} at repository {}", perm, user.getName(), repository.getName()); } @@ -254,6 +255,7 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector collectGlobalPermissions(builder, user, groups); collectRepositoryPermissions(builder, user, groups); + builder.add(canReadOwnUser(user)); permissions = builder.build(); } @@ -262,6 +264,10 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector return info; } + private String canReadOwnUser(User user) { + return "user:" + UserPermissions.ACTION_READ + ":" + user.getName(); + } + //~--- get methods ---------------------------------------------------------- private boolean isUserPermitted(User user, GroupNames groups, @@ -272,7 +278,7 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector || ((!perm.isGroupPermission()) && user.getName().equals(perm.getName())); //J+ } - + @Subscribe public void invalidateCache(AuthorizationChangedEvent event) { if (event.isEveryUserAffected()) { @@ -281,12 +287,12 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector invalidateCache(); } } - + private void invalidateUserCache(final String username) { logger.info("invalidate cache for user {}, because of a received authorization event", username); cache.removeAll((CacheKey item) -> username.equalsIgnoreCase(item.username)); } - + private void invalidateCache() { logger.info("invalidate cache, because of a received authorization event"); cache.clear(); 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 new file mode 100644 index 0000000000..f3f6c94006 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/IndexResourceTest.java @@ -0,0 +1,44 @@ +package sonia.scm.api.v2.resources; + +import com.github.sdorra.shiro.ShiroRule; +import com.github.sdorra.shiro.SubjectAware; +import org.assertj.core.api.Assertions; +import org.junit.Rule; +import org.junit.Test; + +import java.net.URI; +import java.util.Optional; + +@SubjectAware(configuration = "classpath:sonia/scm/shiro-001.ini") +public class IndexResourceTest { + + @Rule + public final ShiroRule shiroRule = new ShiroRule(); + + private final IndexDtoGenerator indexDtoGenerator = new IndexDtoGenerator(ResourceLinksMock.createMock(URI.create("/"))); + private final IndexResource indexResource = new IndexResource(indexDtoGenerator); + + @Test + public void shouldRenderLoginUrlsForUnauthenticatedRequest() { + IndexDto index = indexResource.getIndex(); + + Assertions.assertThat(index.getLinks().getLinkBy("formLogin")).matches(Optional::isPresent); + Assertions.assertThat(index.getLinks().getLinkBy("jsonLogin")).matches(Optional::isPresent); + } + + @Test + @SubjectAware(username = "trillian", password = "secret") + public void shouldRenderMeUrlForAuthenticatedRequest() { + IndexDto index = indexResource.getIndex(); + + Assertions.assertThat(index.getLinks().getLinkBy("me")).matches(Optional::isPresent); + } + + @Test + @SubjectAware(username = "trillian", password = "secret") + public void shouldRenderLogoutUrlForAuthenticatedRequest() { + IndexDto index = indexResource.getIndex(); + + Assertions.assertThat(index.getLinks().getLinkBy("logout")).matches(Optional::isPresent); + } +} 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 c70510fe39..2e2c1ae586 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 @@ -34,6 +34,7 @@ public class ResourceLinksMock { when(resourceLinks.repositoryTypeCollection()).thenReturn(new ResourceLinks.RepositoryTypeCollectionLinks(uriInfo)); when(resourceLinks.uiPluginCollection()).thenReturn(new ResourceLinks.UIPluginCollectionLinks(uriInfo)); when(resourceLinks.uiPlugin()).thenReturn(new ResourceLinks.UIPluginLinks(uriInfo)); + when(resourceLinks.authentication()).thenReturn(new ResourceLinks.AuthenticationLinks(uriInfo)); return resourceLinks; } From 987cd54dc709c37d0a2ca03252ff7d4d52296a1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Wed, 26 Sep 2018 17:33:07 +0200 Subject: [PATCH 02/21] Fix missing media type --- scm-core/src/main/java/sonia/scm/web/VndMediaType.java | 1 + .../main/java/sonia/scm/api/v2/resources/IndexResource.java | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/scm-core/src/main/java/sonia/scm/web/VndMediaType.java b/scm-core/src/main/java/sonia/scm/web/VndMediaType.java index 14350902a2..f0711cd1e4 100644 --- a/scm-core/src/main/java/sonia/scm/web/VndMediaType.java +++ b/scm-core/src/main/java/sonia/scm/web/VndMediaType.java @@ -15,6 +15,7 @@ public class VndMediaType { public static final String PLAIN_TEXT_PREFIX = "text/" + SUBTYPE_PREFIX; public static final String PLAIN_TEXT_SUFFIX = "+plain;v=" + VERSION; + public static final String INDEX = PREFIX + "index" + SUFFIX; public static final String USER = PREFIX + "user" + SUFFIX; public static final String GROUP = PREFIX + "group" + SUFFIX; public static final String REPOSITORY = PREFIX + "repository" + SUFFIX; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexResource.java index a6e977d661..f9aa568251 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexResource.java @@ -1,8 +1,12 @@ package sonia.scm.api.v2.resources; +import com.webcohesion.enunciate.metadata.rs.TypeHint; +import sonia.scm.web.VndMediaType; + import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; +import javax.ws.rs.Produces; @Path(IndexResource.INDEX_PATH_V2) public class IndexResource { @@ -16,6 +20,8 @@ public class IndexResource { } @GET + @Produces(VndMediaType.INDEX) + @TypeHint(IndexDto.class) public IndexDto getIndex() { return indexDtoGenerator.generate(); } From f74003e4856be8a3f1b2beca2663c8f0c024716a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Thu, 27 Sep 2018 08:55:07 +0200 Subject: [PATCH 03/21] Introduce new permissions for listings --- .../java/sonia/scm/config/Configuration.java | 2 +- .../src/main/java/sonia/scm/group/Group.java | 2 +- .../src/main/java/sonia/scm/user/User.java | 2 +- .../api/v2/resources/IndexDtoGenerator.java | 13 ++++++++ .../api/v2/resources/IndexResourceTest.java | 31 +++++++++++++++++++ 5 files changed, 47 insertions(+), 3 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/config/Configuration.java b/scm-core/src/main/java/sonia/scm/config/Configuration.java index e9bf3528d5..823c50b155 100644 --- a/scm-core/src/main/java/sonia/scm/config/Configuration.java +++ b/scm-core/src/main/java/sonia/scm/config/Configuration.java @@ -22,7 +22,7 @@ import com.github.sdorra.ssp.StaticPermissions; @StaticPermissions( value = "configuration", permissions = {"read", "write"}, - globalPermissions = {} + globalPermissions = {"list"} ) public interface Configuration extends PermissionObject { } diff --git a/scm-core/src/main/java/sonia/scm/group/Group.java b/scm-core/src/main/java/sonia/scm/group/Group.java index 98d9dcc7a3..5e7f596c58 100644 --- a/scm-core/src/main/java/sonia/scm/group/Group.java +++ b/scm-core/src/main/java/sonia/scm/group/Group.java @@ -60,7 +60,7 @@ import java.util.List; * * @author Sebastian Sdorra */ -@StaticPermissions("group") +@StaticPermissions(value = "group", globalPermissions = {"create", "list"}) @XmlRootElement(name = "groups") @XmlAccessorType(XmlAccessType.FIELD) public class Group extends BasicPropertiesAware diff --git a/scm-core/src/main/java/sonia/scm/user/User.java b/scm-core/src/main/java/sonia/scm/user/User.java index 97c6bb16c7..057554013c 100644 --- a/scm-core/src/main/java/sonia/scm/user/User.java +++ b/scm-core/src/main/java/sonia/scm/user/User.java @@ -55,7 +55,7 @@ import java.security.Principal; * * @author Sebastian Sdorra */ -@StaticPermissions("user") +@StaticPermissions(value = "user", globalPermissions = {"create", "list"}) @XmlRootElement(name = "users") @XmlAccessorType(XmlAccessType.FIELD) public class 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 1e969824bf..72cd2e277f 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 @@ -3,6 +3,9 @@ package sonia.scm.api.v2.resources; import de.otto.edison.hal.Link; import de.otto.edison.hal.Links; import org.apache.shiro.SecurityUtils; +import sonia.scm.config.ConfigurationPermissions; +import sonia.scm.group.GroupPermissions; +import sonia.scm.user.UserPermissions; import javax.inject.Inject; @@ -22,6 +25,16 @@ public class IndexDtoGenerator { Link.link("me", resourceLinks.me().self()), Link.link("logout", resourceLinks.authentication().logout()) ); + if (UserPermissions.list().isPermitted()) { + builder.single(Link.link("users", resourceLinks.userCollection().self())); + } + if (GroupPermissions.list().isPermitted()) { + builder.single(Link.link("groups", resourceLinks.groupCollection().self())); + } + if (ConfigurationPermissions.list().isPermitted()) { + builder.single(Link.link("configuration", resourceLinks.config().self())); + } + builder.single(Link.link("repositories", resourceLinks.repositoryCollection().self())); } else { builder.single( Link.link("formLogin", resourceLinks.authentication().formLogin()), 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 f3f6c94006..c674feabcd 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 @@ -3,12 +3,15 @@ package sonia.scm.api.v2.resources; import com.github.sdorra.shiro.ShiroRule; import com.github.sdorra.shiro.SubjectAware; import org.assertj.core.api.Assertions; +import org.assertj.core.api.Condition; import org.junit.Rule; import org.junit.Test; import java.net.URI; import java.util.Optional; +import static org.mockito.AdditionalMatchers.not; + @SubjectAware(configuration = "classpath:sonia/scm/shiro-001.ini") public class IndexResourceTest { @@ -41,4 +44,32 @@ public class IndexResourceTest { Assertions.assertThat(index.getLinks().getLinkBy("logout")).matches(Optional::isPresent); } + + @Test + @SubjectAware(username = "trillian", password = "secret") + public void shouldRenderRepositoriesForAuthenticatedRequest() { + IndexDto index = indexResource.getIndex(); + + Assertions.assertThat(index.getLinks().getLinkBy("repositories")).matches(Optional::isPresent); + } + + @Test + @SubjectAware(username = "trillian", password = "secret") + public void shouldNotRenderUserCollectionIfNotAuthorized() { + IndexDto index = indexResource.getIndex(); + + Assertions.assertThat(index.getLinks().getLinkBy("users")).matches(o -> !o.isPresent()); + Assertions.assertThat(index.getLinks().getLinkBy("groups")).matches(o -> !o.isPresent()); + Assertions.assertThat(index.getLinks().getLinkBy("configuration")).matches(o -> !o.isPresent()); + } + + @Test + @SubjectAware(username = "dent", password = "secret") + public void shouldRenderUserCollectionIfAuthorized() { + IndexDto index = indexResource.getIndex(); + + Assertions.assertThat(index.getLinks().getLinkBy("users")).matches(Optional::isPresent); + Assertions.assertThat(index.getLinks().getLinkBy("groups")).matches(Optional::isPresent); + Assertions.assertThat(index.getLinks().getLinkBy("configuration")).matches(Optional::isPresent); + } } From 20f3f86f731338250ef3f025ae52e846b6ad53da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Thu, 27 Sep 2018 09:34:37 +0200 Subject: [PATCH 04/21] Fix unit test --- scm-core/src/main/java/sonia/scm/user/User.java | 3 +-- .../scm/security/DefaultAuthorizationCollectorTest.java | 8 +++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/user/User.java b/scm-core/src/main/java/sonia/scm/user/User.java index 057554013c..0d909bec8d 100644 --- a/scm-core/src/main/java/sonia/scm/user/User.java +++ b/scm-core/src/main/java/sonia/scm/user/User.java @@ -58,8 +58,7 @@ import java.security.Principal; @StaticPermissions(value = "user", globalPermissions = {"create", "list"}) @XmlRootElement(name = "users") @XmlAccessorType(XmlAccessType.FIELD) -public class -User extends BasicPropertiesAware implements Principal, ModelObject, PermissionObject +public class User extends BasicPropertiesAware implements Principal, ModelObject, PermissionObject { /** Field description */ diff --git a/scm-webapp/src/test/java/sonia/scm/security/DefaultAuthorizationCollectorTest.java b/scm-webapp/src/test/java/sonia/scm/security/DefaultAuthorizationCollectorTest.java index ed5689c9ce..5e7963ef1d 100644 --- a/scm-webapp/src/test/java/sonia/scm/security/DefaultAuthorizationCollectorTest.java +++ b/scm-webapp/src/test/java/sonia/scm/security/DefaultAuthorizationCollectorTest.java @@ -57,6 +57,7 @@ import sonia.scm.repository.RepositoryTestData; import sonia.scm.user.User; import sonia.scm.user.UserTestData; +import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.nullValue; @@ -160,7 +161,8 @@ public class DefaultAuthorizationCollectorTest { AuthorizationInfo authInfo = collector.collect(); assertThat(authInfo.getRoles(), Matchers.contains(Role.USER)); - assertThat(authInfo.getStringPermissions(), hasSize(0)); + assertThat(authInfo.getStringPermissions(), hasSize(1)); + assertThat(authInfo.getStringPermissions(), contains("user:read:trillian")); assertThat(authInfo.getObjectPermissions(), nullValue()); } @@ -207,7 +209,7 @@ public class DefaultAuthorizationCollectorTest { AuthorizationInfo authInfo = collector.collect(); assertThat(authInfo.getRoles(), Matchers.containsInAnyOrder(Role.USER)); assertThat(authInfo.getObjectPermissions(), nullValue()); - assertThat(authInfo.getStringPermissions(), containsInAnyOrder("repository:read,pull:one", "repository:read,pull,push:two")); + assertThat(authInfo.getStringPermissions(), containsInAnyOrder("repository:read,pull:one", "repository:read,pull,push:two", "user:read:trillian")); } /** @@ -228,7 +230,7 @@ public class DefaultAuthorizationCollectorTest { AuthorizationInfo authInfo = collector.collect(); assertThat(authInfo.getRoles(), Matchers.containsInAnyOrder(Role.USER)); assertThat(authInfo.getObjectPermissions(), nullValue()); - assertThat(authInfo.getStringPermissions(), containsInAnyOrder("one:one", "two:two")); + assertThat(authInfo.getStringPermissions(), containsInAnyOrder("one:one", "two:two", "user:read:trillian")); } private void authenticate(User user, String group, String... groups) { From fdbc5077d2585baa72ee738447973fdf2c3a07c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Thu, 27 Sep 2018 11:50:58 +0200 Subject: [PATCH 05/21] Fix integration tests Every authenticated user should be able to see her own "Me" Resource. --- scm-it/src/test/java/sonia/scm/it/MeITCase.java | 14 -------------- .../src/test/java/sonia/scm/it/UserITCase.java | 17 ----------------- .../java/sonia/scm/it/UserPermissionITCase.java | 2 ++ 3 files changed, 2 insertions(+), 31 deletions(-) diff --git a/scm-it/src/test/java/sonia/scm/it/MeITCase.java b/scm-it/src/test/java/sonia/scm/it/MeITCase.java index 1c7b5c9e03..64f06765ea 100644 --- a/scm-it/src/test/java/sonia/scm/it/MeITCase.java +++ b/scm-it/src/test/java/sonia/scm/it/MeITCase.java @@ -62,18 +62,4 @@ public class MeITCase { .assertType(s -> assertThat(s).isEqualTo(type)) .assertPasswordLinkDoesNotExists(); } - - @Test - public void shouldGet403IfUserIsNotAdmin() { - String newUser = "user"; - String password = "pass"; - String type = "xml"; - TestData.createUser(newUser, password, false, type); - ScmRequests.start() - .given() - .url(TestData.getMeUrl()) - .usernameAndPassword(newUser, password) - .getMeResource() - .assertStatusCode(403); - } } diff --git a/scm-it/src/test/java/sonia/scm/it/UserITCase.java b/scm-it/src/test/java/sonia/scm/it/UserITCase.java index 67fe23dcbc..33fbe0cc5d 100644 --- a/scm-it/src/test/java/sonia/scm/it/UserITCase.java +++ b/scm-it/src/test/java/sonia/scm/it/UserITCase.java @@ -93,21 +93,4 @@ public class UserITCase { .assertType(s -> assertThat(s).isEqualTo(type)) .assertPasswordLinkDoesNotExists(); } - - @Test - public void shouldGet403IfUserIsNotAdmin() { - String newUser = "user"; - String password = "pass"; - String type = "xml"; - TestData.createUser(newUser, password, false, type); - ScmRequests.start() - .given() - .url(TestData.getMeUrl()) - .usernameAndPassword(newUser, password) - .getUserResource() - .assertStatusCode(403); - } - - - } diff --git a/scm-webapp/src/test/java/sonia/scm/it/UserPermissionITCase.java b/scm-webapp/src/test/java/sonia/scm/it/UserPermissionITCase.java index dc960c1b42..3c0e2eed52 100644 --- a/scm-webapp/src/test/java/sonia/scm/it/UserPermissionITCase.java +++ b/scm-webapp/src/test/java/sonia/scm/it/UserPermissionITCase.java @@ -37,6 +37,7 @@ package sonia.scm.it; import com.sun.jersey.api.client.ClientResponse; import de.otto.edison.hal.HalRepresentation; +import org.junit.Assume; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import sonia.scm.api.rest.ObjectMapperProvider; @@ -158,6 +159,7 @@ public class UserPermissionITCase extends AbstractPermissionITCaseBase @Override protected void checkGetAllResponse(ClientResponse response) { + Assume.assumeTrue(credentials.getUsername() == null); if (!credentials.isAnonymous()) { assertNotNull(response); From 6361ae35c184b768db18ff72351a451dcff2c63e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Fri, 28 Sep 2018 14:17:48 +0200 Subject: [PATCH 06/21] Add config links for git, hg and svn --- .../resources/GitConfigInIndexResource.java | 45 +++++++++++++++++++ .../v2/resources/HgConfigInIndexResource.java | 45 +++++++++++++++++++ .../resources/SvnConfigInIndexResource.java | 45 +++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigInIndexResource.java create mode 100644 scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInIndexResource.java create mode 100644 scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/v2/resources/SvnConfigInIndexResource.java diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigInIndexResource.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigInIndexResource.java new file mode 100644 index 0000000000..59cd79c13b --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigInIndexResource.java @@ -0,0 +1,45 @@ +package sonia.scm.api.v2.resources; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import sonia.scm.config.ConfigurationPermissions; +import sonia.scm.plugin.Extension; +import sonia.scm.web.JsonEnricher; +import sonia.scm.web.JsonEnricherContext; +import sonia.scm.web.VndMediaType; + +import javax.inject.Inject; +import javax.inject.Provider; + +@Extension +public class GitConfigInIndexResource implements JsonEnricher { + + private final Provider scmPathInfoStore; + private final ObjectMapper objectMapper; + + @Inject + public GitConfigInIndexResource(Provider scmPathInfoStore, ObjectMapper objectMapper) { + this.scmPathInfoStore = scmPathInfoStore; + this.objectMapper = objectMapper; + } + + @Override + public void enrich(JsonEnricherContext context) { + if (isIndexRequest(context) && ConfigurationPermissions.list().isPermitted()) { + String gitConfigUrl = new LinkBuilder(scmPathInfoStore.get().get(), GitConfigResource.class) + .method("get") + .parameters() + .href(); + + ObjectNode gitConfigRefNode = objectMapper.createObjectNode(); + gitConfigRefNode.set("href", objectMapper.convertValue(gitConfigUrl, JsonNode.class)); + + ((ObjectNode) context.getResponseEntity().get("_links")).put("gitConfig", gitConfigRefNode); + } + } + + private boolean isIndexRequest(JsonEnricherContext context) { + return VndMediaType.INDEX.equals(context.getResponseMediaType().toString()); + } +} diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInIndexResource.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInIndexResource.java new file mode 100644 index 0000000000..131caad0c0 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInIndexResource.java @@ -0,0 +1,45 @@ +package sonia.scm.api.v2.resources; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import sonia.scm.config.ConfigurationPermissions; +import sonia.scm.plugin.Extension; +import sonia.scm.web.JsonEnricher; +import sonia.scm.web.JsonEnricherContext; +import sonia.scm.web.VndMediaType; + +import javax.inject.Inject; +import javax.inject.Provider; + +@Extension +public class HgConfigInIndexResource implements JsonEnricher { + + private final Provider scmPathInfoStore; + private final ObjectMapper objectMapper; + + @Inject + public HgConfigInIndexResource(Provider scmPathInfoStore, ObjectMapper objectMapper) { + this.scmPathInfoStore = scmPathInfoStore; + this.objectMapper = objectMapper; + } + + @Override + public void enrich(JsonEnricherContext context) { + if (isIndexRequest(context) && ConfigurationPermissions.list().isPermitted()) { + String gitConfigUrl = new LinkBuilder(scmPathInfoStore.get().get(), HgConfigResource.class) + .method("get") + .parameters() + .href(); + + ObjectNode gitConfigRefNode = objectMapper.createObjectNode(); + gitConfigRefNode.set("href", objectMapper.convertValue(gitConfigUrl, JsonNode.class)); + + ((ObjectNode) context.getResponseEntity().get("_links")).put("hgConfig", gitConfigRefNode); + } + } + + private boolean isIndexRequest(JsonEnricherContext context) { + return VndMediaType.INDEX.equals(context.getResponseMediaType().toString()); + } +} diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/v2/resources/SvnConfigInIndexResource.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/v2/resources/SvnConfigInIndexResource.java new file mode 100644 index 0000000000..68c169834d --- /dev/null +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/v2/resources/SvnConfigInIndexResource.java @@ -0,0 +1,45 @@ +package sonia.scm.api.v2.resources; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import sonia.scm.config.ConfigurationPermissions; +import sonia.scm.plugin.Extension; +import sonia.scm.web.JsonEnricher; +import sonia.scm.web.JsonEnricherContext; +import sonia.scm.web.VndMediaType; + +import javax.inject.Inject; +import javax.inject.Provider; + +@Extension +public class SvnConfigInIndexResource implements JsonEnricher { + + private final Provider scmPathInfoStore; + private final ObjectMapper objectMapper; + + @Inject + public SvnConfigInIndexResource(Provider scmPathInfoStore, ObjectMapper objectMapper) { + this.scmPathInfoStore = scmPathInfoStore; + this.objectMapper = objectMapper; + } + + @Override + public void enrich(JsonEnricherContext context) { + if (isIndexRequest(context) && ConfigurationPermissions.list().isPermitted()) { + String gitConfigUrl = new LinkBuilder(scmPathInfoStore.get().get(), SvnConfigResource.class) + .method("get") + .parameters() + .href(); + + ObjectNode gitConfigRefNode = objectMapper.createObjectNode(); + gitConfigRefNode.set("href", objectMapper.convertValue(gitConfigUrl, JsonNode.class)); + + ((ObjectNode) context.getResponseEntity().get("_links")).put("svnConfig", gitConfigRefNode); + } + } + + private boolean isIndexRequest(JsonEnricherContext context) { + return VndMediaType.INDEX.equals(context.getResponseMediaType().toString()); + } +} From ed9b10b86f5026d9e1f4b74357667743fafaf579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Fri, 28 Sep 2018 14:40:26 +0200 Subject: [PATCH 07/21] Permit unathenticated index access --- .../main/java/sonia/scm/security/SecurityRequests.java | 10 ++++++++++ .../scm/web/security/ApiAuthenticationFilter.java | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/scm-webapp/src/main/java/sonia/scm/security/SecurityRequests.java b/scm-webapp/src/main/java/sonia/scm/security/SecurityRequests.java index 81bb2092c9..7b467c237a 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/SecurityRequests.java +++ b/scm-webapp/src/main/java/sonia/scm/security/SecurityRequests.java @@ -11,6 +11,7 @@ import static sonia.scm.api.v2.resources.ScmPathInfo.REST_API_PATH; public final class SecurityRequests { private static final Pattern URI_LOGIN_PATTERN = Pattern.compile(REST_API_PATH + "(?:/v2)?/auth/access_token"); + private static final Pattern URI_INDEX_PATTERN = Pattern.compile(REST_API_PATH + "/v2/?"); private SecurityRequests() {} @@ -23,4 +24,13 @@ public final class SecurityRequests { return URI_LOGIN_PATTERN.matcher(uri).matches(); } + public static boolean isIndexRequest(HttpServletRequest request) { + String uri = request.getRequestURI().substring(request.getContextPath().length()); + return isAuthenticationRequest(uri); + } + + public static boolean isIndexRequest(String uri) { + return URI_INDEX_PATTERN.matcher(uri).matches(); + } + } diff --git a/scm-webapp/src/main/java/sonia/scm/web/security/ApiAuthenticationFilter.java b/scm-webapp/src/main/java/sonia/scm/web/security/ApiAuthenticationFilter.java index d8fe469af9..26c8d65250 100644 --- a/scm-webapp/src/main/java/sonia/scm/web/security/ApiAuthenticationFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/web/security/ApiAuthenticationFilter.java @@ -99,7 +99,7 @@ public class ApiAuthenticationFilter extends AuthenticationFilter throws IOException, ServletException { // skip filter on login resource - if (SecurityRequests.isAuthenticationRequest(request)) + if (SecurityRequests.isAuthenticationRequest(request) || SecurityRequests.isIndexRequest(request)) { chain.doFilter(request, response); } From e28f30fbeab2c7af12867a9971983430d3bc8608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Fri, 28 Sep 2018 15:55:42 +0200 Subject: [PATCH 08/21] Change correct filter for security --- scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java | 2 +- .../src/main/java/sonia/scm/security/SecurityRequests.java | 2 +- .../java/sonia/scm/web/security/ApiAuthenticationFilter.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java b/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java index de0d689c52..d97a5b050e 100644 --- a/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/filter/SecurityFilter.java @@ -84,7 +84,7 @@ public class SecurityFilter extends HttpFilter HttpServletResponse response, FilterChain chain) throws IOException, ServletException { - if (!SecurityRequests.isAuthenticationRequest(request)) + if (!SecurityRequests.isAuthenticationRequest(request) && !SecurityRequests.isIndexRequest(request)) { Subject subject = SecurityUtils.getSubject(); if (hasPermission(subject)) diff --git a/scm-webapp/src/main/java/sonia/scm/security/SecurityRequests.java b/scm-webapp/src/main/java/sonia/scm/security/SecurityRequests.java index 7b467c237a..49d03f598b 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/SecurityRequests.java +++ b/scm-webapp/src/main/java/sonia/scm/security/SecurityRequests.java @@ -26,7 +26,7 @@ public final class SecurityRequests { public static boolean isIndexRequest(HttpServletRequest request) { String uri = request.getRequestURI().substring(request.getContextPath().length()); - return isAuthenticationRequest(uri); + return isIndexRequest(uri); } public static boolean isIndexRequest(String uri) { diff --git a/scm-webapp/src/main/java/sonia/scm/web/security/ApiAuthenticationFilter.java b/scm-webapp/src/main/java/sonia/scm/web/security/ApiAuthenticationFilter.java index 26c8d65250..c2444b43f5 100644 --- a/scm-webapp/src/main/java/sonia/scm/web/security/ApiAuthenticationFilter.java +++ b/scm-webapp/src/main/java/sonia/scm/web/security/ApiAuthenticationFilter.java @@ -99,7 +99,7 @@ public class ApiAuthenticationFilter extends AuthenticationFilter throws IOException, ServletException { // skip filter on login resource - if (SecurityRequests.isAuthenticationRequest(request) || SecurityRequests.isIndexRequest(request)) + if (SecurityRequests.isAuthenticationRequest(request) ) { chain.doFilter(request, response); } From 1440fa7b9d6ff0d06cd1a7b929c3615d970ec992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Fri, 28 Sep 2018 15:55:57 +0200 Subject: [PATCH 09/21] Add integration test --- .../test/java/sonia/scm/it/IndexITCase.java | 48 +++++++++++++++++++ .../java/sonia/scm/it/utils/RegExMatcher.java | 2 +- .../api/v2/resources/IndexDtoGenerator.java | 2 +- 3 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 scm-it/src/test/java/sonia/scm/it/IndexITCase.java diff --git a/scm-it/src/test/java/sonia/scm/it/IndexITCase.java b/scm-it/src/test/java/sonia/scm/it/IndexITCase.java new file mode 100644 index 0000000000..67090686f4 --- /dev/null +++ b/scm-it/src/test/java/sonia/scm/it/IndexITCase.java @@ -0,0 +1,48 @@ +package sonia.scm.it; + +import io.restassured.RestAssured; +import org.apache.http.HttpStatus; +import org.junit.Test; +import sonia.scm.it.utils.RestUtil; +import sonia.scm.web.VndMediaType; + +import static sonia.scm.it.utils.RegExMatcher.matchesPattern; +import static sonia.scm.it.utils.RestUtil.given; + +public class IndexITCase { + + @Test + public void shouldLinkEverythingForAdmin() { + given(VndMediaType.INDEX) + + .when() + .get(RestUtil.createResourceUrl("")) + + .then() + .statusCode(HttpStatus.SC_OK) + .body( + "_links.repositories.href", matchesPattern(".+/repositories/"), + "_links.users.href", matchesPattern(".+/users/"), + "_links.groups.href", matchesPattern(".+/groups/"), + "_links.config.href", matchesPattern(".+/config"), + "_links.gitConfig.href", matchesPattern(".+/config/git"), + "_links.hgConfig.href", matchesPattern(".+/config/hg"), + "_links.svnConfig.href", matchesPattern(".+/config/svn") + ); + } + + @Test + public void shouldCreateLoginLinksForAnonymousAccess() { + RestAssured.given() // do not specify user credentials + + .when() + .get(RestUtil.createResourceUrl("")) + + .then() + .statusCode(HttpStatus.SC_OK) + .body( + "_links.formLogin.href", matchesPattern(".+/auth/.+") + ); + } + +} diff --git a/scm-it/src/test/java/sonia/scm/it/utils/RegExMatcher.java b/scm-it/src/test/java/sonia/scm/it/utils/RegExMatcher.java index 10386a682f..8fb9fdf798 100644 --- a/scm-it/src/test/java/sonia/scm/it/utils/RegExMatcher.java +++ b/scm-it/src/test/java/sonia/scm/it/utils/RegExMatcher.java @@ -24,6 +24,6 @@ public class RegExMatcher extends BaseMatcher { @Override public boolean matches(Object o) { - return Pattern.compile(pattern).matcher(o.toString()).matches(); + return o != null && Pattern.compile(pattern).matcher(o.toString()).matches(); } } 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 72cd2e277f..b7a7415853 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 @@ -32,7 +32,7 @@ public class IndexDtoGenerator { builder.single(Link.link("groups", resourceLinks.groupCollection().self())); } if (ConfigurationPermissions.list().isPermitted()) { - builder.single(Link.link("configuration", resourceLinks.config().self())); + builder.single(Link.link("config", resourceLinks.config().self())); } builder.single(Link.link("repositories", resourceLinks.repositoryCollection().self())); } else { From 8f139c8d4ab264ea259077ded30de8cd40fd572b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Fri, 28 Sep 2018 16:53:09 +0200 Subject: [PATCH 10/21] Reduce code duplication for json object modification --- .../java/sonia/scm/web/JsonEnricherBase.java | 40 +++++++++++++++++++ .../resources/GitConfigInIndexResource.java | 23 +++++------ .../v2/resources/HgConfigInIndexResource.java | 23 +++++------ .../resources/SvnConfigInIndexResource.java | 23 +++++------ 4 files changed, 67 insertions(+), 42 deletions(-) create mode 100644 scm-core/src/main/java/sonia/scm/web/JsonEnricherBase.java diff --git a/scm-core/src/main/java/sonia/scm/web/JsonEnricherBase.java b/scm-core/src/main/java/sonia/scm/web/JsonEnricherBase.java new file mode 100644 index 0000000000..b56e67bb10 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/web/JsonEnricherBase.java @@ -0,0 +1,40 @@ +package sonia.scm.web; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import java.util.Map; + +public abstract class JsonEnricherBase implements JsonEnricher { + + private final ObjectMapper objectMapper; + + protected JsonEnricherBase(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + protected boolean resultHasMediaType(String mediaType, JsonEnricherContext context) { + return mediaType.equals(context.getResponseMediaType().toString()); + } + + protected JsonNode value(Object gitConfigUrl) { + return objectMapper.convertValue(gitConfigUrl, JsonNode.class); + } + + protected ObjectNode createObject() { + return objectMapper.createObjectNode(); + } + + protected ObjectNode createObject(Map values) { + ObjectNode object = createObject(); + + values.forEach((key, value) -> object.set(key, value(value))); + + return object; + } + + protected void addPropertyNode(JsonNode parent, String newKey, JsonNode child) { + ((ObjectNode) parent).put(newKey, child); + } +} diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigInIndexResource.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigInIndexResource.java index 59cd79c13b..a1120adda4 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigInIndexResource.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/api/v2/resources/GitConfigInIndexResource.java @@ -2,44 +2,39 @@ package sonia.scm.api.v2.resources; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; import sonia.scm.config.ConfigurationPermissions; import sonia.scm.plugin.Extension; -import sonia.scm.web.JsonEnricher; +import sonia.scm.web.JsonEnricherBase; import sonia.scm.web.JsonEnricherContext; -import sonia.scm.web.VndMediaType; import javax.inject.Inject; import javax.inject.Provider; +import static java.util.Collections.singletonMap; +import static sonia.scm.web.VndMediaType.INDEX; + @Extension -public class GitConfigInIndexResource implements JsonEnricher { +public class GitConfigInIndexResource extends JsonEnricherBase { private final Provider scmPathInfoStore; - private final ObjectMapper objectMapper; @Inject public GitConfigInIndexResource(Provider scmPathInfoStore, ObjectMapper objectMapper) { + super(objectMapper); this.scmPathInfoStore = scmPathInfoStore; - this.objectMapper = objectMapper; } @Override public void enrich(JsonEnricherContext context) { - if (isIndexRequest(context) && ConfigurationPermissions.list().isPermitted()) { + if (resultHasMediaType(INDEX, context) && ConfigurationPermissions.list().isPermitted()) { String gitConfigUrl = new LinkBuilder(scmPathInfoStore.get().get(), GitConfigResource.class) .method("get") .parameters() .href(); - ObjectNode gitConfigRefNode = objectMapper.createObjectNode(); - gitConfigRefNode.set("href", objectMapper.convertValue(gitConfigUrl, JsonNode.class)); + JsonNode gitConfigRefNode = createObject(singletonMap("href", value(gitConfigUrl))); - ((ObjectNode) context.getResponseEntity().get("_links")).put("gitConfig", gitConfigRefNode); + addPropertyNode(context.getResponseEntity().get("_links"), "gitConfig", gitConfigRefNode); } } - - private boolean isIndexRequest(JsonEnricherContext context) { - return VndMediaType.INDEX.equals(context.getResponseMediaType().toString()); - } } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInIndexResource.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInIndexResource.java index 131caad0c0..0507987a26 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInIndexResource.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInIndexResource.java @@ -2,44 +2,39 @@ package sonia.scm.api.v2.resources; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; import sonia.scm.config.ConfigurationPermissions; import sonia.scm.plugin.Extension; -import sonia.scm.web.JsonEnricher; +import sonia.scm.web.JsonEnricherBase; import sonia.scm.web.JsonEnricherContext; -import sonia.scm.web.VndMediaType; import javax.inject.Inject; import javax.inject.Provider; +import static java.util.Collections.singletonMap; +import static sonia.scm.web.VndMediaType.INDEX; + @Extension -public class HgConfigInIndexResource implements JsonEnricher { +public class HgConfigInIndexResource extends JsonEnricherBase { private final Provider scmPathInfoStore; - private final ObjectMapper objectMapper; @Inject public HgConfigInIndexResource(Provider scmPathInfoStore, ObjectMapper objectMapper) { + super(objectMapper); this.scmPathInfoStore = scmPathInfoStore; - this.objectMapper = objectMapper; } @Override public void enrich(JsonEnricherContext context) { - if (isIndexRequest(context) && ConfigurationPermissions.list().isPermitted()) { + if (resultHasMediaType(INDEX, context) && ConfigurationPermissions.list().isPermitted()) { String gitConfigUrl = new LinkBuilder(scmPathInfoStore.get().get(), HgConfigResource.class) .method("get") .parameters() .href(); - ObjectNode gitConfigRefNode = objectMapper.createObjectNode(); - gitConfigRefNode.set("href", objectMapper.convertValue(gitConfigUrl, JsonNode.class)); + JsonNode hgConfigRefNode = createObject(singletonMap("href", value(gitConfigUrl))); - ((ObjectNode) context.getResponseEntity().get("_links")).put("hgConfig", gitConfigRefNode); + addPropertyNode(context.getResponseEntity().get("_links"), "hgConfig", hgConfigRefNode); } } - - private boolean isIndexRequest(JsonEnricherContext context) { - return VndMediaType.INDEX.equals(context.getResponseMediaType().toString()); - } } diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/v2/resources/SvnConfigInIndexResource.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/v2/resources/SvnConfigInIndexResource.java index 68c169834d..5f9e4837b1 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/v2/resources/SvnConfigInIndexResource.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/v2/resources/SvnConfigInIndexResource.java @@ -2,44 +2,39 @@ package sonia.scm.api.v2.resources; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; import sonia.scm.config.ConfigurationPermissions; import sonia.scm.plugin.Extension; -import sonia.scm.web.JsonEnricher; +import sonia.scm.web.JsonEnricherBase; import sonia.scm.web.JsonEnricherContext; -import sonia.scm.web.VndMediaType; import javax.inject.Inject; import javax.inject.Provider; +import static java.util.Collections.singletonMap; +import static sonia.scm.web.VndMediaType.INDEX; + @Extension -public class SvnConfigInIndexResource implements JsonEnricher { +public class SvnConfigInIndexResource extends JsonEnricherBase { private final Provider scmPathInfoStore; - private final ObjectMapper objectMapper; @Inject public SvnConfigInIndexResource(Provider scmPathInfoStore, ObjectMapper objectMapper) { + super(objectMapper); this.scmPathInfoStore = scmPathInfoStore; - this.objectMapper = objectMapper; } @Override public void enrich(JsonEnricherContext context) { - if (isIndexRequest(context) && ConfigurationPermissions.list().isPermitted()) { + if (resultHasMediaType(INDEX, context) && ConfigurationPermissions.list().isPermitted()) { String gitConfigUrl = new LinkBuilder(scmPathInfoStore.get().get(), SvnConfigResource.class) .method("get") .parameters() .href(); - ObjectNode gitConfigRefNode = objectMapper.createObjectNode(); - gitConfigRefNode.set("href", objectMapper.convertValue(gitConfigUrl, JsonNode.class)); + JsonNode gitConfigRefNode = createObject(singletonMap("href", value(gitConfigUrl))); - ((ObjectNode) context.getResponseEntity().get("_links")).put("svnConfig", gitConfigRefNode); + addPropertyNode(context.getResponseEntity().get("_links"), "svnConfig", gitConfigRefNode); } } - - private boolean isIndexRequest(JsonEnricherContext context) { - return VndMediaType.INDEX.equals(context.getResponseMediaType().toString()); - } } From 80740cfb0e58e52831dbd799598fb6168dcbbc6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Mon, 1 Oct 2018 09:22:41 +0200 Subject: [PATCH 11/21] Correct config link name in unit test --- .../sonia/scm/api/v2/resources/IndexResourceTest.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) 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 c674feabcd..73bff79991 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 @@ -3,15 +3,12 @@ package sonia.scm.api.v2.resources; import com.github.sdorra.shiro.ShiroRule; import com.github.sdorra.shiro.SubjectAware; import org.assertj.core.api.Assertions; -import org.assertj.core.api.Condition; import org.junit.Rule; import org.junit.Test; import java.net.URI; import java.util.Optional; -import static org.mockito.AdditionalMatchers.not; - @SubjectAware(configuration = "classpath:sonia/scm/shiro-001.ini") public class IndexResourceTest { @@ -55,21 +52,21 @@ public class IndexResourceTest { @Test @SubjectAware(username = "trillian", password = "secret") - public void shouldNotRenderUserCollectionIfNotAuthorized() { + public void shouldNotRenderAdminLinksIfNotAuthorized() { IndexDto index = indexResource.getIndex(); Assertions.assertThat(index.getLinks().getLinkBy("users")).matches(o -> !o.isPresent()); Assertions.assertThat(index.getLinks().getLinkBy("groups")).matches(o -> !o.isPresent()); - Assertions.assertThat(index.getLinks().getLinkBy("configuration")).matches(o -> !o.isPresent()); + Assertions.assertThat(index.getLinks().getLinkBy("config")).matches(o -> !o.isPresent()); } @Test @SubjectAware(username = "dent", password = "secret") - public void shouldRenderUserCollectionIfAuthorized() { + public void shouldRenderAdminLinksIfAuthorized() { IndexDto index = indexResource.getIndex(); Assertions.assertThat(index.getLinks().getLinkBy("users")).matches(Optional::isPresent); Assertions.assertThat(index.getLinks().getLinkBy("groups")).matches(Optional::isPresent); - Assertions.assertThat(index.getLinks().getLinkBy("configuration")).matches(Optional::isPresent); + Assertions.assertThat(index.getLinks().getLinkBy("config")).matches(Optional::isPresent); } } From aed4c60296f6b7cdaee0e01861dd4bf0f28e12f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Mon, 1 Oct 2018 12:58:53 +0200 Subject: [PATCH 12/21] Add missing unit tests --- .../GitConfigInIndexResourceTest.java | 64 +++++++++++++++++++ .../sonia/scm/configuration/shiro.ini | 2 + .../HgConfigInIndexResourceTest.java | 64 +++++++++++++++++++ .../sonia/scm/configuration/shiro.ini | 2 + .../SvnConfigInIndexResourceTest.java | 64 +++++++++++++++++++ .../sonia/scm/configuration/shiro.ini | 2 + 6 files changed, 198 insertions(+) create mode 100644 scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigInIndexResourceTest.java create mode 100644 scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInIndexResourceTest.java create mode 100644 scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigInIndexResourceTest.java diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigInIndexResourceTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigInIndexResourceTest.java new file mode 100644 index 0000000000..3151d539bf --- /dev/null +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigInIndexResourceTest.java @@ -0,0 +1,64 @@ +package sonia.scm.api.v2.resources; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.github.sdorra.shiro.ShiroRule; +import com.github.sdorra.shiro.SubjectAware; +import com.google.inject.util.Providers; +import org.junit.Rule; +import org.junit.Test; +import sonia.scm.web.JsonEnricherContext; +import sonia.scm.web.VndMediaType; + +import javax.ws.rs.core.MediaType; +import java.net.URI; + +import static org.junit.Assert.assertEquals; + +@SubjectAware(configuration = "classpath:sonia/scm/configuration/shiro.ini") +public class GitConfigInIndexResourceTest { + + @Rule + public final ShiroRule shiroRule = new ShiroRule(); + + private final ObjectMapper objectMapper = new ObjectMapper(); + private final ObjectNode root = objectMapper.createObjectNode(); + private final GitConfigInIndexResource gitConfigInIndexResource; + + public GitConfigInIndexResourceTest() { + root.put("_links", objectMapper.createObjectNode()); + ScmPathInfoStore pathInfoStore = new ScmPathInfoStore(); + pathInfoStore.set(() -> URI.create("/")); + gitConfigInIndexResource = new GitConfigInIndexResource(Providers.of(pathInfoStore), objectMapper); + } + + @Test + @SubjectAware(username = "admin", password = "secret") + public void admin() { + JsonEnricherContext context = new JsonEnricherContext(URI.create("/index"), MediaType.valueOf(VndMediaType.INDEX), root); + + gitConfigInIndexResource.enrich(context); + + System.out.println(root); + assertEquals("/v2/config/git", root.get("_links").get("gitConfig").get("href").asText()); + } + + @Test + @SubjectAware(username = "readOnly", password = "secret") + public void user() { + JsonEnricherContext context = new JsonEnricherContext(URI.create("/index"), MediaType.valueOf(VndMediaType.INDEX), root); + + gitConfigInIndexResource.enrich(context); + + System.out.println(root); + } + + @Test + public void anonymous() { + JsonEnricherContext context = new JsonEnricherContext(URI.create("/index"), MediaType.valueOf(VndMediaType.INDEX), root); + + gitConfigInIndexResource.enrich(context); + + System.out.println(root); + } +} diff --git a/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/configuration/shiro.ini b/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/configuration/shiro.ini index 36226edd7d..5d30a000f2 100644 --- a/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/configuration/shiro.ini +++ b/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/configuration/shiro.ini @@ -2,8 +2,10 @@ readOnly = secret, reader writeOnly = secret, writer readWrite = secret, readerWriter +admin = secret, admin [roles] reader = configuration:read:git writer = configuration:write:git readerWriter = configuration:*:git +admin = * diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInIndexResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInIndexResourceTest.java new file mode 100644 index 0000000000..a2b500e9f6 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInIndexResourceTest.java @@ -0,0 +1,64 @@ +package sonia.scm.api.v2.resources; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.github.sdorra.shiro.ShiroRule; +import com.github.sdorra.shiro.SubjectAware; +import com.google.inject.util.Providers; +import org.junit.Rule; +import org.junit.Test; +import sonia.scm.web.JsonEnricherContext; +import sonia.scm.web.VndMediaType; + +import javax.ws.rs.core.MediaType; +import java.net.URI; + +import static org.junit.Assert.assertEquals; + +@SubjectAware(configuration = "classpath:sonia/scm/configuration/shiro.ini") +public class HgConfigInIndexResourceTest { + + @Rule + public final ShiroRule shiroRule = new ShiroRule(); + + private final ObjectMapper objectMapper = new ObjectMapper(); + private final ObjectNode root = objectMapper.createObjectNode(); + private final HgConfigInIndexResource hgConfigInIndexResource; + + public HgConfigInIndexResourceTest() { + root.put("_links", objectMapper.createObjectNode()); + ScmPathInfoStore pathInfoStore = new ScmPathInfoStore(); + pathInfoStore.set(() -> URI.create("/")); + hgConfigInIndexResource = new HgConfigInIndexResource(Providers.of(pathInfoStore), objectMapper); + } + + @Test + @SubjectAware(username = "admin", password = "secret") + public void admin() { + JsonEnricherContext context = new JsonEnricherContext(URI.create("/index"), MediaType.valueOf(VndMediaType.INDEX), root); + + hgConfigInIndexResource.enrich(context); + + System.out.println(root); + assertEquals("/v2/config/hg", root.get("_links").get("hgConfig").get("href").asText()); + } + + @Test + @SubjectAware(username = "readOnly", password = "secret") + public void user() { + JsonEnricherContext context = new JsonEnricherContext(URI.create("/index"), MediaType.valueOf(VndMediaType.INDEX), root); + + hgConfigInIndexResource.enrich(context); + + System.out.println(root); + } + + @Test + public void anonymous() { + JsonEnricherContext context = new JsonEnricherContext(URI.create("/index"), MediaType.valueOf(VndMediaType.INDEX), root); + + hgConfigInIndexResource.enrich(context); + + System.out.println(root); + } +} diff --git a/scm-plugins/scm-hg-plugin/src/test/resources/sonia/scm/configuration/shiro.ini b/scm-plugins/scm-hg-plugin/src/test/resources/sonia/scm/configuration/shiro.ini index fc08bb83ac..d8083a04c9 100644 --- a/scm-plugins/scm-hg-plugin/src/test/resources/sonia/scm/configuration/shiro.ini +++ b/scm-plugins/scm-hg-plugin/src/test/resources/sonia/scm/configuration/shiro.ini @@ -2,8 +2,10 @@ readOnly = secret, reader writeOnly = secret, writer readWrite = secret, readerWriter +admin = secret, admin [roles] reader = configuration:read:hg writer = configuration:write:hg readerWriter = configuration:*:hg +admin = * diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigInIndexResourceTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigInIndexResourceTest.java new file mode 100644 index 0000000000..762d024125 --- /dev/null +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigInIndexResourceTest.java @@ -0,0 +1,64 @@ +package sonia.scm.api.v2.resources; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.github.sdorra.shiro.ShiroRule; +import com.github.sdorra.shiro.SubjectAware; +import com.google.inject.util.Providers; +import org.junit.Rule; +import org.junit.Test; +import sonia.scm.web.JsonEnricherContext; +import sonia.scm.web.VndMediaType; + +import javax.ws.rs.core.MediaType; +import java.net.URI; + +import static org.junit.Assert.assertEquals; + +@SubjectAware(configuration = "classpath:sonia/scm/configuration/shiro.ini") +public class SvnConfigInIndexResourceTest { + + @Rule + public final ShiroRule shiroRule = new ShiroRule(); + + private final ObjectMapper objectMapper = new ObjectMapper(); + private final ObjectNode root = objectMapper.createObjectNode(); + private final SvnConfigInIndexResource svnConfigInIndexResource; + + public SvnConfigInIndexResourceTest() { + root.put("_links", objectMapper.createObjectNode()); + ScmPathInfoStore pathInfoStore = new ScmPathInfoStore(); + pathInfoStore.set(() -> URI.create("/")); + svnConfigInIndexResource = new SvnConfigInIndexResource(Providers.of(pathInfoStore), objectMapper); + } + + @Test + @SubjectAware(username = "admin", password = "secret") + public void admin() { + JsonEnricherContext context = new JsonEnricherContext(URI.create("/index"), MediaType.valueOf(VndMediaType.INDEX), root); + + svnConfigInIndexResource.enrich(context); + + System.out.println(root); + assertEquals("/v2/config/svn", root.get("_links").get("svnConfig").get("href").asText()); + } + + @Test + @SubjectAware(username = "readOnly", password = "secret") + public void user() { + JsonEnricherContext context = new JsonEnricherContext(URI.create("/index"), MediaType.valueOf(VndMediaType.INDEX), root); + + svnConfigInIndexResource.enrich(context); + + System.out.println(root); + } + + @Test + public void anonymous() { + JsonEnricherContext context = new JsonEnricherContext(URI.create("/index"), MediaType.valueOf(VndMediaType.INDEX), root); + + svnConfigInIndexResource.enrich(context); + + System.out.println(root); + } +} diff --git a/scm-plugins/scm-svn-plugin/src/test/resources/sonia/scm/configuration/shiro.ini b/scm-plugins/scm-svn-plugin/src/test/resources/sonia/scm/configuration/shiro.ini index 7e4233b540..fe84723e0a 100644 --- a/scm-plugins/scm-svn-plugin/src/test/resources/sonia/scm/configuration/shiro.ini +++ b/scm-plugins/scm-svn-plugin/src/test/resources/sonia/scm/configuration/shiro.ini @@ -2,8 +2,10 @@ readOnly = secret, reader writeOnly = secret, writer readWrite = secret, readerWriter +admin = secret, admin [roles] reader = configuration:read:svn writer = configuration:write:svn readerWriter = configuration:*:svn +admin = * From 2c5fdd6b41b42a6cef90ecc02984a62dbe42aba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Mon, 1 Oct 2018 12:59:45 +0200 Subject: [PATCH 13/21] Add ui plugin and self links --- .../api/v2/resources/IndexDtoGenerator.java | 21 +++++++------ .../scm/api/v2/resources/IndexResource.java | 1 + .../scm/api/v2/resources/ResourceLinks.java | 18 ++++++++++- .../api/v2/resources/IndexResourceTest.java | 30 +++++++++++++++++++ .../api/v2/resources/ResourceLinksMock.java | 1 + 5 files changed, 61 insertions(+), 10 deletions(-) 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 b7a7415853..b0e39c197a 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 @@ -1,6 +1,5 @@ package sonia.scm.api.v2.resources; -import de.otto.edison.hal.Link; import de.otto.edison.hal.Links; import org.apache.shiro.SecurityUtils; import sonia.scm.config.ConfigurationPermissions; @@ -9,6 +8,8 @@ import sonia.scm.user.UserPermissions; import javax.inject.Inject; +import static de.otto.edison.hal.Link.link; + public class IndexDtoGenerator { private final ResourceLinks resourceLinks; @@ -20,25 +21,27 @@ public class IndexDtoGenerator { public IndexDto generate() { Links.Builder builder = Links.linkingTo(); + builder.self(resourceLinks.index().self()); + builder.single(link("uiPlugins", resourceLinks.uiPluginCollection().self())); if (SecurityUtils.getSubject().isAuthenticated()) { builder.single( - Link.link("me", resourceLinks.me().self()), - Link.link("logout", resourceLinks.authentication().logout()) + link("me", resourceLinks.me().self()), + link("logout", resourceLinks.authentication().logout()) ); if (UserPermissions.list().isPermitted()) { - builder.single(Link.link("users", resourceLinks.userCollection().self())); + builder.single(link("users", resourceLinks.userCollection().self())); } if (GroupPermissions.list().isPermitted()) { - builder.single(Link.link("groups", resourceLinks.groupCollection().self())); + builder.single(link("groups", resourceLinks.groupCollection().self())); } if (ConfigurationPermissions.list().isPermitted()) { - builder.single(Link.link("config", resourceLinks.config().self())); + builder.single(link("config", resourceLinks.config().self())); } - builder.single(Link.link("repositories", resourceLinks.repositoryCollection().self())); + builder.single(link("repositories", resourceLinks.repositoryCollection().self())); } else { builder.single( - Link.link("formLogin", resourceLinks.authentication().formLogin()), - Link.link("jsonLogin", resourceLinks.authentication().jsonLogin()) + link("formLogin", resourceLinks.authentication().formLogin()), + link("jsonLogin", resourceLinks.authentication().jsonLogin()) ); } diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexResource.java index f9aa568251..088558c7dc 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexResource.java @@ -20,6 +20,7 @@ public class IndexResource { } @GET + @Path("") @Produces(VndMediaType.INDEX) @TypeHint(IndexDto.class) public IndexDto getIndex() { 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 02d7a3d53d..488577618c 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 @@ -440,7 +440,6 @@ class ResourceLinks { } } - public UIPluginLinks uiPlugin() { return new UIPluginLinks(scmPathInfoStore.get()); } @@ -496,4 +495,21 @@ class ResourceLinks { return loginLinkBuilder.method("logout").parameters().href(); } } + + public IndexLinks index() { + return new IndexLinks(scmPathInfoStore.get()); + } + + static class IndexLinks { + private final LinkBuilder indexLinkBuilder; + + IndexLinks(ScmPathInfo pathInfo) { + indexLinkBuilder = new LinkBuilder(pathInfo, IndexResource.class); + } + + String self() { + return indexLinkBuilder.method("getIndex").parameters().href(); + } + } + } 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 73bff79991..00c6b39313 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 @@ -26,6 +26,36 @@ public class IndexResourceTest { Assertions.assertThat(index.getLinks().getLinkBy("jsonLogin")).matches(Optional::isPresent); } + @Test + public void shouldRenderSelfLinkForUnauthenticatedRequest() { + IndexDto index = indexResource.getIndex(); + + Assertions.assertThat(index.getLinks().getLinkBy("self")).matches(Optional::isPresent); + } + + @Test + public void shouldRenderUiPluginsLinkForUnauthenticatedRequest() { + IndexDto index = indexResource.getIndex(); + + Assertions.assertThat(index.getLinks().getLinkBy("uiPlugins")).matches(Optional::isPresent); + } + + @Test + @SubjectAware(username = "trillian", password = "secret") + public void shouldRenderSelfLinkForAuthenticatedRequest() { + IndexDto index = indexResource.getIndex(); + + Assertions.assertThat(index.getLinks().getLinkBy("self")).matches(Optional::isPresent); + } + + @Test + @SubjectAware(username = "trillian", password = "secret") + public void shouldRenderUiPluginsLinkForAuthenticatedRequest() { + IndexDto index = indexResource.getIndex(); + + Assertions.assertThat(index.getLinks().getLinkBy("uiPlugins")).matches(Optional::isPresent); + } + @Test @SubjectAware(username = "trillian", password = "secret") public void shouldRenderMeUrlForAuthenticatedRequest() { 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 2e2c1ae586..eed46a1f6e 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 @@ -35,6 +35,7 @@ public class ResourceLinksMock { when(resourceLinks.uiPluginCollection()).thenReturn(new ResourceLinks.UIPluginCollectionLinks(uriInfo)); when(resourceLinks.uiPlugin()).thenReturn(new ResourceLinks.UIPluginLinks(uriInfo)); when(resourceLinks.authentication()).thenReturn(new ResourceLinks.AuthenticationLinks(uriInfo)); + when(resourceLinks.index()).thenReturn(new ResourceLinks.IndexLinks(uriInfo)); return resourceLinks; } From d4237b1fe9efe49967c69d6e4a0e4fcc403a0285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Mon, 1 Oct 2018 13:02:34 +0200 Subject: [PATCH 14/21] Generate only one login link --- .../java/sonia/scm/api/v2/resources/IndexDtoGenerator.java | 5 +---- .../java/sonia/scm/api/v2/resources/IndexResourceTest.java | 3 +-- 2 files changed, 2 insertions(+), 6 deletions(-) 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 b0e39c197a..8ed193348c 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 @@ -39,10 +39,7 @@ public class IndexDtoGenerator { } builder.single(link("repositories", resourceLinks.repositoryCollection().self())); } else { - builder.single( - link("formLogin", resourceLinks.authentication().formLogin()), - link("jsonLogin", resourceLinks.authentication().jsonLogin()) - ); + builder.single(link("login", resourceLinks.authentication().jsonLogin())); } return new IndexDto(builder.build()); 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 00c6b39313..6bdd44e791 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 @@ -22,8 +22,7 @@ public class IndexResourceTest { public void shouldRenderLoginUrlsForUnauthenticatedRequest() { IndexDto index = indexResource.getIndex(); - Assertions.assertThat(index.getLinks().getLinkBy("formLogin")).matches(Optional::isPresent); - Assertions.assertThat(index.getLinks().getLinkBy("jsonLogin")).matches(Optional::isPresent); + Assertions.assertThat(index.getLinks().getLinkBy("login")).matches(Optional::isPresent); } @Test From 67d68a5bc2dc7873903849296d7642e104562545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Mon, 1 Oct 2018 13:06:59 +0200 Subject: [PATCH 15/21] Add version attribute --- .../sonia/scm/api/v2/resources/IndexDto.java | 7 ++++++- .../scm/api/v2/resources/IndexDtoGenerator.java | 7 +++++-- .../scm/api/v2/resources/IndexResourceTest.java | 16 +++++++++++++++- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDto.java index 699ba1ea83..9346420f58 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDto.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/IndexDto.java @@ -2,10 +2,15 @@ package sonia.scm.api.v2.resources; import de.otto.edison.hal.HalRepresentation; import de.otto.edison.hal.Links; +import lombok.Getter; +@Getter public class IndexDto extends HalRepresentation { - IndexDto(Links links) { + private final String version; + + IndexDto(String version, Links links) { super(links); + this.version = version; } } 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 8ed193348c..c8159f072c 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 @@ -2,6 +2,7 @@ package sonia.scm.api.v2.resources; import de.otto.edison.hal.Links; import org.apache.shiro.SecurityUtils; +import sonia.scm.SCMContextProvider; import sonia.scm.config.ConfigurationPermissions; import sonia.scm.group.GroupPermissions; import sonia.scm.user.UserPermissions; @@ -13,10 +14,12 @@ import static de.otto.edison.hal.Link.link; public class IndexDtoGenerator { private final ResourceLinks resourceLinks; + private final SCMContextProvider scmContextProvider; @Inject - public IndexDtoGenerator(ResourceLinks resourceLinks) { + public IndexDtoGenerator(ResourceLinks resourceLinks, SCMContextProvider scmContextProvider) { this.resourceLinks = resourceLinks; + this.scmContextProvider = scmContextProvider; } public IndexDto generate() { @@ -42,6 +45,6 @@ public class IndexDtoGenerator { builder.single(link("login", resourceLinks.authentication().jsonLogin())); } - return new IndexDto(builder.build()); + return new IndexDto(scmContextProvider.getVersion(), builder.build()); } } 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 6bdd44e791..83697a3c4a 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 @@ -5,17 +5,22 @@ import com.github.sdorra.shiro.SubjectAware; import org.assertj.core.api.Assertions; import org.junit.Rule; import org.junit.Test; +import sonia.scm.SCMContextProvider; import java.net.URI; import java.util.Optional; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + @SubjectAware(configuration = "classpath:sonia/scm/shiro-001.ini") public class IndexResourceTest { @Rule public final ShiroRule shiroRule = new ShiroRule(); - private final IndexDtoGenerator indexDtoGenerator = new IndexDtoGenerator(ResourceLinksMock.createMock(URI.create("/"))); + private final SCMContextProvider scmContextProvider = mock(SCMContextProvider.class); + private final IndexDtoGenerator indexDtoGenerator = new IndexDtoGenerator(ResourceLinksMock.createMock(URI.create("/")), scmContextProvider); private final IndexResource indexResource = new IndexResource(indexDtoGenerator); @Test @@ -98,4 +103,13 @@ public class IndexResourceTest { Assertions.assertThat(index.getLinks().getLinkBy("groups")).matches(Optional::isPresent); Assertions.assertThat(index.getLinks().getLinkBy("config")).matches(Optional::isPresent); } + + @Test + public void shouldGenerateVersion() { + when(scmContextProvider.getVersion()).thenReturn("v1"); + + IndexDto index = indexResource.getIndex(); + + Assertions.assertThat(index.getVersion()).isEqualTo("v1"); + } } From 740abcca47eb76530ede2afd621edcf0194f7062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Mon, 1 Oct 2018 13:39:21 +0200 Subject: [PATCH 16/21] Fix integration test --- scm-it/src/test/java/sonia/scm/it/IndexITCase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scm-it/src/test/java/sonia/scm/it/IndexITCase.java b/scm-it/src/test/java/sonia/scm/it/IndexITCase.java index 67090686f4..4a621d962f 100644 --- a/scm-it/src/test/java/sonia/scm/it/IndexITCase.java +++ b/scm-it/src/test/java/sonia/scm/it/IndexITCase.java @@ -41,7 +41,7 @@ public class IndexITCase { .then() .statusCode(HttpStatus.SC_OK) .body( - "_links.formLogin.href", matchesPattern(".+/auth/.+") + "_links.login.href", matchesPattern(".+/auth/.+") ); } From d14d029919f1624ee2d094220008fc28bcced1bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Mon, 1 Oct 2018 15:02:23 +0200 Subject: [PATCH 17/21] Fix peer review issues --- scm-core/src/main/java/sonia/scm/web/JsonEnricherBase.java | 4 ++-- .../scm/api/v2/resources/GitConfigInIndexResourceTest.java | 6 +++--- .../sonia/scm/api/v2/resources/HgConfigInIndexResource.java | 4 ++-- .../scm/api/v2/resources/HgConfigInIndexResourceTest.java | 6 +++--- .../scm/api/v2/resources/SvnConfigInIndexResource.java | 6 +++--- .../scm/api/v2/resources/SvnConfigInIndexResourceTest.java | 6 +++--- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/web/JsonEnricherBase.java b/scm-core/src/main/java/sonia/scm/web/JsonEnricherBase.java index b56e67bb10..71fa962964 100644 --- a/scm-core/src/main/java/sonia/scm/web/JsonEnricherBase.java +++ b/scm-core/src/main/java/sonia/scm/web/JsonEnricherBase.java @@ -18,8 +18,8 @@ public abstract class JsonEnricherBase implements JsonEnricher { return mediaType.equals(context.getResponseMediaType().toString()); } - protected JsonNode value(Object gitConfigUrl) { - return objectMapper.convertValue(gitConfigUrl, JsonNode.class); + protected JsonNode value(Object object) { + return objectMapper.convertValue(object, JsonNode.class); } protected ObjectNode createObject() { diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigInIndexResourceTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigInIndexResourceTest.java index 3151d539bf..665be19788 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigInIndexResourceTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/api/v2/resources/GitConfigInIndexResourceTest.java @@ -14,6 +14,7 @@ import javax.ws.rs.core.MediaType; import java.net.URI; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; @SubjectAware(configuration = "classpath:sonia/scm/configuration/shiro.ini") public class GitConfigInIndexResourceTest { @@ -39,7 +40,6 @@ public class GitConfigInIndexResourceTest { gitConfigInIndexResource.enrich(context); - System.out.println(root); assertEquals("/v2/config/git", root.get("_links").get("gitConfig").get("href").asText()); } @@ -50,7 +50,7 @@ public class GitConfigInIndexResourceTest { gitConfigInIndexResource.enrich(context); - System.out.println(root); + assertFalse(root.get("_links").iterator().hasNext()); } @Test @@ -59,6 +59,6 @@ public class GitConfigInIndexResourceTest { gitConfigInIndexResource.enrich(context); - System.out.println(root); + assertFalse(root.get("_links").iterator().hasNext()); } } diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInIndexResource.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInIndexResource.java index 0507987a26..3de79b2f81 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInIndexResource.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/api/v2/resources/HgConfigInIndexResource.java @@ -27,12 +27,12 @@ public class HgConfigInIndexResource extends JsonEnricherBase { @Override public void enrich(JsonEnricherContext context) { if (resultHasMediaType(INDEX, context) && ConfigurationPermissions.list().isPermitted()) { - String gitConfigUrl = new LinkBuilder(scmPathInfoStore.get().get(), HgConfigResource.class) + String hgConfigUrl = new LinkBuilder(scmPathInfoStore.get().get(), HgConfigResource.class) .method("get") .parameters() .href(); - JsonNode hgConfigRefNode = createObject(singletonMap("href", value(gitConfigUrl))); + JsonNode hgConfigRefNode = createObject(singletonMap("href", value(hgConfigUrl))); addPropertyNode(context.getResponseEntity().get("_links"), "hgConfig", hgConfigRefNode); } diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInIndexResourceTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInIndexResourceTest.java index a2b500e9f6..27ab74932c 100644 --- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInIndexResourceTest.java +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/api/v2/resources/HgConfigInIndexResourceTest.java @@ -14,6 +14,7 @@ import javax.ws.rs.core.MediaType; import java.net.URI; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; @SubjectAware(configuration = "classpath:sonia/scm/configuration/shiro.ini") public class HgConfigInIndexResourceTest { @@ -39,7 +40,6 @@ public class HgConfigInIndexResourceTest { hgConfigInIndexResource.enrich(context); - System.out.println(root); assertEquals("/v2/config/hg", root.get("_links").get("hgConfig").get("href").asText()); } @@ -50,7 +50,7 @@ public class HgConfigInIndexResourceTest { hgConfigInIndexResource.enrich(context); - System.out.println(root); + assertFalse(root.get("_links").iterator().hasNext()); } @Test @@ -59,6 +59,6 @@ public class HgConfigInIndexResourceTest { hgConfigInIndexResource.enrich(context); - System.out.println(root); + assertFalse(root.get("_links").iterator().hasNext()); } } diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/v2/resources/SvnConfigInIndexResource.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/v2/resources/SvnConfigInIndexResource.java index 5f9e4837b1..5ee1de3169 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/v2/resources/SvnConfigInIndexResource.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/api/v2/resources/SvnConfigInIndexResource.java @@ -27,14 +27,14 @@ public class SvnConfigInIndexResource extends JsonEnricherBase { @Override public void enrich(JsonEnricherContext context) { if (resultHasMediaType(INDEX, context) && ConfigurationPermissions.list().isPermitted()) { - String gitConfigUrl = new LinkBuilder(scmPathInfoStore.get().get(), SvnConfigResource.class) + String svnConfigUrl = new LinkBuilder(scmPathInfoStore.get().get(), SvnConfigResource.class) .method("get") .parameters() .href(); - JsonNode gitConfigRefNode = createObject(singletonMap("href", value(gitConfigUrl))); + JsonNode svnConfigRefNode = createObject(singletonMap("href", value(svnConfigUrl))); - addPropertyNode(context.getResponseEntity().get("_links"), "svnConfig", gitConfigRefNode); + addPropertyNode(context.getResponseEntity().get("_links"), "svnConfig", svnConfigRefNode); } } } diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigInIndexResourceTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigInIndexResourceTest.java index 762d024125..8b87b57c6c 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigInIndexResourceTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/api/v2/resources/SvnConfigInIndexResourceTest.java @@ -14,6 +14,7 @@ import javax.ws.rs.core.MediaType; import java.net.URI; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; @SubjectAware(configuration = "classpath:sonia/scm/configuration/shiro.ini") public class SvnConfigInIndexResourceTest { @@ -39,7 +40,6 @@ public class SvnConfigInIndexResourceTest { svnConfigInIndexResource.enrich(context); - System.out.println(root); assertEquals("/v2/config/svn", root.get("_links").get("svnConfig").get("href").asText()); } @@ -50,7 +50,7 @@ public class SvnConfigInIndexResourceTest { svnConfigInIndexResource.enrich(context); - System.out.println(root); + assertFalse(root.get("_links").iterator().hasNext()); } @Test @@ -59,6 +59,6 @@ public class SvnConfigInIndexResourceTest { svnConfigInIndexResource.enrich(context); - System.out.println(root); + assertFalse(root.get("_links").iterator().hasNext()); } } From 1cc5368db4943d14dde96efd92755f5b6055f9df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Mon, 1 Oct 2018 16:16:44 +0200 Subject: [PATCH 18/21] Use generated string for shiro permission --- pom.xml | 7 +++---- scm-core/pom.xml | 5 ++--- .../sonia/scm/security/DefaultAuthorizationCollector.java | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 944ebb6eb6..df49023265 100644 --- a/pom.xml +++ b/pom.xml @@ -188,15 +188,14 @@ test - - com.github.sdorra.shiro-static-permissions + com.github.sdorra ssp-lib ${ssp.version} - com.github.sdorra.shiro-static-permissions + com.github.sdorra ssp-processor ${ssp.version} true @@ -765,7 +764,7 @@ 9.2.10.v20150310 - 967c8fd521 + 1.1.0 1.4.0 diff --git a/scm-core/pom.xml b/scm-core/pom.xml index a3fa037c7b..a2c9e7128e 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -160,14 +160,13 @@ provided - - com.github.sdorra.shiro-static-permissions + com.github.sdorra ssp-lib - com.github.sdorra.shiro-static-permissions + com.github.sdorra ssp-processor true diff --git a/scm-webapp/src/main/java/sonia/scm/security/DefaultAuthorizationCollector.java b/scm-webapp/src/main/java/sonia/scm/security/DefaultAuthorizationCollector.java index cd22e22712..bf46eb2a6f 100644 --- a/scm-webapp/src/main/java/sonia/scm/security/DefaultAuthorizationCollector.java +++ b/scm-webapp/src/main/java/sonia/scm/security/DefaultAuthorizationCollector.java @@ -265,7 +265,7 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector } private String canReadOwnUser(User user) { - return "user:" + UserPermissions.ACTION_READ + ":" + user.getName(); + return UserPermissions.read(user.getName()).asShiroString(); } //~--- get methods ---------------------------------------------------------- From de0247e60faf20f200074b22d2ac14068c674427 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 2 Oct 2018 07:45:28 +0200 Subject: [PATCH 19/21] use set, because put is deprecated --- scm-core/src/main/java/sonia/scm/web/JsonEnricherBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scm-core/src/main/java/sonia/scm/web/JsonEnricherBase.java b/scm-core/src/main/java/sonia/scm/web/JsonEnricherBase.java index 71fa962964..1baecb62af 100644 --- a/scm-core/src/main/java/sonia/scm/web/JsonEnricherBase.java +++ b/scm-core/src/main/java/sonia/scm/web/JsonEnricherBase.java @@ -35,6 +35,6 @@ public abstract class JsonEnricherBase implements JsonEnricher { } protected void addPropertyNode(JsonNode parent, String newKey, JsonNode child) { - ((ObjectNode) parent).put(newKey, child); + ((ObjectNode) parent).set(newKey, child); } } From fe93d8baa6460f2ed4d844bd2353e18ad4128fab Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 2 Oct 2018 08:04:39 +0200 Subject: [PATCH 20/21] added unit tests for JsonEnricher --- scm-core/pom.xml | 6 +++ .../sonia/scm/web/JsonEnricherBaseTest.java | 51 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 scm-core/src/test/java/sonia/scm/web/JsonEnricherBaseTest.java diff --git a/scm-core/pom.xml b/scm-core/pom.xml index a3fa037c7b..761a784258 100644 --- a/scm-core/pom.xml +++ b/scm-core/pom.xml @@ -94,6 +94,12 @@ javax.ws.rs-api + + org.jboss.resteasy + resteasy-jaxrs + test + + diff --git a/scm-core/src/test/java/sonia/scm/web/JsonEnricherBaseTest.java b/scm-core/src/test/java/sonia/scm/web/JsonEnricherBaseTest.java new file mode 100644 index 0000000000..43ed4940fa --- /dev/null +++ b/scm-core/src/test/java/sonia/scm/web/JsonEnricherBaseTest.java @@ -0,0 +1,51 @@ +package sonia.scm.web; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.junit.Test; + +import javax.ws.rs.core.MediaType; + +import static java.util.Collections.singletonMap; +import static org.assertj.core.api.Assertions.assertThat; + +public class JsonEnricherBaseTest { + + private ObjectMapper objectMapper = new ObjectMapper(); + private TestJsonEnricher enricher = new TestJsonEnricher(objectMapper); + + @Test + public void testResultHasMediaType() { + JsonEnricherContext context = new JsonEnricherContext(null, MediaType.APPLICATION_JSON_TYPE, null); + + assertThat(enricher.resultHasMediaType(MediaType.APPLICATION_JSON, context)).isTrue(); + assertThat(enricher.resultHasMediaType(MediaType.APPLICATION_XML, context)).isFalse(); + } + + @Test + public void testAppendLink() { + ObjectNode root = objectMapper.createObjectNode(); + ObjectNode links = objectMapper.createObjectNode(); + root.set("_links", links); + JsonEnricherContext context = new JsonEnricherContext(null, MediaType.APPLICATION_JSON_TYPE, root); + enricher.enrich(context); + + assertThat(links.get("awesome").get("href").asText()).isEqualTo("/my/awesome/link"); + } + + private static class TestJsonEnricher extends JsonEnricherBase { + + public TestJsonEnricher(ObjectMapper objectMapper) { + super(objectMapper); + } + + @Override + public void enrich(JsonEnricherContext context) { + JsonNode gitConfigRefNode = createObject(singletonMap("href", value("/my/awesome/link"))); + + addPropertyNode(context.getResponseEntity().get("_links"), "awesome", gitConfigRefNode); + } + } + +} From fb08676fcddace91cc80e1fc0879c5fc7f3bf199 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Tue, 2 Oct 2018 06:33:07 +0000 Subject: [PATCH 21/21] Close branch feature/index_endpoint_v2