From 76bb2b084740697ccc3d49e9f16fdceb6e8070bb Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Tue, 25 Jun 2024 10:34:16 +0200 Subject: [PATCH] Add endpoint for calculating effective user start nodes (#16609) * Add endpoint for calculating effective user start nodes * Fix OpenAPI --- .../User/CalculateStartNodesUserController.cs | 59 ++++++++++ .../Factories/IUserPresentationFactory.cs | 2 + .../Factories/UserPresentationFactory.cs | 17 +++ src/Umbraco.Cms.Api.Management/OpenApi.json | 105 ++++++++++++++++++ .../CalculatedUserStartNodesResponseModel.cs | 14 +++ 5 files changed, 197 insertions(+) create mode 100644 src/Umbraco.Cms.Api.Management/Controllers/User/CalculateStartNodesUserController.cs create mode 100644 src/Umbraco.Cms.Api.Management/ViewModels/User/CalculatedUserStartNodesResponseModel.cs diff --git a/src/Umbraco.Cms.Api.Management/Controllers/User/CalculateStartNodesUserController.cs b/src/Umbraco.Cms.Api.Management/Controllers/User/CalculateStartNodesUserController.cs new file mode 100644 index 0000000000..98f1526ce9 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/User/CalculateStartNodesUserController.cs @@ -0,0 +1,59 @@ +using Asp.Versioning; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Management.Factories; +using Umbraco.Cms.Api.Management.ViewModels.User; +using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Security.Authorization; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Services.OperationStatus; +using Umbraco.Cms.Web.Common.Authorization; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Api.Management.Controllers.User; + +[ApiVersion("1.0")] +public class CalculatedStartNodesUserController : UserControllerBase +{ + private readonly IAuthorizationService _authorizationService; + private readonly IUserService _userService; + private readonly IUserPresentationFactory _userPresentationFactory; + + public CalculatedStartNodesUserController( + IAuthorizationService authorizationService, + IUserService userService, + IUserPresentationFactory userPresentationFactory) + { + _authorizationService = authorizationService; + _userService = userService; + _userPresentationFactory = userPresentationFactory; + } + + [HttpGet("{id:guid}/calculate-start-nodes")] + [MapToApiVersion("1.0")] + [ProducesResponseType(typeof(CalculatedUserStartNodesResponseModel), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)] + public async Task CalculatedStartNodes(CancellationToken cancellationToken, Guid id) + { + AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync( + User, + UserPermissionResource.WithKeys(id), + AuthorizationPolicies.UserPermissionByResource); + + if (!authorizationResult.Succeeded) + { + return Forbidden(); + } + + IUser? user = await _userService.GetAsync(id); + + if (user is null) + { + return UserOperationStatusResult(UserOperationStatus.UserNotFound); + } + + CalculatedUserStartNodesResponseModel responseModel = await _userPresentationFactory.CreateCalculatedUserStartNodesResponseModelAsync(user); + return Ok(responseModel); + } +} diff --git a/src/Umbraco.Cms.Api.Management/Factories/IUserPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/IUserPresentationFactory.cs index 9af4ace9e1..a1e8a79edf 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/IUserPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/IUserPresentationFactory.cs @@ -25,4 +25,6 @@ public interface IUserPresentationFactory Task CreateCurrentUserConfigurationModelAsync(); UserItemResponseModel CreateItemResponseModel(IUser user); + + Task CreateCalculatedUserStartNodesResponseModelAsync(IUser user); } diff --git a/src/Umbraco.Cms.Api.Management/Factories/UserPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/UserPresentationFactory.cs index 641f5883ed..9164be6772 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/UserPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/UserPresentationFactory.cs @@ -212,6 +212,23 @@ public class UserPresentationFactory : IUserPresentationFactory }); } + public async Task CreateCalculatedUserStartNodesResponseModelAsync(IUser user) + { + var mediaStartNodeIds = user.CalculateMediaStartNodeIds(_entityService, _appCaches); + ISet mediaStartNodeKeys = GetKeysFromIds(mediaStartNodeIds, UmbracoObjectTypes.Media); + var contentStartNodeIds = user.CalculateContentStartNodeIds(_entityService, _appCaches); + ISet documentStartNodeKeys = GetKeysFromIds(contentStartNodeIds, UmbracoObjectTypes.Document); + + return await Task.FromResult(new CalculatedUserStartNodesResponseModel() + { + Id = user.Key, + MediaStartNodeIds = mediaStartNodeKeys, + HasMediaRootAccess = HasRootAccess(mediaStartNodeIds), + DocumentStartNodeIds = documentStartNodeKeys, + HasDocumentRootAccess = HasRootAccess(contentStartNodeIds), + }); + } + private ISet GetKeysFromIds(IEnumerable? ids, UmbracoObjectTypes type) { IEnumerable? models = ids? diff --git a/src/Umbraco.Cms.Api.Management/OpenApi.json b/src/Umbraco.Cms.Api.Management/OpenApi.json index 0adc3c36b7..11dee452ca 100644 --- a/src/Umbraco.Cms.Api.Management/OpenApi.json +++ b/src/Umbraco.Cms.Api.Management/OpenApi.json @@ -30298,6 +30298,66 @@ ] } }, + "/umbraco/management/api/v1/user/{id}/calculate-start-nodes": { + "get": { + "tags": [ + "User" + ], + "operationId": "GetUserByIdCalculateStartNodes", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/CalculatedUserStartNodesResponseModel" + } + ] + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ProblemDetails" + } + ] + } + } + } + }, + "401": { + "description": "The resource is protected and requires an authentication token" + }, + "403": { + "description": "The authenticated user do not have access to this resource" + } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] + } + }, "/umbraco/management/api/v1/user/{id}/change-password": { "post": { "tags": [ @@ -33447,6 +33507,51 @@ }, "additionalProperties": false }, + "CalculatedUserStartNodesResponseModel": { + "required": [ + "documentStartNodeIds", + "hasDocumentRootAccess", + "hasMediaRootAccess", + "id", + "mediaStartNodeIds" + ], + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "documentStartNodeIds": { + "uniqueItems": true, + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/ReferenceByIdModel" + } + ] + } + }, + "hasDocumentRootAccess": { + "type": "boolean" + }, + "mediaStartNodeIds": { + "uniqueItems": true, + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/ReferenceByIdModel" + } + ] + } + }, + "hasMediaRootAccess": { + "type": "boolean" + } + }, + "additionalProperties": false + }, "ChangePasswordCurrentUserRequestModel": { "required": [ "newPassword" diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/User/CalculatedUserStartNodesResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/User/CalculatedUserStartNodesResponseModel.cs new file mode 100644 index 0000000000..8cc71e8482 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/ViewModels/User/CalculatedUserStartNodesResponseModel.cs @@ -0,0 +1,14 @@ +namespace Umbraco.Cms.Api.Management.ViewModels.User; + +public class CalculatedUserStartNodesResponseModel +{ + public required Guid Id { get; init; } + + public ISet DocumentStartNodeIds { get; set; } = new HashSet(); + + public bool HasDocumentRootAccess { get; set; } + + public ISet MediaStartNodeIds { get; set; } = new HashSet(); + + public bool HasMediaRootAccess { get; set; } +}