diff --git a/index.adoc b/index.adoc index 0b8b735..17b8664 100644 --- a/index.adoc +++ b/index.adoc @@ -3212,6 +3212,80 @@ ifdef::internal-generation[] endif::internal-generation[] +[.getPermissionGlobal] +==== getPermissionGlobal + +`GET /permissions/global` + +Get global permissions configuration + +===== Description + +Get the global permissions for ... TODO + + +// markup not found, no include::{specDir}permissions/global/GET/spec.adoc[opts=optional] + + + +===== Parameters + + + + + + + +===== Return Type + +<> + + +===== Content Type + +* application/json + +===== Responses + +.HTTP Response Codes +[cols="2,3,1"] +|=== +| Code | Message | Datatype + + +| 200 +| +| <> + + +| 400 +| +| <> + +|=== + +===== Samples + + +// markup not found, no include::{snippetDir}permissions/global/GET/http-request.adoc[opts=optional] + + +// markup not found, no include::{snippetDir}permissions/global/GET/http-response.adoc[opts=optional] + + + +// file not found, no * wiremock data link :permissions/global/GET/GET.json[] + + +ifdef::internal-generation[] +===== Implementation + +// markup not found, no include::{specDir}permissions/global/GET/implementation.adoc[opts=optional] + + +endif::internal-generation[] + + [.setPermissionAnonymousAccess] ==== setPermissionAnonymousAccess @@ -3299,6 +3373,93 @@ ifdef::internal-generation[] endif::internal-generation[] +[.setPermissionGlobal] +==== setPermissionGlobal + +`PUT /permissions/global` + +Set global permissions configuration + +===== Description + +Set the global permissions for ... TODO + + +// markup not found, no include::{specDir}permissions/global/PUT/spec.adoc[opts=optional] + + + +===== Parameters + + +====== Body Parameter + +[cols="2,3,1,1,1"] +|=== +|Name| Description| Required| Default| Pattern + +| PermissionsGlobalBean +| <> +| X +| +| + +|=== + + + + + +===== Return Type + +<> + + +===== Content Type + +* application/json + +===== Responses + +.HTTP Response Codes +[cols="2,3,1"] +|=== +| Code | Message | Datatype + + +| 200 +| +| <> + + +| 400 +| +| <> + +|=== + +===== Samples + + +// markup not found, no include::{snippetDir}permissions/global/PUT/http-request.adoc[opts=optional] + + +// markup not found, no include::{snippetDir}permissions/global/PUT/http-response.adoc[opts=optional] + + + +// file not found, no * wiremock data link :permissions/global/PUT/PUT.json[] + + +ifdef::internal-generation[] +===== Implementation + +// markup not found, no include::{specDir}permissions/global/PUT/implementation.adoc[opts=optional] + + +endif::internal-generation[] + + [.Ping] === Ping @@ -5960,6 +6121,31 @@ endif::internal-generation[] |=== +[#PermissionsGlobalBean] +=== _PermissionsGlobalBean_ + + + +[.fields-PermissionsGlobalBean] +[cols="2,1,2,4,1"] +|=== +| Field Name| Required| Type| Description| Format + +| groupPermissions +| +| Map of <> +| +| + +| anonymousPermissions +| +| List of <> +| +| + +|=== + + [#SettingsBean] === _SettingsBean_ diff --git a/pom.xml b/pom.xml index 48962cc..e619dd4 100644 --- a/pom.xml +++ b/pom.xml @@ -68,7 +68,7 @@ 8.1.4 ${project.groupId}.${project.artifactId} 2.2.4 - 0.5.0 + 0.5.1-SNAPSHOT 2.0.2 2.3.1 2.1.1 diff --git a/src/main/java/de/aservo/confapi/confluence/rest/PermissionsResourceImpl.java b/src/main/java/de/aservo/confapi/confluence/rest/PermissionsResourceImpl.java index 81b5cf3..1e0d3c0 100644 --- a/src/main/java/de/aservo/confapi/confluence/rest/PermissionsResourceImpl.java +++ b/src/main/java/de/aservo/confapi/confluence/rest/PermissionsResourceImpl.java @@ -1,6 +1,7 @@ package de.aservo.confapi.confluence.rest; import com.sun.jersey.spi.container.ResourceFilters; +import de.aservo.confapi.commons.model.PermissionsGlobalBean; import de.aservo.confapi.confluence.filter.SysAdminOnlyResourceFilter; import de.aservo.confapi.confluence.model.PermissionAnonymousAccessBean; import de.aservo.confapi.confluence.rest.api.PermissionsResource; @@ -28,6 +29,17 @@ public PermissionsResourceImpl(PermissionsService permissionsService) { this.permissionsService = permissionsService; } + @Override + public Response getPermissionGlobal() { + return Response.ok(permissionsService.getPermissionsGlobal()).build(); + } + + @Override + public Response setPermissionGlobal( + @NotNull final PermissionsGlobalBean permissionsGlobalBean) { + return Response.ok(permissionsService.setPermissionsGlobal(permissionsGlobalBean)).build(); + } + @Override public Response getPermissionAnonymousAccess() { return Response.ok(permissionsService.getPermissionAnonymousAccess()).build(); diff --git a/src/main/java/de/aservo/confapi/confluence/rest/api/PermissionsResource.java b/src/main/java/de/aservo/confapi/confluence/rest/api/PermissionsResource.java index 99d9c33..5b1ba45 100644 --- a/src/main/java/de/aservo/confapi/confluence/rest/api/PermissionsResource.java +++ b/src/main/java/de/aservo/confapi/confluence/rest/api/PermissionsResource.java @@ -1,23 +1,54 @@ package de.aservo.confapi.confluence.rest.api; -import de.aservo.confapi.confluence.model.PermissionAnonymousAccessBean; import de.aservo.confapi.commons.constants.ConfAPI; import de.aservo.confapi.commons.model.ErrorCollection; +import de.aservo.confapi.commons.model.PermissionsGlobalBean; +import de.aservo.confapi.confluence.model.PermissionAnonymousAccessBean; import io.swagger.v3.oas.annotations.Operation; 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 javax.validation.constraints.NotNull; -import javax.ws.rs.GET; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; public interface PermissionsResource { + @GET + @Path(ConfAPI.PERMISSIONS_GLOBAL) + @Produces(MediaType.APPLICATION_JSON) + @Operation( + tags = { ConfAPI.PERMISSIONS }, + summary = "Get global permissions configuration", + description = "Get the global permissions for ... TODO", + responses = { + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = PermissionsGlobalBean.class))), + @ApiResponse(responseCode = "400", content = @Content(schema = @Schema(implementation = ErrorCollection.class))) + } + ) + Response getPermissionGlobal(); + + @PUT + @Path(ConfAPI.PERMISSIONS_GLOBAL) + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + @Operation( + tags = { ConfAPI.PERMISSIONS }, + summary = "Set global permissions configuration", + description = "Set the global permissions for ... TODO", + responses = { + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = PermissionsGlobalBean.class))), + @ApiResponse(responseCode = "400", content = @Content(schema = @Schema(implementation = ErrorCollection.class))) + } + ) + Response setPermissionGlobal( + @NotNull PermissionsGlobalBean permissionsGlobalBean); + @GET @Path(ConfAPI.PERMISSION_ANONYMOUS_ACCESS) + @Produces(MediaType.APPLICATION_JSON) @Operation( tags = { ConfAPI.PERMISSIONS }, summary = "Retrieve current anonymous access configuration", @@ -31,6 +62,8 @@ public interface PermissionsResource { @PUT @Path(ConfAPI.PERMISSION_ANONYMOUS_ACCESS) + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) @Operation( tags = { ConfAPI.PERMISSIONS }, summary = "Set anonymous access configuration", diff --git a/src/main/java/de/aservo/confapi/confluence/service/PermissionsServiceImpl.java b/src/main/java/de/aservo/confapi/confluence/service/PermissionsServiceImpl.java index 2271463..f0ed11f 100644 --- a/src/main/java/de/aservo/confapi/confluence/service/PermissionsServiceImpl.java +++ b/src/main/java/de/aservo/confapi/confluence/service/PermissionsServiceImpl.java @@ -5,31 +5,73 @@ import com.atlassian.confluence.security.service.AnonymousUserPermissionsService; import com.atlassian.plugin.spring.scanner.annotation.export.ExportAsService; import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport; +import de.aservo.confapi.commons.exception.BadRequestException; +import de.aservo.confapi.commons.exception.InternalServerErrorException; +import de.aservo.confapi.commons.model.PermissionsGlobalBean; import de.aservo.confapi.confluence.model.PermissionAnonymousAccessBean; import de.aservo.confapi.confluence.service.api.PermissionsService; import org.springframework.stereotype.Component; import javax.inject.Inject; -import java.util.List; +import javax.validation.constraints.NotNull; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; -import static com.atlassian.confluence.security.SpacePermission.BROWSE_USERS_PERMISSION; -import static com.atlassian.confluence.security.SpacePermission.USE_CONFLUENCE_PERMISSION; +import static com.atlassian.confluence.security.SpacePermission.*; @Component @ExportAsService(PermissionsService.class) public class PermissionsServiceImpl implements PermissionsService { + @ComponentImport private final AnonymousUserPermissionsService anonymousUserPermissionsService; + + @ComponentImport private final SpacePermissionManager spacePermissionManager; @Inject public PermissionsServiceImpl( - @ComponentImport AnonymousUserPermissionsService anonymousUserPermissionsService, - @ComponentImport SpacePermissionManager spacePermissionManager) { + final AnonymousUserPermissionsService anonymousUserPermissionsService, + final SpacePermissionManager spacePermissionManager) { + this.anonymousUserPermissionsService = anonymousUserPermissionsService; this.spacePermissionManager = spacePermissionManager; } + @Override + public PermissionsGlobalBean getPermissionsGlobal() { + final HashSet globalPermissions = new HashSet<>(spacePermissionManager.getGlobalPermissions()); + final TreeSet anonymousGlobalPermissions = new TreeSet<>(); + final TreeMap> groupGlobalPermissions = new TreeMap<>(); + + // group global permissions + for (SpacePermission globalPermission : globalPermissions.stream().filter(SpacePermission::isGroupPermission).collect(Collectors.toList())) { + final String group = globalPermission.getGroup(); + groupGlobalPermissions.putIfAbsent(group, new TreeSet<>()); + groupGlobalPermissions.get(group).add(globalPermission.getType()); + } + + // anonymous global permissions + for (SpacePermission globalPermission : globalPermissions.stream().filter(SpacePermission::isAnonymousPermission).collect(Collectors.toList())) { + anonymousGlobalPermissions.add(globalPermission.getType()); + } + + final PermissionsGlobalBean permissionsGlobalBean = new PermissionsGlobalBean(); + permissionsGlobalBean.setGroupPermissions(groupGlobalPermissions); + permissionsGlobalBean.setAnonymousPermissions(anonymousGlobalPermissions); + return permissionsGlobalBean; + } + + @Override + public PermissionsGlobalBean setPermissionsGlobal( + @NotNull final PermissionsGlobalBean permissionsGlobalBean) { + + setPermissionsGlobalForGroup(permissionsGlobalBean); + setPermissionsGlobalForAnonymous(permissionsGlobalBean); + return getPermissionsGlobal(); + } + @Override public PermissionAnonymousAccessBean getPermissionAnonymousAccess() { List globalPermissions = spacePermissionManager.getGlobalPermissions(); @@ -50,6 +92,101 @@ public PermissionAnonymousAccessBean setPermissionAnonymousAccess(PermissionAnon return getPermissionAnonymousAccess(); } + private void setPermissionsGlobalForGroup( + @NotNull final PermissionsGlobalBean permissionsGlobalBean) { + + final Map> existingGroupPermissions = spacePermissionManager.getGlobalPermissions().stream() + .filter(SpacePermission::isGroupPermission) + .filter(permission -> permission.getGroup() != null) + .collect(Collectors.groupingBy( + SpacePermission::getGroup, + Collectors.toMap( + SpacePermission::getType, + Function.identity(), + (existing, replacement) -> existing + ) + )); + + final Map> requestGroupPermissions = permissionsGlobalBean.getGroupPermissions(); + final Set validGlobalPermissions = new HashSet<>(SpacePermission.GLOBAL_PERMISSIONS); + + if (requestGroupPermissions == null) { + return; + } + + // remove all global permissions of a group that currently exist but are not contained in the request + for (String group : existingGroupPermissions.keySet()) { + // only consider removing global permissions of a group if the group is contained in the request + if (!requestGroupPermissions.containsKey(group)) { + continue; + } + + for (Map.Entry permissionEntry : existingGroupPermissions.get(group).entrySet()) { + if (!validGlobalPermissions.contains(permissionEntry.getKey())) { + throw new InternalServerErrorException(String.format("The given global permission '%s' does not exist", permissionEntry.getKey())); + } + + if (!requestGroupPermissions.get(group).contains(permissionEntry.getKey())) { + spacePermissionManager.removePermission(permissionEntry.getValue()); // nosonar: deprecated but no alternative + } + } + } + + // add all global permissions of a group that currently don't exist but are contained in the request + for (String group : requestGroupPermissions.keySet()) { + // consider all groups of the request for global permissions that are supposed to be added + for (String permissionType : requestGroupPermissions.get(group)) { + if (!validGlobalPermissions.contains(permissionType)) { + throw new BadRequestException(String.format("The given global permission '%s' does not exist", permissionType)); + } + + if (!existingGroupPermissions.containsKey(group) || !existingGroupPermissions.get(group).containsKey(permissionType)) { + final SpacePermission permission = SpacePermission.createGroupSpacePermission(permissionType, null, group); + spacePermissionManager.savePermission(permission); // nosonar: deprecated but no alternative + } + } + } + } + + private void setPermissionsGlobalForAnonymous( + @NotNull final PermissionsGlobalBean permissionsGlobalBean) { + + final Map existingAnonymousPermissions = spacePermissionManager.getGlobalPermissions().stream() + .filter(SpacePermission::isAnonymousPermission) + .collect(Collectors.toMap( + SpacePermission::getType, + Function.identity(), + (existing, replacement) -> existing + ) + ); + + final Set requestAnonymousPermissions = new HashSet<>(permissionsGlobalBean.getAnonymousPermissions()); + final Set invalidAnonymousPermissions = new HashSet<>(INVALID_ANONYMOUS_PERMISSIONS); + + // remove all anonymous global permissions that currently exist but are not contained in the request + for (Map.Entry permissionEntry : existingAnonymousPermissions.entrySet()) { + if (invalidAnonymousPermissions.contains(permissionEntry.getKey())) { + throw new InternalServerErrorException(String.format("The given global permission '%s' is invalid for anonymous", permissionEntry.getKey())); + } + + if (!requestAnonymousPermissions.contains(permissionEntry.getKey())) { + spacePermissionManager.removePermission(permissionEntry.getValue()); // nosonar: deprecated but no alternative + } + } + + // add all anonymous permissions that currently don't exist but are contained in the request + for (String permissionType : requestAnonymousPermissions) { + if (invalidAnonymousPermissions.contains(permissionType)) { + throw new BadRequestException(String.format("The given global permission '%s' is invalid for anonymous", permissionType)); + } + + if (!existingAnonymousPermissions.containsKey(permissionType)) { + final SpacePermission permission = SpacePermission.createAnonymousSpacePermission(permissionType, null); + spacePermissionManager.savePermission(permission); // nosonar: deprecated but no alternative + } + } + } + private boolean containsAnonymousPermission(List permissions, String permissionType) { for (SpacePermission permission : permissions) { if (permission.getType().equals(permissionType) && permission.getGroup() == null) { diff --git a/src/main/java/de/aservo/confapi/confluence/service/api/PermissionsService.java b/src/main/java/de/aservo/confapi/confluence/service/api/PermissionsService.java index d119d0e..aceddfe 100644 --- a/src/main/java/de/aservo/confapi/confluence/service/api/PermissionsService.java +++ b/src/main/java/de/aservo/confapi/confluence/service/api/PermissionsService.java @@ -1,15 +1,28 @@ package de.aservo.confapi.confluence.service.api; +import de.aservo.confapi.commons.model.PermissionsGlobalBean; import de.aservo.confapi.confluence.model.PermissionAnonymousAccessBean; import javax.validation.constraints.NotNull; public interface PermissionsService { + /** + * @return TODO + */ + PermissionsGlobalBean getPermissionsGlobal(); + + /** + * @param permissionsGlobalBean TODO + * @return TODO + */ + PermissionsGlobalBean setPermissionsGlobal( + @NotNull PermissionsGlobalBean permissionsGlobalBean); + /** * Returns the currently configured anonymous access permissions * - * @return + * @return @return */ PermissionAnonymousAccessBean getPermissionAnonymousAccess(); @@ -19,5 +32,7 @@ public interface PermissionsService { * @param accessBean - the config to set * @return the updated anonymous access permissions */ - PermissionAnonymousAccessBean setPermissionAnonymousAccess(@NotNull PermissionAnonymousAccessBean accessBean); + PermissionAnonymousAccessBean setPermissionAnonymousAccess( + @NotNull PermissionAnonymousAccessBean accessBean); + }