Skip to content

Commit 76bb2b0

Browse files
authored
Add endpoint for calculating effective user start nodes (#16609)
* Add endpoint for calculating effective user start nodes * Fix OpenAPI
1 parent 215fc43 commit 76bb2b0

File tree

5 files changed

+197
-0
lines changed

5 files changed

+197
-0
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using Asp.Versioning;
2+
using Microsoft.AspNetCore.Authorization;
3+
using Microsoft.AspNetCore.Http;
4+
using Microsoft.AspNetCore.Mvc;
5+
using Umbraco.Cms.Api.Management.Factories;
6+
using Umbraco.Cms.Api.Management.ViewModels.User;
7+
using Umbraco.Cms.Core.Models.Membership;
8+
using Umbraco.Cms.Core.Security.Authorization;
9+
using Umbraco.Cms.Core.Services;
10+
using Umbraco.Cms.Core.Services.OperationStatus;
11+
using Umbraco.Cms.Web.Common.Authorization;
12+
using Umbraco.Extensions;
13+
14+
namespace Umbraco.Cms.Api.Management.Controllers.User;
15+
16+
[ApiVersion("1.0")]
17+
public class CalculatedStartNodesUserController : UserControllerBase
18+
{
19+
private readonly IAuthorizationService _authorizationService;
20+
private readonly IUserService _userService;
21+
private readonly IUserPresentationFactory _userPresentationFactory;
22+
23+
public CalculatedStartNodesUserController(
24+
IAuthorizationService authorizationService,
25+
IUserService userService,
26+
IUserPresentationFactory userPresentationFactory)
27+
{
28+
_authorizationService = authorizationService;
29+
_userService = userService;
30+
_userPresentationFactory = userPresentationFactory;
31+
}
32+
33+
[HttpGet("{id:guid}/calculate-start-nodes")]
34+
[MapToApiVersion("1.0")]
35+
[ProducesResponseType(typeof(CalculatedUserStartNodesResponseModel), StatusCodes.Status200OK)]
36+
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
37+
public async Task<IActionResult> CalculatedStartNodes(CancellationToken cancellationToken, Guid id)
38+
{
39+
AuthorizationResult authorizationResult = await _authorizationService.AuthorizeResourceAsync(
40+
User,
41+
UserPermissionResource.WithKeys(id),
42+
AuthorizationPolicies.UserPermissionByResource);
43+
44+
if (!authorizationResult.Succeeded)
45+
{
46+
return Forbidden();
47+
}
48+
49+
IUser? user = await _userService.GetAsync(id);
50+
51+
if (user is null)
52+
{
53+
return UserOperationStatusResult(UserOperationStatus.UserNotFound);
54+
}
55+
56+
CalculatedUserStartNodesResponseModel responseModel = await _userPresentationFactory.CreateCalculatedUserStartNodesResponseModelAsync(user);
57+
return Ok(responseModel);
58+
}
59+
}

src/Umbraco.Cms.Api.Management/Factories/IUserPresentationFactory.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@ public interface IUserPresentationFactory
2525
Task<CurrenUserConfigurationResponseModel> CreateCurrentUserConfigurationModelAsync();
2626

2727
UserItemResponseModel CreateItemResponseModel(IUser user);
28+
29+
Task<CalculatedUserStartNodesResponseModel> CreateCalculatedUserStartNodesResponseModelAsync(IUser user);
2830
}

src/Umbraco.Cms.Api.Management/Factories/UserPresentationFactory.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,23 @@ public async Task<CurrentUserResponseModel> CreateCurrentUserResponseModelAsync(
212212
});
213213
}
214214

215+
public async Task<CalculatedUserStartNodesResponseModel> CreateCalculatedUserStartNodesResponseModelAsync(IUser user)
216+
{
217+
var mediaStartNodeIds = user.CalculateMediaStartNodeIds(_entityService, _appCaches);
218+
ISet<ReferenceByIdModel> mediaStartNodeKeys = GetKeysFromIds(mediaStartNodeIds, UmbracoObjectTypes.Media);
219+
var contentStartNodeIds = user.CalculateContentStartNodeIds(_entityService, _appCaches);
220+
ISet<ReferenceByIdModel> documentStartNodeKeys = GetKeysFromIds(contentStartNodeIds, UmbracoObjectTypes.Document);
221+
222+
return await Task.FromResult(new CalculatedUserStartNodesResponseModel()
223+
{
224+
Id = user.Key,
225+
MediaStartNodeIds = mediaStartNodeKeys,
226+
HasMediaRootAccess = HasRootAccess(mediaStartNodeIds),
227+
DocumentStartNodeIds = documentStartNodeKeys,
228+
HasDocumentRootAccess = HasRootAccess(contentStartNodeIds),
229+
});
230+
}
231+
215232
private ISet<ReferenceByIdModel> GetKeysFromIds(IEnumerable<int>? ids, UmbracoObjectTypes type)
216233
{
217234
IEnumerable<ReferenceByIdModel>? models = ids?

src/Umbraco.Cms.Api.Management/OpenApi.json

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30298,6 +30298,66 @@
3029830298
]
3029930299
}
3030030300
},
30301+
"/umbraco/management/api/v1/user/{id}/calculate-start-nodes": {
30302+
"get": {
30303+
"tags": [
30304+
"User"
30305+
],
30306+
"operationId": "GetUserByIdCalculateStartNodes",
30307+
"parameters": [
30308+
{
30309+
"name": "id",
30310+
"in": "path",
30311+
"required": true,
30312+
"schema": {
30313+
"type": "string",
30314+
"format": "uuid"
30315+
}
30316+
}
30317+
],
30318+
"responses": {
30319+
"200": {
30320+
"description": "OK",
30321+
"content": {
30322+
"application/json": {
30323+
"schema": {
30324+
"oneOf": [
30325+
{
30326+
"$ref": "#/components/schemas/CalculatedUserStartNodesResponseModel"
30327+
}
30328+
]
30329+
}
30330+
}
30331+
}
30332+
},
30333+
"404": {
30334+
"description": "Not Found",
30335+
"content": {
30336+
"application/json": {
30337+
"schema": {
30338+
"oneOf": [
30339+
{
30340+
"$ref": "#/components/schemas/ProblemDetails"
30341+
}
30342+
]
30343+
}
30344+
}
30345+
}
30346+
},
30347+
"401": {
30348+
"description": "The resource is protected and requires an authentication token"
30349+
},
30350+
"403": {
30351+
"description": "The authenticated user do not have access to this resource"
30352+
}
30353+
},
30354+
"security": [
30355+
{
30356+
"Backoffice User": [ ]
30357+
}
30358+
]
30359+
}
30360+
},
3030130361
"/umbraco/management/api/v1/user/{id}/change-password": {
3030230362
"post": {
3030330363
"tags": [
@@ -33447,6 +33507,51 @@
3344733507
},
3344833508
"additionalProperties": false
3344933509
},
33510+
"CalculatedUserStartNodesResponseModel": {
33511+
"required": [
33512+
"documentStartNodeIds",
33513+
"hasDocumentRootAccess",
33514+
"hasMediaRootAccess",
33515+
"id",
33516+
"mediaStartNodeIds"
33517+
],
33518+
"type": "object",
33519+
"properties": {
33520+
"id": {
33521+
"type": "string",
33522+
"format": "uuid"
33523+
},
33524+
"documentStartNodeIds": {
33525+
"uniqueItems": true,
33526+
"type": "array",
33527+
"items": {
33528+
"oneOf": [
33529+
{
33530+
"$ref": "#/components/schemas/ReferenceByIdModel"
33531+
}
33532+
]
33533+
}
33534+
},
33535+
"hasDocumentRootAccess": {
33536+
"type": "boolean"
33537+
},
33538+
"mediaStartNodeIds": {
33539+
"uniqueItems": true,
33540+
"type": "array",
33541+
"items": {
33542+
"oneOf": [
33543+
{
33544+
"$ref": "#/components/schemas/ReferenceByIdModel"
33545+
}
33546+
]
33547+
}
33548+
},
33549+
"hasMediaRootAccess": {
33550+
"type": "boolean"
33551+
}
33552+
},
33553+
"additionalProperties": false
33554+
},
3345033555
"ChangePasswordCurrentUserRequestModel": {
3345133556
"required": [
3345233557
"newPassword"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace Umbraco.Cms.Api.Management.ViewModels.User;
2+
3+
public class CalculatedUserStartNodesResponseModel
4+
{
5+
public required Guid Id { get; init; }
6+
7+
public ISet<ReferenceByIdModel> DocumentStartNodeIds { get; set; } = new HashSet<ReferenceByIdModel>();
8+
9+
public bool HasDocumentRootAccess { get; set; }
10+
11+
public ISet<ReferenceByIdModel> MediaStartNodeIds { get; set; } = new HashSet<ReferenceByIdModel>();
12+
13+
public bool HasMediaRootAccess { get; set; }
14+
}

0 commit comments

Comments
 (0)