mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-01-17 13:02:14 +01:00
Add REST endpoint for namespace permissions
This commit is contained in:
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import de.otto.edison.hal.HalRepresentation;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.headers.Header;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import sonia.scm.NotFoundException;
|
||||
import sonia.scm.repository.Namespace;
|
||||
import sonia.scm.repository.NamespaceManager;
|
||||
import sonia.scm.repository.NamespacePermissions;
|
||||
import sonia.scm.repository.RepositoryPermission;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.validation.Valid;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.net.URI;
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static sonia.scm.AlreadyExistsException.alreadyExists;
|
||||
import static sonia.scm.ContextEntry.ContextBuilder.entity;
|
||||
import static sonia.scm.NotFoundException.notFound;
|
||||
import static sonia.scm.api.v2.resources.RepositoryPermissionDto.GROUP_PREFIX;
|
||||
|
||||
@Slf4j
|
||||
public class NamespacePermissionResource {
|
||||
|
||||
private RepositoryPermissionDtoToRepositoryPermissionMapper dtoToModelMapper;
|
||||
private RepositoryPermissionToRepositoryPermissionDtoMapper modelToDtoMapper;
|
||||
private RepositoryPermissionCollectionToDtoMapper repositoryPermissionCollectionToDtoMapper;
|
||||
private ResourceLinks resourceLinks;
|
||||
private final NamespaceManager manager;
|
||||
|
||||
@Inject
|
||||
public NamespacePermissionResource(
|
||||
RepositoryPermissionDtoToRepositoryPermissionMapper dtoToModelMapper,
|
||||
RepositoryPermissionToRepositoryPermissionDtoMapper modelToDtoMapper,
|
||||
RepositoryPermissionCollectionToDtoMapper repositoryPermissionCollectionToDtoMapper,
|
||||
ResourceLinks resourceLinks,
|
||||
NamespaceManager manager) {
|
||||
this.dtoToModelMapper = dtoToModelMapper;
|
||||
this.modelToDtoMapper = modelToDtoMapper;
|
||||
this.repositoryPermissionCollectionToDtoMapper = repositoryPermissionCollectionToDtoMapper;
|
||||
this.resourceLinks = resourceLinks;
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new namespace permission for the user or group
|
||||
*
|
||||
* @param permission permission to add
|
||||
* @return a web response with the status code 201 and the url to GET the added permission
|
||||
*/
|
||||
@POST
|
||||
@Path("")
|
||||
@Consumes(VndMediaType.REPOSITORY_PERMISSION)
|
||||
@Operation(summary = "Create namespace-specific permission", description = "Adds a new permission to the namespace for the user or group.", tags = {"Namespace", "Permissions"})
|
||||
@ApiResponse(
|
||||
responseCode = "201",
|
||||
description = "creates",
|
||||
headers = @Header(
|
||||
name = "Location",
|
||||
description = "uri of the created permission",
|
||||
schema = @Schema(type = "string")
|
||||
)
|
||||
)
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "not found",
|
||||
content = @Content(
|
||||
mediaType = VndMediaType.ERROR_TYPE,
|
||||
schema = @Schema(implementation = ErrorDto.class)
|
||||
))
|
||||
@ApiResponse(responseCode = "409", description = "conflict")
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "internal server error",
|
||||
content = @Content(
|
||||
mediaType = VndMediaType.ERROR_TYPE,
|
||||
schema = @Schema(implementation = ErrorDto.class)
|
||||
)
|
||||
)
|
||||
public Response create(@PathParam("namespace") String namespaceName, @Valid RepositoryPermissionDto permission) {
|
||||
log.info("try to add new permission: {}", permission);
|
||||
Namespace namespace = load(namespaceName);
|
||||
NamespacePermissions.permissionWrite().check();
|
||||
checkPermissionAlreadyExists(permission, namespace);
|
||||
namespace.addPermission(dtoToModelMapper.map(permission));
|
||||
manager.modify(namespace);
|
||||
String urlPermissionName = modelToDtoMapper.getUrlPermissionName(permission);
|
||||
return Response.created(URI.create(resourceLinks.namespacePermission().self(namespaceName, urlPermissionName))).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the searched permission with permission name related to a namespace
|
||||
*
|
||||
* @param namespaceName the name of the namespace
|
||||
* @return the http response with a list of permissionDto objects
|
||||
* @throws NotFoundException if the namespace or the permission does not exists
|
||||
*/
|
||||
@GET
|
||||
@Path("{permission-name}")
|
||||
@Produces(VndMediaType.REPOSITORY_PERMISSION)
|
||||
@Operation(summary = "Get single repository-specific permission", description = "Get the searched permission with permission name related to a repository.", tags = {"Repository", "Permissions"})
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "success",
|
||||
content = @Content(
|
||||
mediaType = VndMediaType.REPOSITORY_PERMISSION,
|
||||
schema = @Schema(implementation = RepositoryPermissionDto.class)
|
||||
)
|
||||
)
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "not found",
|
||||
content = @Content(
|
||||
mediaType = VndMediaType.ERROR_TYPE,
|
||||
schema = @Schema(implementation = ErrorDto.class)
|
||||
))
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "internal server error",
|
||||
content = @Content(
|
||||
mediaType = VndMediaType.ERROR_TYPE,
|
||||
schema = @Schema(implementation = ErrorDto.class)
|
||||
)
|
||||
)
|
||||
public RepositoryPermissionDto get(@PathParam("namespace") String namespaceName, @PathParam("permission-name") String permissionName) {
|
||||
Namespace namespace = load(namespaceName);
|
||||
NamespacePermissions.permissionRead().check();
|
||||
return
|
||||
namespace.getPermissions()
|
||||
.stream()
|
||||
.filter(filterPermission(permissionName))
|
||||
.map(permission -> modelToDtoMapper.map(permission, namespace))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> notFound(entity(RepositoryPermission.class, permissionName).in(Namespace.class, namespaceName)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all permissions related to a namespace
|
||||
*
|
||||
* @param namespaceMame the name of the namespace
|
||||
* @return the http response with a list of permissionDto objects
|
||||
* @throws NotFoundException if the namespace does not exists
|
||||
*/
|
||||
@GET
|
||||
@Path("")
|
||||
@Produces(VndMediaType.REPOSITORY_PERMISSION)
|
||||
@Operation(summary = "List of namespace-specific permissions", description = "Get all permissions related to a namespace.", tags = {"Repository", "Permissions"})
|
||||
@ApiResponse(
|
||||
responseCode = "200",
|
||||
description = "success",
|
||||
content = @Content(
|
||||
mediaType = VndMediaType.REPOSITORY_PERMISSION,
|
||||
schema = @Schema(implementation = RepositoryPermissionDto.class)
|
||||
)
|
||||
)
|
||||
@ApiResponse(
|
||||
responseCode = "404",
|
||||
description = "not found",
|
||||
content = @Content(
|
||||
mediaType = VndMediaType.ERROR_TYPE,
|
||||
schema = @Schema(implementation = ErrorDto.class)
|
||||
))
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "internal server error",
|
||||
content = @Content(
|
||||
mediaType = VndMediaType.ERROR_TYPE,
|
||||
schema = @Schema(implementation = ErrorDto.class)
|
||||
)
|
||||
)
|
||||
public HalRepresentation getAll(@PathParam("namespace") String namespaceMame) {
|
||||
Namespace namespace = load(namespaceMame);
|
||||
NamespacePermissions.permissionRead().check();
|
||||
return repositoryPermissionCollectionToDtoMapper.map(namespace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a permission to the user or group managed by the repository
|
||||
* ignore the user input for groupPermission and take it from the path parameter (if the group prefix (@) exists it is a group permission)
|
||||
*
|
||||
* @param permission permission to modify
|
||||
* @param permissionName permission to modify
|
||||
* @return a web response with the status code 204
|
||||
*/
|
||||
@PUT
|
||||
@Path("{permission-name}")
|
||||
@Consumes(VndMediaType.REPOSITORY_PERMISSION)
|
||||
@Operation(summary = "Update repository-specific permission", description = "Update a permission to the user or group managed by the repository.", tags = {"Repository", "Permissions"})
|
||||
@ApiResponse(responseCode = "204", description = "update success")
|
||||
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "internal server error",
|
||||
content = @Content(
|
||||
mediaType = VndMediaType.ERROR_TYPE,
|
||||
schema = @Schema(implementation = ErrorDto.class)
|
||||
)
|
||||
)
|
||||
public void update(@PathParam("namespace") String namespaceName,
|
||||
@PathParam("permission-name") String permissionName,
|
||||
@Valid RepositoryPermissionDto permission) {
|
||||
log.info("try to update the permission with name: {}. the modified permission is: {}", permissionName, permission);
|
||||
Namespace namespace = load(namespaceName);
|
||||
NamespacePermissions.permissionWrite().check();
|
||||
String extractedPermissionName = getPermissionName(permissionName);
|
||||
if (!isPermissionExist(new RepositoryPermissionDto(extractedPermissionName, isGroupPermission(permissionName)), namespace)) {
|
||||
throw notFound(entity(RepositoryPermission.class, permissionName).in(Namespace.class, namespaceName));
|
||||
}
|
||||
permission.setGroupPermission(isGroupPermission(permissionName));
|
||||
if (!extractedPermissionName.equals(permission.getName())) {
|
||||
checkPermissionAlreadyExists(permission, namespace);
|
||||
}
|
||||
|
||||
RepositoryPermission existingPermission = namespace.getPermissions()
|
||||
.stream()
|
||||
.filter(filterPermission(permissionName))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> notFound(entity(RepositoryPermission.class, permissionName).in(Namespace.class, namespaceName)));
|
||||
RepositoryPermission newPermission = dtoToModelMapper.map(permission);
|
||||
if (!namespace.removePermission(existingPermission)) {
|
||||
throw new IllegalStateException(String.format("could not delete modified permission %s from namespace %s", existingPermission, namespaceName));
|
||||
}
|
||||
namespace.addPermission(newPermission);
|
||||
manager.modify(namespace);
|
||||
log.info("the permission with name: {} is updated.", permissionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a permission to the user or group managed by the repository
|
||||
*
|
||||
* @param permissionName permission to delete
|
||||
* @return a web response with the status code 204
|
||||
*/
|
||||
@DELETE
|
||||
@Path("{permission-name}")
|
||||
@Operation(summary = "Delete repository-specific permission", description = "Delete a permission with the given name.", tags = {"Repository", "Permissions"})
|
||||
@ApiResponse(responseCode = "204", description = "delete success or nothing to delete")
|
||||
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||
@ApiResponse(responseCode = "403", description = "not authorized")
|
||||
@ApiResponse(
|
||||
responseCode = "500",
|
||||
description = "internal server error",
|
||||
content = @Content(
|
||||
mediaType = VndMediaType.ERROR_TYPE,
|
||||
schema = @Schema(implementation = ErrorDto.class)
|
||||
)
|
||||
)
|
||||
public void delete(@PathParam("namespace") String namespaceName,
|
||||
@PathParam("permission-name") String permissionName) {
|
||||
log.info("try to delete the permission with name: {}.", permissionName);
|
||||
Namespace namespace = load(namespaceName);
|
||||
NamespacePermissions.permissionWrite().check();
|
||||
namespace.getPermissions()
|
||||
.stream()
|
||||
.filter(filterPermission(permissionName))
|
||||
.findFirst()
|
||||
.ifPresent(permission -> {
|
||||
namespace.removePermission(permission);
|
||||
manager.modify(namespace);
|
||||
});
|
||||
log.info("the permission with name: {} is deleted.", permissionName);
|
||||
}
|
||||
|
||||
private Predicate<RepositoryPermission> filterPermission(String name) {
|
||||
return permission -> getPermissionName(name).equals(permission.getName())
|
||||
&&
|
||||
permission.isGroupPermission() == isGroupPermission(name);
|
||||
}
|
||||
|
||||
private String getPermissionName(String permissionName) {
|
||||
return Optional.of(permissionName)
|
||||
.filter(p -> !isGroupPermission(permissionName))
|
||||
.orElse(permissionName.substring(1));
|
||||
}
|
||||
|
||||
private boolean isGroupPermission(String permissionName) {
|
||||
return permissionName.startsWith(GROUP_PREFIX);
|
||||
}
|
||||
|
||||
private Namespace load(String namespaceMame) {
|
||||
return manager.get(namespaceMame)
|
||||
.orElseThrow(() -> notFound(entity("Namespace", namespaceMame)));
|
||||
}
|
||||
|
||||
private void checkPermissionAlreadyExists(RepositoryPermissionDto permission, Namespace namespace) {
|
||||
if (isPermissionExist(permission, namespace)) {
|
||||
throw alreadyExists(entity("Permission", permission.getName()).in(Namespace.class, namespace.getNamespace()));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPermissionExist(RepositoryPermissionDto permission, Namespace namespace) {
|
||||
return namespace.getPermissions()
|
||||
.stream()
|
||||
.anyMatch(p -> p.getName().equals(permission.getName()) && p.isGroupPermission() == permission.isGroupPermission());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.web.VndMediaType;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
@@ -44,11 +45,13 @@ public class NamespaceResource {
|
||||
|
||||
private final RepositoryManager manager;
|
||||
private final NamespaceToNamespaceDtoMapper namespaceMapper;
|
||||
private final Provider<NamespacePermissionResource> namespacePermissionResource;
|
||||
|
||||
@Inject
|
||||
public NamespaceResource(RepositoryManager manager, NamespaceToNamespaceDtoMapper namespaceMapper) {
|
||||
public NamespaceResource(RepositoryManager manager, NamespaceToNamespaceDtoMapper namespaceMapper, Provider<NamespacePermissionResource> namespacePermissionResource) {
|
||||
this.manager = manager;
|
||||
this.namespaceMapper = namespaceMapper;
|
||||
this.namespacePermissionResource = namespacePermissionResource;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,4 +100,8 @@ public class NamespaceResource {
|
||||
.orElseThrow(() -> notFound(entity("Namespace", namespace)));
|
||||
}
|
||||
|
||||
@Path("permissions")
|
||||
public NamespacePermissionResource permissions() {
|
||||
return namespacePermissionResource.get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import de.otto.edison.hal.Links;
|
||||
import sonia.scm.repository.NamespacePermissions;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static de.otto.edison.hal.Link.link;
|
||||
@@ -39,12 +42,15 @@ class NamespaceToNamespaceDtoMapper {
|
||||
}
|
||||
|
||||
NamespaceDto map(String namespace) {
|
||||
return new NamespaceDto(
|
||||
namespace,
|
||||
linkingTo()
|
||||
.self(links.namespace().self(namespace))
|
||||
.single(link("repositories", links.repositoryCollection().forNamespace(namespace)))
|
||||
.build()
|
||||
);
|
||||
Links.Builder linkingTo = linkingTo();
|
||||
linkingTo
|
||||
.self(links.namespace().self(namespace))
|
||||
.single(link("repositories", links.repositoryCollection().forNamespace(namespace)));
|
||||
|
||||
if (NamespacePermissions.permissionRead().isPermitted()) {
|
||||
linkingTo
|
||||
.single(link("permissions", links.namespacePermission().all(namespace)));
|
||||
}
|
||||
return new NamespaceDto(namespace, linkingTo.build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,13 +21,15 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import de.otto.edison.hal.Embedded;
|
||||
import de.otto.edison.hal.HalRepresentation;
|
||||
import de.otto.edison.hal.Links;
|
||||
import sonia.scm.repository.Namespace;
|
||||
import sonia.scm.repository.NamespacePermissions;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryPermissions;
|
||||
|
||||
@@ -57,6 +59,14 @@ public class RepositoryPermissionCollectionToDtoMapper {
|
||||
return new HalRepresentation(createLinks(repository), embedDtos(repositoryPermissionDtoList));
|
||||
}
|
||||
|
||||
public HalRepresentation map(Namespace namespace) {
|
||||
List<RepositoryPermissionDto> repositoryPermissionDtoList = namespace.getPermissions()
|
||||
.stream()
|
||||
.map(permission -> repositoryPermissionToRepositoryPermissionDtoMapper.map(permission, namespace))
|
||||
.collect(toList());
|
||||
return new HalRepresentation(createLinks(namespace), embedDtos(repositoryPermissionDtoList));
|
||||
}
|
||||
|
||||
private Links createLinks(Repository repository) {
|
||||
RepositoryPermissions.permissionRead(repository).check();
|
||||
Links.Builder linksBuilder = linkingTo()
|
||||
@@ -67,6 +77,16 @@ public class RepositoryPermissionCollectionToDtoMapper {
|
||||
return linksBuilder.build();
|
||||
}
|
||||
|
||||
private Links createLinks(Namespace namespace) {
|
||||
NamespacePermissions.permissionRead().check();
|
||||
Links.Builder linksBuilder = linkingTo()
|
||||
.with(Links.linkingTo().self(resourceLinks.namespacePermission().all(namespace.getNamespace())).build());
|
||||
if (NamespacePermissions.permissionWrite().isPermitted()) {
|
||||
linksBuilder.single(link("create", resourceLinks.namespacePermission().create(namespace.getNamespace())));
|
||||
}
|
||||
return linksBuilder.build();
|
||||
}
|
||||
|
||||
private Embedded embedDtos(List<RepositoryPermissionDto> repositoryPermissionDtoList) {
|
||||
return embeddedBuilder()
|
||||
.with("permissions", repositoryPermissionDtoList)
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import de.otto.edison.hal.Links;
|
||||
@@ -31,6 +31,8 @@ import org.mapstruct.Context;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.MappingTarget;
|
||||
import sonia.scm.repository.Namespace;
|
||||
import sonia.scm.repository.NamespacePermissions;
|
||||
import sonia.scm.repository.RepositoryPermission;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryPermissions;
|
||||
@@ -51,18 +53,19 @@ public abstract class RepositoryPermissionToRepositoryPermissionDtoMapper {
|
||||
@Mapping(target = "attributes", ignore = true) // We do not map HAL attributes
|
||||
public abstract RepositoryPermissionDto map(RepositoryPermission permission, @Context Repository repository);
|
||||
|
||||
@Mapping(target = "attributes", ignore = true) // We do not map HAL attributes
|
||||
public abstract RepositoryPermissionDto map(RepositoryPermission permission, @Context Namespace namespace);
|
||||
|
||||
@BeforeMapping
|
||||
void validatePermissions(@Context Repository repository) {
|
||||
RepositoryPermissions.permissionRead(repository).check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the self, update and delete links.
|
||||
*
|
||||
* @param target the mapped dto
|
||||
* @param repository the repository
|
||||
*/
|
||||
@BeforeMapping
|
||||
void validatePermissions(@Context Namespace namespace) {
|
||||
NamespacePermissions.permissionRead().check();
|
||||
}
|
||||
|
||||
@AfterMapping
|
||||
void appendLinks(@MappingTarget RepositoryPermissionDto target, @Context Repository repository) {
|
||||
String permissionName = getUrlPermissionName(target);
|
||||
@@ -75,6 +78,18 @@ public abstract class RepositoryPermissionToRepositoryPermissionDtoMapper {
|
||||
target.add(linksBuilder.build());
|
||||
}
|
||||
|
||||
@AfterMapping
|
||||
void appendLinks(@MappingTarget RepositoryPermissionDto target, @Context Namespace namespace) {
|
||||
String permissionName = getUrlPermissionName(target);
|
||||
Links.Builder linksBuilder = linkingTo()
|
||||
.self(resourceLinks.namespacePermission().self(namespace.getNamespace(), permissionName));
|
||||
if (NamespacePermissions.permissionWrite().isPermitted()) {
|
||||
linksBuilder.single(link("update", resourceLinks.namespacePermission().update(namespace.getNamespace(), permissionName)));
|
||||
linksBuilder.single(link("delete", resourceLinks.namespacePermission().delete(namespace.getNamespace(), permissionName)));
|
||||
}
|
||||
target.add(linksBuilder.build());
|
||||
}
|
||||
|
||||
public String getUrlPermissionName(RepositoryPermissionDto repositoryPermissionDto) {
|
||||
return Optional.of(repositoryPermissionDto.getName())
|
||||
.filter(p -> !repositoryPermissionDto.isGroupPermission())
|
||||
|
||||
@@ -916,4 +916,40 @@ class ResourceLinks {
|
||||
return namespaceLinkBuilder.method("getNamespaceResource").parameters().method("get").parameters(namespace).href();
|
||||
}
|
||||
}
|
||||
|
||||
public NamespacePermissionLinks namespacePermission() {
|
||||
return new NamespacePermissionLinks(scmPathInfoStore.get());
|
||||
}
|
||||
|
||||
static class NamespacePermissionLinks {
|
||||
private final LinkBuilder permissionLinkBuilder;
|
||||
|
||||
NamespacePermissionLinks(ScmPathInfo pathInfo) {
|
||||
permissionLinkBuilder = new LinkBuilder(pathInfo, NamespaceRootResource.class, NamespaceResource.class, NamespacePermissionResource.class);
|
||||
}
|
||||
|
||||
String all(String namespace) {
|
||||
return permissionLinkBuilder.method("getNamespaceResource").parameters(namespace).method("permissions").parameters().method("getAll").parameters().href();
|
||||
}
|
||||
|
||||
String create(String namespace) {
|
||||
return permissionLinkBuilder.method("getNamespaceResource").parameters(namespace).method("permissions").parameters().method("create").parameters().href();
|
||||
}
|
||||
|
||||
String self(String namespace, String permissionName) {
|
||||
return getLink(namespace, permissionName, "get");
|
||||
}
|
||||
|
||||
String update(String namespace, String permissionName) {
|
||||
return getLink(namespace, permissionName, "update");
|
||||
}
|
||||
|
||||
String delete(String namespace, String permissionName) {
|
||||
return getLink(namespace, permissionName, "delete");
|
||||
}
|
||||
|
||||
private String getLink(String namespace, String permissionName, String methodName) {
|
||||
return permissionLinkBuilder.method("getNamespaceResource").parameters(namespace).method("permissions").parameters().method(methodName).parameters(permissionName).href();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,10 +64,12 @@ import sonia.scm.net.ahc.XmlContentTransformer;
|
||||
import sonia.scm.plugin.DefaultPluginManager;
|
||||
import sonia.scm.plugin.PluginLoader;
|
||||
import sonia.scm.plugin.PluginManager;
|
||||
import sonia.scm.repository.DefaultNamespaceManager;
|
||||
import sonia.scm.repository.DefaultRepositoryManager;
|
||||
import sonia.scm.repository.DefaultRepositoryProvider;
|
||||
import sonia.scm.repository.DefaultRepositoryRoleManager;
|
||||
import sonia.scm.repository.HealthCheckContextListener;
|
||||
import sonia.scm.repository.NamespaceManager;
|
||||
import sonia.scm.repository.NamespaceStrategy;
|
||||
import sonia.scm.repository.NamespaceStrategyProvider;
|
||||
import sonia.scm.repository.Repository;
|
||||
@@ -191,6 +193,7 @@ class ScmServletModule extends ServletModule {
|
||||
bindDecorated(GroupManager.class, DefaultGroupManager.class,
|
||||
GroupManagerProvider.class);
|
||||
bind(GroupDisplayManager.class, DefaultGroupDisplayManager.class);
|
||||
bind(NamespaceManager.class, DefaultNamespaceManager.class);
|
||||
bind(GroupCollector.class, DefaultGroupCollector.class);
|
||||
bind(CGIExecutorFactory.class, DefaultCGIExecutorFactory.class);
|
||||
|
||||
|
||||
@@ -24,83 +24,354 @@
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import org.apache.shiro.authz.AuthorizationException;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.apache.shiro.util.ThreadContext;
|
||||
import org.jboss.resteasy.mock.MockHttpRequest;
|
||||
import org.jboss.resteasy.mock.MockHttpResponse;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.repository.Namespace;
|
||||
import sonia.scm.repository.NamespaceManager;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.repository.RepositoryPermission;
|
||||
import sonia.scm.web.RestDispatcher;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Optional;
|
||||
|
||||
import static com.google.inject.util.Providers.of;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.singleton;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class NamespaceRootResourceTest {
|
||||
|
||||
@Mock
|
||||
RepositoryManager repositoryManager;
|
||||
@Mock
|
||||
NamespaceManager namespaceManager;
|
||||
@Mock
|
||||
Subject subject;
|
||||
|
||||
RestDispatcher dispatcher = new RestDispatcher();
|
||||
MockHttpResponse response = new MockHttpResponse();
|
||||
|
||||
ResourceLinks links = ResourceLinksMock.createMock(URI.create("/"));
|
||||
|
||||
@InjectMocks
|
||||
RepositoryPermissionToRepositoryPermissionDtoMapperImpl repositoryPermissionToRepositoryPermissionDtoMapper;
|
||||
|
||||
@BeforeEach
|
||||
void mockSubject() {
|
||||
ThreadContext.bind(subject);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void unbindSubject() {
|
||||
ThreadContext.unbindSubject();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUpResources() {
|
||||
NamespaceToNamespaceDtoMapper namespaceMapper = new NamespaceToNamespaceDtoMapper(links);
|
||||
NamespaceCollectionToDtoMapper namespaceCollectionToDtoMapper = new NamespaceCollectionToDtoMapper(namespaceMapper, links);
|
||||
RepositoryPermissionCollectionToDtoMapper repositoryPermissionCollectionToDtoMapper = new RepositoryPermissionCollectionToDtoMapper(repositoryPermissionToRepositoryPermissionDtoMapper, links);
|
||||
RepositoryPermissionDtoToRepositoryPermissionMapperImpl dtoToModelMapper = new RepositoryPermissionDtoToRepositoryPermissionMapperImpl();
|
||||
|
||||
NamespaceCollectionResource namespaceCollectionResource = new NamespaceCollectionResource(repositoryManager, namespaceCollectionToDtoMapper);
|
||||
NamespaceResource namespaceResource = new NamespaceResource(repositoryManager, namespaceMapper);
|
||||
NamespacePermissionResource namespacePermissionResource = new NamespacePermissionResource(dtoToModelMapper, repositoryPermissionToRepositoryPermissionDtoMapper, repositoryPermissionCollectionToDtoMapper, links, namespaceManager);
|
||||
NamespaceResource namespaceResource = new NamespaceResource(repositoryManager, namespaceMapper, of(namespacePermissionResource));
|
||||
dispatcher.addSingletonResource(new NamespaceRootResource(of(namespaceCollectionResource), of(namespaceResource)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnAllNamespaces() throws URISyntaxException, UnsupportedEncodingException {
|
||||
when(repositoryManager.getAllNamespaces()).thenReturn(asList("hitchhiker", "space"));
|
||||
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + NamespaceRootResource.NAMESPACE_PATH_V2);
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getContentAsString())
|
||||
.contains("\"self\":{\"href\":\"/v2/namespaces/\"}")
|
||||
.contains("\"_embedded\"")
|
||||
.contains("\"namespace\":\"hitchhiker\"")
|
||||
.contains("\"namespace\":\"space\"");
|
||||
@BeforeEach
|
||||
void mockExistingNamespaces() {
|
||||
lenient().when(repositoryManager.getAllNamespaces()).thenReturn(asList("hitchhiker", "space"));
|
||||
Namespace hitchhikerNamespace = new Namespace("hitchhiker");
|
||||
hitchhikerNamespace.setPermissions(singleton(new RepositoryPermission("humans", "READ", true)));
|
||||
Namespace spaceNamespace = new Namespace("space");
|
||||
lenient().when(namespaceManager.getAll()).thenReturn(asList(hitchhikerNamespace, spaceNamespace));
|
||||
lenient().when(namespaceManager.get("hitchhiker")).thenReturn(Optional.of(hitchhikerNamespace));
|
||||
lenient().when(namespaceManager.get("space")).thenReturn(Optional.of(spaceNamespace));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnSingleNamespace() throws URISyntaxException, UnsupportedEncodingException {
|
||||
when(repositoryManager.getAllNamespaces()).thenReturn(asList("hitchhiker", "space"));
|
||||
@Nested
|
||||
class WithoutSpecialPermission {
|
||||
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + NamespaceRootResource.NAMESPACE_PATH_V2 + "space");
|
||||
@BeforeEach
|
||||
void mockNoPermissions() {
|
||||
lenient().when(subject.isPermitted(anyString())).thenReturn(false);
|
||||
lenient().doThrow(AuthorizationException.class).when(subject).checkPermission("namespace:permissionRead");
|
||||
lenient().doThrow(AuthorizationException.class).when(subject).checkPermission("namespace:permissionWrite");
|
||||
}
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
@Test
|
||||
void shouldReturnAllNamespaces() throws URISyntaxException, UnsupportedEncodingException {
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + NamespaceRootResource.NAMESPACE_PATH_V2);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getContentAsString())
|
||||
.contains("\"namespace\":\"space\"")
|
||||
.contains("\"self\":{\"href\":\"/v2/namespaces/space\"}");
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getContentAsString())
|
||||
.contains("\"self\":{\"href\":\"/v2/namespaces/\"}")
|
||||
.contains("\"_embedded\"")
|
||||
.contains("\"namespace\":\"hitchhiker\"")
|
||||
.contains("\"namespace\":\"space\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnSingleNamespace() throws URISyntaxException, UnsupportedEncodingException {
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + NamespaceRootResource.NAMESPACE_PATH_V2 + "space");
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getContentAsString())
|
||||
.contains("\"namespace\":\"space\"")
|
||||
.contains("\"self\":{\"href\":\"/v2/namespaces/space\"}")
|
||||
.doesNotContain("permissions");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleUnknownNamespace() throws URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + NamespaceRootResource.NAMESPACE_PATH_V2 + "unknown");
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(404);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotReturnPermissions() throws URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + NamespaceRootResource.NAMESPACE_PATH_V2 + "space/permissions");
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(403);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleUnknownNamespace() throws URISyntaxException, UnsupportedEncodingException {
|
||||
when(repositoryManager.getAllNamespaces()).thenReturn(asList("hitchhiker", "space"));
|
||||
@Nested
|
||||
class WithReadPermission {
|
||||
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + NamespaceRootResource.NAMESPACE_PATH_V2 + "unknown");
|
||||
@BeforeEach
|
||||
void grantReadPermission() {
|
||||
lenient().when(subject.isPermitted("namespace:permissionRead")).thenReturn(true);
|
||||
lenient().when(subject.isPermitted("namespace:permissionWrite")).thenReturn(false);
|
||||
lenient().doThrow(AuthorizationException.class).when(subject).checkPermission("namespace:permissionWrite");
|
||||
}
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
@Test
|
||||
void shouldContainPermissionLinkWhenPermitted() throws UnsupportedEncodingException, URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + NamespaceRootResource.NAMESPACE_PATH_V2 + "space");
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(404);
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getContentAsString())
|
||||
.contains("\"permissions\":{\"href\":\"/v2/namespaces/space/permissions\"}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnPermissions() throws UnsupportedEncodingException, URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + NamespaceRootResource.NAMESPACE_PATH_V2 + "hitchhiker/permissions");
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getContentAsString())
|
||||
.contains("\"self\":{\"href\":\"/v2/namespaces/hitchhiker/permissions\"}")
|
||||
.contains("{\"name\":\"humans\",\"verbs\":[],\"role\":\"READ\",\"groupPermission\":true,\"")
|
||||
.doesNotContain("create");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnSinglePermission() throws UnsupportedEncodingException, URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + NamespaceRootResource.NAMESPACE_PATH_V2 + "hitchhiker/permissions/@humans");
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getContentAsString())
|
||||
.contains("\"self\":{\"href\":\"/v2/namespaces/hitchhiker/permissions/@humans\"}")
|
||||
.contains("{\"name\":\"humans\",\"verbs\":[],\"role\":\"READ\",\"groupPermission\":true,\"")
|
||||
.doesNotContain("update")
|
||||
.doesNotContain("delete");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleMissingNamespace() throws URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + NamespaceRootResource.NAMESPACE_PATH_V2 + "no_such_namespace/permissions");
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(404);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotCreateNewPermission() throws URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.post("/" + NamespaceRootResource.NAMESPACE_PATH_V2 + "space/permissions")
|
||||
.content("{\"name\":\"dent\",\"verbs\":[],\"role\":\"WRITE\",\"groupPermission\":false}".getBytes())
|
||||
.header("Content-Type", "application/vnd.scmm-repositoryPermission+json;v=2");
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(403);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotDeletePermission() throws URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.delete("/" + NamespaceRootResource.NAMESPACE_PATH_V2 + "hitchhiker/permissions/@humans");
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(403);
|
||||
}
|
||||
|
||||
@Nested
|
||||
class WithWritePermission {
|
||||
|
||||
@BeforeEach
|
||||
void grantWritePermission() {
|
||||
lenient().when(subject.isPermitted("namespace:permissionWrite")).thenReturn(true);
|
||||
lenient().doNothing().when(subject).checkPermission("namespace:permissionWrite");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldContainCreateLink() throws UnsupportedEncodingException, URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + NamespaceRootResource.NAMESPACE_PATH_V2 + "space/permissions");
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getContentAsString())
|
||||
.contains("\"create\":{\"href\":\"/v2/namespaces/space/permissions\"}");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldContainModificationLinks() throws UnsupportedEncodingException, URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.get("/" + NamespaceRootResource.NAMESPACE_PATH_V2 + "hitchhiker/permissions/@humans");
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.getContentAsString())
|
||||
.contains("\"update\":{\"href\":\"/v2/namespaces/hitchhiker/permissions/@humans\"")
|
||||
.contains("\"delete\":{\"href\":\"/v2/namespaces/hitchhiker/permissions/@humans\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateNewPermission() throws URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.post("/" + NamespaceRootResource.NAMESPACE_PATH_V2 + "space/permissions")
|
||||
.content("{\"name\":\"dent\",\"verbs\":[],\"role\":\"WRITE\",\"groupPermission\":false}".getBytes())
|
||||
.header("Content-Type", "application/vnd.scmm-repositoryPermission+json;v=2");
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(201);
|
||||
verify(namespaceManager)
|
||||
.modify(argThat(
|
||||
namespace -> {
|
||||
assertThat(namespace.getPermissions()).hasSize(1);
|
||||
RepositoryPermission permission = namespace.getPermissions().iterator().next();
|
||||
assertThat(permission.getName()).isEqualTo("dent");
|
||||
assertThat(permission.getRole()).isEqualTo("WRITE");
|
||||
assertThat(permission.getVerbs()).isEmpty();
|
||||
assertThat(permission.isGroupPermission()).isFalse();
|
||||
return true;
|
||||
})
|
||||
);
|
||||
assertThat(response.getOutputHeaders().get("Location"))
|
||||
.containsExactly(URI.create("/v2/namespaces/space/permissions/dent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUpdatePermission() throws URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.put("/" + NamespaceRootResource.NAMESPACE_PATH_V2 + "hitchhiker/permissions/@humans")
|
||||
.content("{\"name\":\"humans\",\"verbs\":[],\"role\":\"WRITE\",\"groupPermission\":true}".getBytes())
|
||||
.header("Content-Type", "application/vnd.scmm-repositoryPermission+json;v=2");
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(204);
|
||||
verify(namespaceManager)
|
||||
.modify(argThat(
|
||||
namespace -> {
|
||||
assertThat(namespace.getPermissions()).hasSize(1);
|
||||
RepositoryPermission permission = namespace.getPermissions().iterator().next();
|
||||
assertThat(permission.getName()).isEqualTo("humans");
|
||||
assertThat(permission.getRole()).isEqualTo("WRITE");
|
||||
assertThat(permission.getVerbs()).isEmpty();
|
||||
assertThat(permission.isGroupPermission()).isTrue();
|
||||
return true;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleNotExistingPermissionOnUpdate() throws URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.put("/" + NamespaceRootResource.NAMESPACE_PATH_V2 + "hitchhiker/permissions/humans")
|
||||
.content("{\"name\":\"humans\",\"verbs\":[],\"role\":\"WRITE\",\"groupPermission\":true}".getBytes())
|
||||
.header("Content-Type", "application/vnd.scmm-repositoryPermission+json;v=2");
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(404);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleExistingPermissionOnCreate() throws URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.post("/" + NamespaceRootResource.NAMESPACE_PATH_V2 + "hitchhiker/permissions")
|
||||
.content("{\"name\":\"humans\",\"verbs\":[],\"role\":\"WRITE\",\"groupPermission\":true}".getBytes())
|
||||
.header("Content-Type", "application/vnd.scmm-repositoryPermission+json;v=2");
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(409);
|
||||
verify(namespaceManager, never()).modify(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDeleteExistingPermission() throws URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.delete("/" + NamespaceRootResource.NAMESPACE_PATH_V2 + "hitchhiker/permissions/@humans");
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(204);
|
||||
verify(namespaceManager)
|
||||
.modify(argThat(
|
||||
namespace -> {
|
||||
assertThat(namespace.getPermissions()).isEmpty();
|
||||
return true;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldHandleRedundantDeleteIdempotent() throws URISyntaxException {
|
||||
MockHttpRequest request = MockHttpRequest.delete("/" + NamespaceRootResource.NAMESPACE_PATH_V2 + "hitchhiker/permissions/humans");
|
||||
|
||||
dispatcher.invoke(request, response);
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(204);
|
||||
verify(namespaceManager, never()).modify(any());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ public class ResourceLinksMock {
|
||||
lenient().when(resourceLinks.annotate()).thenReturn(new ResourceLinks.AnnotateLinks(pathInfo));
|
||||
lenient().when(resourceLinks.namespace()).thenReturn(new ResourceLinks.NamespaceLinks(pathInfo));
|
||||
lenient().when(resourceLinks.namespaceCollection()).thenReturn(new ResourceLinks.NamespaceCollectionLinks(pathInfo));
|
||||
lenient().when(resourceLinks.namespacePermission()).thenReturn(new ResourceLinks.NamespacePermissionLinks(pathInfo));
|
||||
|
||||
return resourceLinks;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user