Better rest

This commit is contained in:
René Pfeuffer
2019-01-23 12:46:08 +01:00
parent 69b64948a0
commit 1bd0bbc7a3
7 changed files with 66 additions and 27 deletions

View File

@@ -33,6 +33,7 @@ public class VndMediaType {
public static final String REPOSITORY_COLLECTION = PREFIX + "repositoryCollection" + SUFFIX; public static final String REPOSITORY_COLLECTION = PREFIX + "repositoryCollection" + SUFFIX;
public static final String BRANCH_COLLECTION = PREFIX + "branchCollection" + SUFFIX; public static final String BRANCH_COLLECTION = PREFIX + "branchCollection" + SUFFIX;
public static final String CONFIG = PREFIX + "config" + 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_COLLECTION = PREFIX + "repositoryTypeCollection" + SUFFIX;
public static final String REPOSITORY_TYPE = PREFIX + "repositoryType" + SUFFIX; public static final String REPOSITORY_TYPE = PREFIX + "repositoryType" + SUFFIX;
public static final String UI_PLUGIN = PREFIX + "uiPlugin" + SUFFIX; public static final String UI_PLUGIN = PREFIX + "uiPlugin" + SUFFIX;

View File

@@ -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<String> availableVerbs;
private final Collection<RepositoryRole> availableRoles;
public AvailableRepositoryPermissionsDto(Collection<String> availableVerbs, Collection<RepositoryRole> availableRoles) {
this.availableVerbs = availableVerbs;
this.availableRoles = availableRoles;
}
public Collection<String> getAvailableVerbs() {
return availableVerbs;
}
public Collection<RepositoryRole> 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);
}
}

View File

@@ -2,6 +2,7 @@ package sonia.scm.api.v2.resources;
import com.webcohesion.enunciate.metadata.rs.ResponseCode; import com.webcohesion.enunciate.metadata.rs.ResponseCode;
import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import de.otto.edison.hal.Links;
import sonia.scm.security.RepositoryPermissionProvider; import sonia.scm.security.RepositoryPermissionProvider;
import sonia.scm.web.VndMediaType; import sonia.scm.web.VndMediaType;
@@ -9,7 +10,6 @@ import javax.inject.Inject;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import java.util.Collection;
/** /**
* RESTful Web Service Resource to get available repository types. * RESTful Web Service Resource to get available repository types.
@@ -20,31 +20,24 @@ public class RepositoryPermissionResource {
static final String PATH = "v2/repositoryPermissions/"; static final String PATH = "v2/repositoryPermissions/";
private final RepositoryPermissionProvider repositoryPermissionProvider; private final RepositoryPermissionProvider repositoryPermissionProvider;
private final ResourceLinks resourceLinks;
@Inject @Inject
public RepositoryPermissionResource(RepositoryPermissionProvider repositoryPermissionProvider) { public RepositoryPermissionResource(RepositoryPermissionProvider repositoryPermissionProvider, ResourceLinks resourceLinks) {
this.repositoryPermissionProvider = repositoryPermissionProvider; this.repositoryPermissionProvider = repositoryPermissionProvider;
this.resourceLinks = resourceLinks;
} }
@GET @GET
@Path("verbs") @Path("")
@StatusCodes({ @StatusCodes({
@ResponseCode(code = 200, condition = "success"), @ResponseCode(code = 200, condition = "success"),
@ResponseCode(code = 500, condition = "internal server error") @ResponseCode(code = 500, condition = "internal server error")
}) })
@Produces(VndMediaType.REPOSITORY_TYPE_COLLECTION) @Produces(VndMediaType.REPOSITORY_PERMISSION_COLLECTION)
public Collection<String> getRepositoryPermissionVerbs() { public AvailableRepositoryPermissionsDto get() {
return repositoryPermissionProvider.availableVerbs(); AvailableRepositoryPermissionsDto dto = new AvailableRepositoryPermissionsDto(repositoryPermissionProvider.availableVerbs(), repositoryPermissionProvider.availableRoles());
} dto.add(Links.linkingTo().self(resourceLinks.availableRepositoryPermissions().self()).build());
return dto;
@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();
} }
} }

View File

@@ -639,14 +639,30 @@ class ResourceLinks {
} }
static class PermissionsLinks { static class PermissionsLinks {
private final LinkBuilder permissionsLlinkBuilder; private final LinkBuilder permissionsLinkBuilder;
PermissionsLinks(ScmPathInfo scmPathInfo) { PermissionsLinks(ScmPathInfo scmPathInfo) {
this.permissionsLlinkBuilder = new LinkBuilder(scmPathInfo, GlobalPermissionResource.class); this.permissionsLinkBuilder = new LinkBuilder(scmPathInfo, GlobalPermissionResource.class);
} }
String self() { 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();
} }
} }
} }

View File

@@ -9,6 +9,7 @@
<verb>push</verb> <verb>push</verb>
<verb>permissionRead</verb> <verb>permissionRead</verb>
<verb>permissionWrite</verb> <verb>permissionWrite</verb>
<verb>*</verb>
</verbs> </verbs>
<roles> <roles>
<role> <role>

View File

@@ -42,6 +42,7 @@ public class ResourceLinksMock {
when(resourceLinks.index()).thenReturn(new ResourceLinks.IndexLinks(uriInfo)); when(resourceLinks.index()).thenReturn(new ResourceLinks.IndexLinks(uriInfo));
when(resourceLinks.merge()).thenReturn(new ResourceLinks.MergeLinks(uriInfo)); when(resourceLinks.merge()).thenReturn(new ResourceLinks.MergeLinks(uriInfo));
when(resourceLinks.permissions()).thenReturn(new ResourceLinks.PermissionsLinks(uriInfo)); when(resourceLinks.permissions()).thenReturn(new ResourceLinks.PermissionsLinks(uriInfo));
when(resourceLinks.availableRepositoryPermissions()).thenReturn(new ResourceLinks.AvailableRepositoryPermissionLinks(uriInfo));
return resourceLinks; return resourceLinks;
} }

View File

@@ -4,7 +4,6 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import sonia.scm.plugin.PluginLoader; import sonia.scm.plugin.PluginLoader;
import sonia.scm.repository.RepositoryPermissions; import sonia.scm.repository.RepositoryPermissions;
import sonia.scm.store.ConfigurationEntryStoreFactory;
import sonia.scm.util.ClassLoaders; import sonia.scm.util.ClassLoaders;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@@ -25,7 +24,6 @@ class RepositoryPermissionProviderTest {
void init() { void init() {
PluginLoader pluginLoader = mock(PluginLoader.class); PluginLoader pluginLoader = mock(PluginLoader.class);
when(pluginLoader.getUberClassLoader()).thenReturn(ClassLoaders.getContextClassLoader(DefaultSecuritySystem.class)); when(pluginLoader.getUberClassLoader()).thenReturn(ClassLoaders.getContextClassLoader(DefaultSecuritySystem.class));
ConfigurationEntryStoreFactory configurationEntryStoreFactory = mock(ConfigurationEntryStoreFactory.class);
repositoryPermissionProvider = new RepositoryPermissionProvider(pluginLoader); repositoryPermissionProvider = new RepositoryPermissionProvider(pluginLoader);
allVerbsFromRepositoryClass = Arrays.stream(RepositoryPermissions.class.getDeclaredFields()) allVerbsFromRepositoryClass = Arrays.stream(RepositoryPermissions.class.getDeclaredFields())
.filter(field -> field.getName().startsWith("ACTION_")) .filter(field -> field.getName().startsWith("ACTION_"))
@@ -37,13 +35,11 @@ class RepositoryPermissionProviderTest {
@Test @Test
void shouldReadAvailableRoles() { void shouldReadAvailableRoles() {
assertThat(repositoryPermissionProvider.availableRoles()).isNotEmpty(); assertThat(repositoryPermissionProvider.availableRoles()).isNotEmpty();
assertThat(repositoryPermissionProvider.availableRoles()).allSatisfy(this::eitherStarOrOnlyAvailableVerbs); assertThat(repositoryPermissionProvider.availableRoles()).allSatisfy(this::containsOnlyAvailableVerbs);
} }
private void eitherStarOrOnlyAvailableVerbs(RepositoryRole role) { private void containsOnlyAvailableVerbs(RepositoryRole role) {
if (!role.getVerbs().contains("*") || role.getVerbs().size() > 1) { assertThat(role.getVerbs()).isSubsetOf(repositoryPermissionProvider.availableVerbs());
assertThat(role.getVerbs()).isSubsetOf(allVerbsFromRepositoryClass);
}
} }
@Test @Test