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 0836f177ee..19859b876b 100644 --- a/scm-core/src/main/java/sonia/scm/web/VndMediaType.java +++ b/scm-core/src/main/java/sonia/scm/web/VndMediaType.java @@ -33,6 +33,7 @@ public class VndMediaType { public static final String REPOSITORY_COLLECTION = PREFIX + "repositoryCollection" + SUFFIX; public static final String BRANCH_COLLECTION = PREFIX + "branchCollection" + SUFFIX; public static final String CONFIG = PREFIX + "config" + SUFFIX; + public static final String REPOSITORY_PERMISSION_COLLECTION = PREFIX + "repositoryPermissionCollection" + SUFFIX; public static final String REPOSITORY_TYPE_COLLECTION = PREFIX + "repositoryTypeCollection" + SUFFIX; public static final String REPOSITORY_TYPE = PREFIX + "repositoryType" + SUFFIX; public static final String UI_PLUGIN = PREFIX + "uiPlugin" + SUFFIX; diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailableRepositoryPermissionsDto.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailableRepositoryPermissionsDto.java new file mode 100644 index 0000000000..60203b565b --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/AvailableRepositoryPermissionsDto.java @@ -0,0 +1,31 @@ +package sonia.scm.api.v2.resources; + +import de.otto.edison.hal.HalRepresentation; +import de.otto.edison.hal.Links; +import sonia.scm.security.RepositoryRole; + +import java.util.Collection; + +public class AvailableRepositoryPermissionsDto extends HalRepresentation { + private final Collection availableVerbs; + private final Collection availableRoles; + + public AvailableRepositoryPermissionsDto(Collection availableVerbs, Collection availableRoles) { + this.availableVerbs = availableVerbs; + this.availableRoles = availableRoles; + } + + public Collection getAvailableVerbs() { + return availableVerbs; + } + + public Collection getAvailableRoles() { + return availableRoles; + } + + @Override + @SuppressWarnings("squid:S1185") // We want to have this method available in this package + protected HalRepresentation add(Links links) { + return super.add(links); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryPermissionResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryPermissionResource.java index 2cfbf30de6..e5734085ca 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryPermissionResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryPermissionResource.java @@ -2,6 +2,7 @@ package sonia.scm.api.v2.resources; import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.StatusCodes; +import de.otto.edison.hal.Links; import sonia.scm.security.RepositoryPermissionProvider; import sonia.scm.web.VndMediaType; @@ -9,7 +10,6 @@ import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; -import java.util.Collection; /** * RESTful Web Service Resource to get available repository types. @@ -20,31 +20,24 @@ public class RepositoryPermissionResource { static final String PATH = "v2/repositoryPermissions/"; private final RepositoryPermissionProvider repositoryPermissionProvider; + private final ResourceLinks resourceLinks; @Inject - public RepositoryPermissionResource(RepositoryPermissionProvider repositoryPermissionProvider) { + public RepositoryPermissionResource(RepositoryPermissionProvider repositoryPermissionProvider, ResourceLinks resourceLinks) { this.repositoryPermissionProvider = repositoryPermissionProvider; + this.resourceLinks = resourceLinks; } @GET - @Path("verbs") + @Path("") @StatusCodes({ @ResponseCode(code = 200, condition = "success"), @ResponseCode(code = 500, condition = "internal server error") }) - @Produces(VndMediaType.REPOSITORY_TYPE_COLLECTION) - public Collection getRepositoryPermissionVerbs() { - return repositoryPermissionProvider.availableVerbs(); - } - - @GET - @Path("roles") - @StatusCodes({ - @ResponseCode(code = 200, condition = "success"), - @ResponseCode(code = 500, condition = "internal server error") - }) - @Produces(VndMediaType.REPOSITORY_TYPE_COLLECTION) - public Collection getRepositoryRoles() { - return repositoryPermissionProvider.availableRoles(); + @Produces(VndMediaType.REPOSITORY_PERMISSION_COLLECTION) + public AvailableRepositoryPermissionsDto get() { + AvailableRepositoryPermissionsDto dto = new AvailableRepositoryPermissionsDto(repositoryPermissionProvider.availableVerbs(), repositoryPermissionProvider.availableRoles()); + dto.add(Links.linkingTo().self(resourceLinks.availableRepositoryPermissions().self()).build()); + return dto; } } 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 c7369f7cd0..644b3adc3f 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 @@ -639,14 +639,30 @@ class ResourceLinks { } static class PermissionsLinks { - private final LinkBuilder permissionsLlinkBuilder; + private final LinkBuilder permissionsLinkBuilder; PermissionsLinks(ScmPathInfo scmPathInfo) { - this.permissionsLlinkBuilder = new LinkBuilder(scmPathInfo, GlobalPermissionResource.class); + this.permissionsLinkBuilder = new LinkBuilder(scmPathInfo, GlobalPermissionResource.class); } String self() { - return permissionsLlinkBuilder.method("getAll").parameters().href(); + return permissionsLinkBuilder.method("getAll").parameters().href(); + } + } + + public AvailableRepositoryPermissionLinks availableRepositoryPermissions() { + return new AvailableRepositoryPermissionLinks(scmPathInfoStore.get()); + } + + static class AvailableRepositoryPermissionLinks { + private final LinkBuilder linkBuilder; + + AvailableRepositoryPermissionLinks(ScmPathInfo scmPathInfo) { + this.linkBuilder = new LinkBuilder(scmPathInfo, RepositoryPermissionResource.class); + } + + String self() { + return linkBuilder.method("get").parameters().href(); } } } diff --git a/scm-webapp/src/main/resources/META-INF/scm/repository-permissions.xml b/scm-webapp/src/main/resources/META-INF/scm/repository-permissions.xml index 14412847a6..acbe0a8a82 100644 --- a/scm-webapp/src/main/resources/META-INF/scm/repository-permissions.xml +++ b/scm-webapp/src/main/resources/META-INF/scm/repository-permissions.xml @@ -9,6 +9,7 @@ push permissionRead permissionWrite + * 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 655d00fc10..8714496e1a 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 @@ -42,6 +42,7 @@ public class ResourceLinksMock { when(resourceLinks.index()).thenReturn(new ResourceLinks.IndexLinks(uriInfo)); when(resourceLinks.merge()).thenReturn(new ResourceLinks.MergeLinks(uriInfo)); when(resourceLinks.permissions()).thenReturn(new ResourceLinks.PermissionsLinks(uriInfo)); + when(resourceLinks.availableRepositoryPermissions()).thenReturn(new ResourceLinks.AvailableRepositoryPermissionLinks(uriInfo)); return resourceLinks; } diff --git a/scm-webapp/src/test/java/sonia/scm/security/RepositoryPermissionProviderTest.java b/scm-webapp/src/test/java/sonia/scm/security/RepositoryPermissionProviderTest.java index 487d8b71c8..b5998084f8 100644 --- a/scm-webapp/src/test/java/sonia/scm/security/RepositoryPermissionProviderTest.java +++ b/scm-webapp/src/test/java/sonia/scm/security/RepositoryPermissionProviderTest.java @@ -4,7 +4,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import sonia.scm.plugin.PluginLoader; import sonia.scm.repository.RepositoryPermissions; -import sonia.scm.store.ConfigurationEntryStoreFactory; import sonia.scm.util.ClassLoaders; import java.lang.reflect.Field; @@ -25,7 +24,6 @@ class RepositoryPermissionProviderTest { void init() { PluginLoader pluginLoader = mock(PluginLoader.class); when(pluginLoader.getUberClassLoader()).thenReturn(ClassLoaders.getContextClassLoader(DefaultSecuritySystem.class)); - ConfigurationEntryStoreFactory configurationEntryStoreFactory = mock(ConfigurationEntryStoreFactory.class); repositoryPermissionProvider = new RepositoryPermissionProvider(pluginLoader); allVerbsFromRepositoryClass = Arrays.stream(RepositoryPermissions.class.getDeclaredFields()) .filter(field -> field.getName().startsWith("ACTION_")) @@ -37,13 +35,11 @@ class RepositoryPermissionProviderTest { @Test void shouldReadAvailableRoles() { assertThat(repositoryPermissionProvider.availableRoles()).isNotEmpty(); - assertThat(repositoryPermissionProvider.availableRoles()).allSatisfy(this::eitherStarOrOnlyAvailableVerbs); + assertThat(repositoryPermissionProvider.availableRoles()).allSatisfy(this::containsOnlyAvailableVerbs); } - private void eitherStarOrOnlyAvailableVerbs(RepositoryRole role) { - if (!role.getVerbs().contains("*") || role.getVerbs().size() > 1) { - assertThat(role.getVerbs()).isSubsetOf(allVerbsFromRepositoryClass); - } + private void containsOnlyAvailableVerbs(RepositoryRole role) { + assertThat(role.getVerbs()).isSubsetOf(repositoryPermissionProvider.availableVerbs()); } @Test