diff --git a/src/Umbraco.Cms.Api.Management/Configuration/ConfigureUmbracoManagementApiSwaggerGenOptions.cs b/src/Umbraco.Cms.Api.Management/Configuration/ConfigureUmbracoManagementApiSwaggerGenOptions.cs index ddef53dac6..b92665a3f2 100644 --- a/src/Umbraco.Cms.Api.Management/Configuration/ConfigureUmbracoManagementApiSwaggerGenOptions.cs +++ b/src/Umbraco.Cms.Api.Management/Configuration/ConfigureUmbracoManagementApiSwaggerGenOptions.cs @@ -33,7 +33,7 @@ public class ConfigureUmbracoManagementApiSwaggerGenOptions : IConfigureOptions< swaggerGenOptions.SelectSubTypesUsing(_umbracoJsonTypeInfoResolver.FindSubTypes); swaggerGenOptions.AddSecurityDefinition( - "Backoffice User", + ManagementApiConfiguration.ApiSecurityName, new OpenApiSecurityScheme { In = ParameterLocation.Header, @@ -51,17 +51,7 @@ public class ConfigureUmbracoManagementApiSwaggerGenOptions : IConfigureOptions< } }); - swaggerGenOptions.AddSecurityRequirement(new OpenApiSecurityRequirement - { - // this weird looking construct works because OpenApiSecurityRequirement - // is a specialization of Dictionary<,> - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference { Id = "OAuth", Type = ReferenceType.SecurityScheme } - }, - new List() - } - }); + // Sets Security requirement on backoffice apis + swaggerGenOptions.OperationFilter(); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Install/InstallControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/Install/InstallControllerBase.cs index 52fcd0e62e..7c597e1a13 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Install/InstallControllerBase.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Install/InstallControllerBase.cs @@ -1,10 +1,12 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core; using Umbraco.Cms.Api.Management.Filters; using Umbraco.Cms.Api.Management.Routing; namespace Umbraco.Cms.Api.Management.Controllers.Install; +[AllowAnonymous] [ApiController] [VersionedApiBackOfficeRoute("install")] [ApiExplorerSettings(GroupName = "Install")] diff --git a/src/Umbraco.Cms.Api.Management/Controllers/ManagementApiControllerBase.cs b/src/Umbraco.Cms.Api.Management/Controllers/ManagementApiControllerBase.cs index f58c0e1a8e..be45e4b812 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/ManagementApiControllerBase.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/ManagementApiControllerBase.cs @@ -1,13 +1,16 @@ using System.Linq.Expressions; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Api.Common.Attributes; using Umbraco.Cms.Api.Common.Filters; using Umbraco.Cms.Api.Management.DependencyInjection; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Web.Common.Authorization; namespace Umbraco.Cms.Api.Management.Controllers; +[Authorize(Policy = "New" + AuthorizationPolicies.BackOfficeAccess)] [MapToApi(ManagementApiConfiguration.ApiName)] [JsonOptionsName(Constants.JsonOptionsNames.BackOffice)] public class ManagementApiControllerBase : Controller @@ -44,6 +47,6 @@ public class ManagementApiControllerBase : Controller protected static Guid CurrentUserKey(IBackOfficeSecurityAccessor backOfficeSecurityAccessor) { //FIXME - Throw if no current user, when we are able to get the current user - return backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Key ?? Core.Constants.Security.SuperUserKey; + return backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Key ?? throw new InvalidOperationException("No backoffice user found"); } } diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs index 9797d0f62d..f13e589e40 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Security/BackOfficeController.cs @@ -2,6 +2,7 @@ using Asp.Versioning; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; @@ -44,6 +45,7 @@ public class BackOfficeController : ManagementApiControllerBase // FIXME: this is a temporary solution to get the new backoffice auth rolling. // once the old backoffice auth is no longer necessary, clean this up and merge with 2FA handling etc. + [AllowAnonymous] [HttpPost("login")] [MapToApiVersion("1.0")] public async Task Login(LoginRequestModel model) @@ -68,6 +70,7 @@ public class BackOfficeController : ManagementApiControllerBase public required string Password { get; init; } } + [AllowAnonymous] [HttpGet("authorize")] [MapToApiVersion("1.0")] public async Task Authorize() diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Server/StatusServerController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Server/StatusServerController.cs index 47e4d424ee..54feb9b3ed 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Server/StatusServerController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Server/StatusServerController.cs @@ -1,4 +1,5 @@ using Asp.Versioning; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Umbraco.Cms.Core.Services; @@ -13,6 +14,7 @@ public class StatusServerController : ServerControllerBase public StatusServerController(IRuntimeState runtimeState) => _runtimeState = runtimeState; + [AllowAnonymous] [HttpGet("status")] [MapToApiVersion("1.0")] [ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)] diff --git a/src/Umbraco.Cms.Api.Management/Controllers/User/Current/GetCurrentUserController.cs b/src/Umbraco.Cms.Api.Management/Controllers/User/Current/GetCurrentUserController.cs new file mode 100644 index 0000000000..6e70c7e2e4 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/User/Current/GetCurrentUserController.cs @@ -0,0 +1,46 @@ +using Asp.Versioning; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Management.Factories; +using Umbraco.Cms.Api.Management.ViewModels.User.Current; +using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Core.Services; + +namespace Umbraco.Cms.Api.Management.Controllers.User.Current; + +[ApiVersion("1.0")] +public class GetCurrentUserController : CurrentUserControllerBase +{ + private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; + private readonly IUserService _userService; + private readonly IUserPresentationFactory _userPresentationFactory; + + public GetCurrentUserController( + IBackOfficeSecurityAccessor backOfficeSecurityAccessor, + IUserService userService, + IUserPresentationFactory userPresentationFactory) + { + _backOfficeSecurityAccessor = backOfficeSecurityAccessor; + _userService = userService; + _userPresentationFactory = userPresentationFactory; + } + + [MapToApiVersion("1.0")] + [HttpGet] + [ProducesResponseType(typeof(CurrentUserResponseModel), StatusCodes.Status200OK)] + public async Task GetCurrentUser() + { + var currentUserKey = CurrentUserKey(_backOfficeSecurityAccessor); + + IUser? user = await _userService.GetAsync(currentUserKey); + + if (user is null) + { + return Unauthorized(); + } + + var responseModel = await _userPresentationFactory.CreateCurrentUserResponseModelAsync(user); + return Ok(responseModel); + } +} diff --git a/src/Umbraco.Cms.Api.Management/Controllers/User/Current/GetPermissionsCurrentUserController.cs b/src/Umbraco.Cms.Api.Management/Controllers/User/Current/GetPermissionsCurrentUserController.cs new file mode 100644 index 0000000000..9e29577c91 --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/Controllers/User/Current/GetPermissionsCurrentUserController.cs @@ -0,0 +1,39 @@ +using Asp.Versioning; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Api.Management.ViewModels.User.Current; +using Umbraco.Cms.Core.Mapping; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Security; +using Umbraco.Cms.Core.Services; + +namespace Umbraco.Cms.Api.Management.Controllers.User.Current; + +[ApiVersion("1.0")] +public class GetPermissionsCurrentUserController : CurrentUserControllerBase +{ + private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; + private readonly IUserService _userService; + private readonly IUmbracoMapper _mapper; + + public GetPermissionsCurrentUserController( + IBackOfficeSecurityAccessor backOfficeSecurityAccessor, + IUserService userService, + IUmbracoMapper mapper) + { + _backOfficeSecurityAccessor = backOfficeSecurityAccessor; + _userService = userService; + _mapper = mapper; + } + + [MapToApiVersion("1.0")] + [HttpGet("permissions")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public async Task GetPermissions([FromQuery(Name = "id")] HashSet ids) + { + IEnumerable permissions = await _userService.GetPermissionsAsync(CurrentUserKey(_backOfficeSecurityAccessor), ids); + List viewmodels = _mapper.MapEnumerable(permissions); + + return Ok(new UserPermissionsResponseModel { Permissions = viewmodels }); + } +} diff --git a/src/Umbraco.Cms.Api.Management/DependencyInjection/BackOfficeAuthBuilderExtensions.cs b/src/Umbraco.Cms.Api.Management/DependencyInjection/BackOfficeAuthBuilderExtensions.cs index 56c2b89bfc..8ed635ad38 100644 --- a/src/Umbraco.Cms.Api.Management/DependencyInjection/BackOfficeAuthBuilderExtensions.cs +++ b/src/Umbraco.Cms.Api.Management/DependencyInjection/BackOfficeAuthBuilderExtensions.cs @@ -173,11 +173,16 @@ public static class BackOfficeAuthBuilderExtensions }); } + options.AddPolicy($"New{AuthorizationPolicies.BackOfficeAccess}", policy => + { + policy.AuthenticationSchemes.Add(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme); + policy.RequireAuthenticatedUser(); + }); // NOTE: these are ONLY sample policies that allow us to test the new management APIs AddPolicy(AuthorizationPolicies.SectionAccessContent, Constants.Security.AllowedApplicationsClaimType, Constants.Applications.Content); AddPolicy(AuthorizationPolicies.SectionAccessForContentTree, Constants.Security.AllowedApplicationsClaimType, Constants.Applications.Content); AddPolicy(AuthorizationPolicies.SectionAccessForMediaTree, Constants.Security.AllowedApplicationsClaimType, Constants.Applications.Media); AddPolicy(AuthorizationPolicies.SectionAccessMedia, Constants.Security.AllowedApplicationsClaimType, Constants.Applications.Media); AddPolicy(AuthorizationPolicies.SectionAccessContentOrMedia, Constants.Security.AllowedApplicationsClaimType, Constants.Applications.Content, Constants.Applications.Media); - } + } } diff --git a/src/Umbraco.Cms.Api.Management/DependencyInjection/ManagementApiConfiguration.cs b/src/Umbraco.Cms.Api.Management/DependencyInjection/ManagementApiConfiguration.cs index 06cf731861..613185a285 100644 --- a/src/Umbraco.Cms.Api.Management/DependencyInjection/ManagementApiConfiguration.cs +++ b/src/Umbraco.Cms.Api.Management/DependencyInjection/ManagementApiConfiguration.cs @@ -4,6 +4,7 @@ namespace Umbraco.Cms.Api.Management.DependencyInjection; internal static class ManagementApiConfiguration { + internal const string ApiSecurityName = "Backoffice User"; internal const string ApiTitle = "Umbraco Management API"; internal const string ApiName = "management"; diff --git a/src/Umbraco.Cms.Api.Management/Factories/IUserGroupPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/IUserGroupPresentationFactory.cs index cf36bba6ed..3044c3248b 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/IUserGroupPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/IUserGroupPresentationFactory.cs @@ -24,6 +24,13 @@ public interface IUserGroupPresentationFactory /// Task> CreateMultipleAsync(IEnumerable userGroups); + /// + /// Creates multiple base on multiple + /// + /// + /// + Task> CreateMultipleAsync(IEnumerable userGroups); + /// /// Creates an based on a /// diff --git a/src/Umbraco.Cms.Api.Management/Factories/IUserPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/IUserPresentationFactory.cs index bff6218318..d58f62323b 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/IUserPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/IUserPresentationFactory.cs @@ -1,4 +1,5 @@ using Umbraco.Cms.Api.Management.ViewModels.User; +using Umbraco.Cms.Api.Management.ViewModels.User.Current; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; @@ -13,4 +14,6 @@ public interface IUserPresentationFactory Task CreateInviteModelAsync(InviteUserRequestModel requestModel); Task CreateUpdateModelAsync(Guid existingUserKey, UpdateUserRequestModel updateModel); + + Task CreateCurrentUserResponseModelAsync(IUser user); } diff --git a/src/Umbraco.Cms.Api.Management/Factories/UserGroupPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/UserGroupPresentationFactory.cs index 9f5cdbec5e..ac0708a6d1 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/UserGroupPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/UserGroupPresentationFactory.cs @@ -53,6 +53,33 @@ public class UserGroupPresentationFactory : IUserGroupPresentationFactory Sections = userGroup.AllowedSections.Select(SectionMapper.GetName), }; } + /// + public async Task CreateAsync(IReadOnlyUserGroup userGroup) + { + // TODO figure out how to reuse code from Task CreateAsync(IUserGroup userGroup) instead of copying + Guid? contentStartNodeKey = GetKeyFromId(userGroup.StartContentId, UmbracoObjectTypes.Document); + Guid? mediaStartNodeKey = GetKeyFromId(userGroup.StartMediaId, UmbracoObjectTypes.Media); + Attempt, UserGroupOperationStatus> languageIsoCodesMappingAttempt = await MapLanguageIdsToIsoCodeAsync(userGroup.AllowedLanguages); + + // We've gotten this data from the database, so the mapping should not fail + if (languageIsoCodesMappingAttempt.Success is false) + { + throw new InvalidOperationException($"Unknown language ID in User Group: {userGroup.Name}"); + } + + return new UserGroupResponseModel + { + Name = userGroup.Name ?? string.Empty, + Id = userGroup.Key, + DocumentStartNodeId = contentStartNodeKey, + MediaStartNodeId = mediaStartNodeKey, + Icon = userGroup.Icon, + Languages = languageIsoCodesMappingAttempt.Result, + HasAccessToAllLanguages = userGroup.HasAccessToAllLanguages, + Permissions = userGroup.PermissionNames, + Sections = userGroup.AllowedSections.Select(SectionMapper.GetName), + }; + } /// public async Task> CreateMultipleAsync(IEnumerable userGroups) @@ -65,6 +92,17 @@ public class UserGroupPresentationFactory : IUserGroupPresentationFactory return userGroupViewModels; } + /// + public async Task> CreateMultipleAsync(IEnumerable userGroups) + { + var userGroupViewModels = new List(); + foreach (IReadOnlyUserGroup userGroup in userGroups) + { + userGroupViewModels.Add(await CreateAsync(userGroup)); + } + + return userGroupViewModels; + } /// public async Task> CreateAsync(CreateUserGroupRequestModel requestModel) @@ -180,6 +218,10 @@ public class UserGroupPresentationFactory : IUserGroupPresentationFactory target.StartContentId = contentId; } + else + { + target.StartContentId = null; + } if (source.MediaStartNodeId is not null) { @@ -192,6 +234,10 @@ public class UserGroupPresentationFactory : IUserGroupPresentationFactory target.StartMediaId = mediaId; } + else + { + target.StartMediaId = null; + } return Attempt.Succeed(UserGroupOperationStatus.Success); } diff --git a/src/Umbraco.Cms.Api.Management/Factories/UserPresentationFactory.cs b/src/Umbraco.Cms.Api.Management/Factories/UserPresentationFactory.cs index 0a51c1d6a9..955cd2dbf4 100644 --- a/src/Umbraco.Cms.Api.Management/Factories/UserPresentationFactory.cs +++ b/src/Umbraco.Cms.Api.Management/Factories/UserPresentationFactory.cs @@ -1,4 +1,6 @@ using Umbraco.Cms.Api.Management.ViewModels.User; +using Umbraco.Cms.Api.Management.ViewModels.User.Current; +using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Media; @@ -10,21 +12,25 @@ namespace Umbraco.Cms.Api.Management.Factories; public class UserPresentationFactory : IUserPresentationFactory { + private readonly IEntityService _entityService; private readonly AppCaches _appCaches; private readonly MediaFileManager _mediaFileManager; private readonly IImageUrlGenerator _imageUrlGenerator; + private readonly IUserGroupPresentationFactory _userGroupPresentationFactory; public UserPresentationFactory( IEntityService entityService, AppCaches appCaches, MediaFileManager mediaFileManager, - IImageUrlGenerator imageUrlGenerator) + IImageUrlGenerator imageUrlGenerator, + IUserGroupPresentationFactory userGroupPresentationFactory) { _entityService = entityService; _appCaches = appCaches; _mediaFileManager = mediaFileManager; _imageUrlGenerator = imageUrlGenerator; + _userGroupPresentationFactory = userGroupPresentationFactory; } public UserResponseModel CreateResponseModel(IUser user) @@ -97,7 +103,34 @@ public class UserPresentationFactory : IUserPresentationFactory return model; } - private SortedSet GetKeysFromIds(IEnumerable? ids, UmbracoObjectTypes type) + public async Task CreateCurrentUserResponseModelAsync(IUser user) + { + var presentationUser = CreateResponseModel(user); + var presentationGroups = await _userGroupPresentationFactory.CreateMultipleAsync(user.Groups); + var languages = presentationGroups.SelectMany(x => x.Languages).Distinct().ToArray(); + var mediaStartNodeKeys = GetKeysFromIds(user.CalculateMediaStartNodeIds(_entityService, _appCaches), UmbracoObjectTypes.Media); + var documentStartNodeKeys = GetKeysFromIds(user.CalculateContentStartNodeIds(_entityService, _appCaches), UmbracoObjectTypes.Document); + + var permissions = presentationGroups.SelectMany(x => x.Permissions).Distinct().ToHashSet(); + var hasAccessToAllLanguages = presentationGroups.Any(x => x.HasAccessToAllLanguages); + + return await Task.FromResult(new CurrentUserResponseModel() + { + Id = presentationUser.Id, + Email = presentationUser.Email, + Name = presentationUser.Name, + UserName = presentationUser.UserName, + Languages = languages, + AvatarUrls = presentationUser.AvatarUrls, + LanguageIsoCode = presentationUser.LanguageIsoCode, + MediaStartNodeIds = mediaStartNodeKeys, + ContentStartNodeIds = documentStartNodeKeys, + Permissions = permissions, + HasAccessToAllLanguages = hasAccessToAllLanguages + }); + } + + private ISet GetKeysFromIds(IEnumerable? ids, UmbracoObjectTypes type) { IEnumerable? keys = ids? .Select(x => _entityService.GetKey(x, type)) @@ -105,7 +138,9 @@ public class UserPresentationFactory : IUserPresentationFactory .Select(x => x.Result); return keys is null - ? new SortedSet() - : new SortedSet(keys); + ? new HashSet() + : new HashSet(keys); } + + } diff --git a/src/Umbraco.Cms.Api.Management/OpenApi.json b/src/Umbraco.Cms.Api.Management/OpenApi.json index a7326e5eb5..bfc6421358 100644 --- a/src/Umbraco.Cms.Api.Management/OpenApi.json +++ b/src/Umbraco.Cms.Api.Management/OpenApi.json @@ -58,7 +58,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/audit-log/{id}": { @@ -122,7 +127,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/audit-log/type/{logType}": { @@ -178,7 +188,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/culture": { @@ -218,7 +233,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/data-type": { @@ -267,7 +287,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/data-type/{id}": { @@ -305,7 +330,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "delete": { "tags": [ @@ -340,7 +370,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "put": { "tags": [ @@ -388,7 +423,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/data-type/{id}/copy": { @@ -438,7 +478,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/data-type/{id}/is-used": { @@ -472,7 +517,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/data-type/{id}/move": { @@ -512,7 +562,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/data-type/{id}/references": { @@ -553,7 +608,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/data-type/folder": { @@ -589,7 +649,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/data-type/folder/{id}": { @@ -627,7 +692,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "delete": { "tags": [ @@ -652,7 +722,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "put": { "tags": [ @@ -690,7 +765,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/data-type/item": { @@ -731,7 +811,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/data-type/children": { @@ -787,7 +872,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/data-type/root": { @@ -835,7 +925,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/dictionary": { @@ -875,7 +970,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "post": { "tags": [ @@ -932,7 +1032,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/dictionary/{id}": { @@ -970,7 +1075,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "delete": { "tags": [ @@ -1005,7 +1115,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "put": { "tags": [ @@ -1053,7 +1168,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/dictionary/{id}/export": { @@ -1096,7 +1216,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/dictionary/{id}/move": { @@ -1146,7 +1271,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/dictionary/import": { @@ -1195,7 +1325,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/dictionary/item": { @@ -1236,7 +1371,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/dictionary/children": { @@ -1284,7 +1424,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/dictionary/root": { @@ -1324,7 +1469,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/document-blueprint/item": { @@ -1365,7 +1515,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/document-blueprint/root": { @@ -1405,7 +1560,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/document-type/{id}": { @@ -1443,7 +1603,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/document-type/item": { @@ -1484,7 +1649,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/document-type/children": { @@ -1540,7 +1710,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/document-type/root": { @@ -1588,7 +1763,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/document": { @@ -1630,7 +1810,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/document/{id}": { @@ -1668,7 +1853,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "delete": { "tags": [ @@ -1696,7 +1886,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "put": { "tags": [ @@ -1737,7 +1932,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/document/{id}/copy": { @@ -1790,7 +1990,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/document/{id}/domains": { @@ -1814,7 +2019,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "put": { "tags": [ @@ -1849,7 +2059,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/document/{id}/move": { @@ -1892,7 +2107,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/document/{id}/notifications": { @@ -1933,7 +2153,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "put": { "tags": [ @@ -1971,7 +2196,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/document/item": { @@ -2027,7 +2257,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/recycle-bin/document/children": { @@ -2078,7 +2313,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/recycle-bin/document/root": { @@ -2121,7 +2361,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/document/children": { @@ -2184,7 +2429,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/document/root": { @@ -2239,7 +2489,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/health-check-group": { @@ -2279,7 +2534,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/health-check-group/{name}": { @@ -2316,7 +2576,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/health-check-group/{name}/check": { @@ -2353,7 +2618,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/health-check/execute-action": { @@ -2400,7 +2670,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/help": { @@ -2470,7 +2745,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/indexer": { @@ -2508,7 +2788,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/indexer/{indexName}": { @@ -2552,7 +2837,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/indexer/{indexName}/rebuild": { @@ -2592,7 +2882,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/install/settings": { @@ -2758,7 +3053,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "post": { "tags": [ @@ -2805,7 +3105,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/language/{isoCode}": { @@ -2842,7 +3147,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "delete": { "tags": [ @@ -2883,7 +3193,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "put": { "tags": [ @@ -2930,7 +3245,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/language/item": { @@ -2970,7 +3290,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/log-viewer/level": { @@ -3010,7 +3335,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/log-viewer/level-count": { @@ -3062,7 +3392,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/log-viewer/log": { @@ -3142,7 +3477,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/log-viewer/message-template": { @@ -3208,7 +3548,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/log-viewer/saved-search": { @@ -3248,7 +3593,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "post": { "tags": [ @@ -3292,7 +3642,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/log-viewer/saved-search/{name}": { @@ -3329,7 +3684,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "delete": { "tags": [ @@ -3353,7 +3713,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/log-viewer/validate-logs-size": { @@ -3394,7 +3759,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/media-type/{id}": { @@ -3432,7 +3802,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/media-type/item": { @@ -3473,7 +3848,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/media-type/children": { @@ -3529,7 +3909,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/media-type/root": { @@ -3577,7 +3962,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/media": { @@ -3619,7 +4009,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/media/{id}": { @@ -3657,7 +4052,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "delete": { "tags": [ @@ -3685,7 +4085,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "put": { "tags": [ @@ -3726,7 +4131,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/media/{id}/move": { @@ -3769,7 +4179,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/media/item": { @@ -3818,7 +4233,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/recycle-bin/media/children": { @@ -3869,7 +4289,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/recycle-bin/media/root": { @@ -3912,7 +4337,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/media/children": { @@ -3968,7 +4398,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/media/item": { @@ -4019,7 +4454,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/media/root": { @@ -4067,7 +4507,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/member-group/item": { @@ -4108,7 +4553,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/member-group/root": { @@ -4148,7 +4598,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/member-type/item": { @@ -4189,7 +4644,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/member-type/root": { @@ -4229,7 +4689,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/member/item": { @@ -4270,7 +4735,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/models-builder/build": { @@ -4293,7 +4763,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/models-builder/dashboard": { @@ -4317,7 +4792,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/models-builder/status": { @@ -4341,7 +4821,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/object-types": { @@ -4381,7 +4866,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/package/{name}/run-migration": { @@ -4417,7 +4907,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/package/created": { @@ -4457,7 +4952,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "post": { "tags": [ @@ -4504,7 +5004,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/package/created/{id}": { @@ -4542,7 +5047,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "delete": { "tags": [ @@ -4567,7 +5077,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "put": { "tags": [ @@ -4605,7 +5120,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/package/created/{id}/download": { @@ -4640,7 +5160,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/package/manifest": { @@ -4667,7 +5192,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/package/migration-status": { @@ -4707,7 +5237,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/partial-view": { @@ -4740,7 +5275,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "post": { "tags": [ @@ -4774,7 +5314,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "delete": { "tags": [ @@ -4794,7 +5339,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "put": { "tags": [ @@ -4818,7 +5368,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/partial-view/folder": { @@ -4840,7 +5395,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "post": { "tags": [ @@ -4864,7 +5424,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "delete": { "tags": [ @@ -4884,7 +5449,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/partial-view/item": { @@ -4924,7 +5494,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/partial-view/snippet": { @@ -4964,7 +5539,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/partial-view/snippet/{name}": { @@ -4998,7 +5578,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/partial-view/children": { @@ -5045,7 +5630,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/partial-view/root": { @@ -5085,7 +5675,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/profiling/status": { @@ -5109,7 +5704,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "put": { "tags": [ @@ -5133,7 +5733,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/property-type/is-used": { @@ -5180,7 +5785,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/published-cache/collect": { @@ -5193,7 +5803,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/published-cache/rebuild": { @@ -5206,7 +5821,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/published-cache/reload": { @@ -5219,7 +5839,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/published-cache/status": { @@ -5239,7 +5864,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/redirect-management": { @@ -5294,7 +5924,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/redirect-management/{id}": { @@ -5341,7 +5976,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "delete": { "tags": [ @@ -5363,7 +6003,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/redirect-management/status": { @@ -5387,7 +6032,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "post": { "tags": [ @@ -5407,7 +6057,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/relation-type": { @@ -5453,7 +6108,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/relation-type/{id}": { @@ -5491,7 +6151,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "delete": { "tags": [ @@ -5523,7 +6188,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "put": { "tags": [ @@ -5589,7 +6259,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/relation-type/item": { @@ -5630,7 +6305,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/relation-type/root": { @@ -5670,7 +6350,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/relation/{id}": { @@ -5708,7 +6393,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/relation/child-relation/{childId}": { @@ -5763,7 +6453,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/relation/type/{id}": { @@ -5812,7 +6507,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/script": { @@ -5845,7 +6545,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "post": { "tags": [ @@ -5879,7 +6584,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "delete": { "tags": [ @@ -5899,7 +6609,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "put": { "tags": [ @@ -5923,7 +6638,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/script/folder": { @@ -5945,7 +6665,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "post": { "tags": [ @@ -5969,7 +6694,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "delete": { "tags": [ @@ -5989,7 +6719,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/script/item": { @@ -6029,7 +6764,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/script/children": { @@ -6076,7 +6816,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/script/root": { @@ -6116,7 +6861,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/searcher": { @@ -6154,7 +6904,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/searcher/{searcherName}/query": { @@ -6217,7 +6972,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/security/back-office/authorize": { @@ -6324,7 +7084,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/static-file/item": { @@ -6364,7 +7129,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/static-file/children": { @@ -6411,7 +7181,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/static-file/root": { @@ -6451,7 +7226,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/stylesheet": { @@ -6484,7 +7264,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "post": { "tags": [ @@ -6518,7 +7303,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "delete": { "tags": [ @@ -6538,7 +7328,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "put": { "tags": [ @@ -6562,7 +7357,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/stylesheet/all": { @@ -6602,7 +7402,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/stylesheet/folder": { @@ -6624,7 +7429,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "post": { "tags": [ @@ -6648,7 +7458,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "delete": { "tags": [ @@ -6668,7 +7483,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/stylesheet/item": { @@ -6708,7 +7528,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/stylesheet/rich-text/extract-rules": { @@ -6745,7 +7570,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/stylesheet/rich-text/interpolate-rules": { @@ -6782,7 +7612,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/stylesheet/rich-text/rules": { @@ -6818,7 +7653,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/stylesheet/children": { @@ -6865,7 +7705,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/stylesheet/root": { @@ -6905,7 +7750,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tag": { @@ -6966,7 +7816,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/telemetry": { @@ -7004,7 +7859,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/telemetry/level": { @@ -7028,7 +7888,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "post": { "tags": [ @@ -7062,7 +7927,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/template": { @@ -7111,7 +7981,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/template/{id}": { @@ -7149,7 +8024,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "delete": { "tags": [ @@ -7184,7 +8064,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "put": { "tags": [ @@ -7232,7 +8117,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/template/item": { @@ -7273,7 +8163,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/template/query/execute": { @@ -7310,7 +8205,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/template/query/settings": { @@ -7334,7 +8234,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/template/scaffold": { @@ -7361,7 +8266,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/template/children": { @@ -7409,7 +8319,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tree/template/root": { @@ -7449,7 +8364,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/temporaryfile": { @@ -7509,7 +8429,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/temporaryfile/{id}": { @@ -7564,7 +8489,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "delete": { "tags": [ @@ -7606,7 +8536,70 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] + } + }, + "/umbraco/management/api/v1/tour": { + "get": { + "tags": [ + "Tour" + ], + "operationId": "GetTour", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/UserTourStatusesResponseModel" + } + ] + } + } + } + } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] + }, + "post": { + "tags": [ + "Tour" + ], + "operationId": "PostTour", + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/SetTourStatusRequestModel" + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "Success" + } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tracked-reference/{id}": { @@ -7663,7 +8656,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tracked-reference/descendants/{parentId}": { @@ -7718,7 +8716,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/tracked-reference/item": { @@ -7778,7 +8781,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/upgrade/authorize": { @@ -7811,7 +8819,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/upgrade/settings": { @@ -7845,7 +8858,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/user-group": { @@ -7891,7 +8909,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "get": { "tags": [ @@ -7929,7 +8952,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/user-group/{id}": { @@ -7967,7 +8995,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "delete": { "tags": [ @@ -7992,7 +9025,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "put": { "tags": [ @@ -8030,7 +9068,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/user-group/item": { @@ -8071,7 +9114,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/user": { @@ -8121,7 +9169,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "get": { "tags": [ @@ -8159,7 +9212,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/user/{id}": { @@ -8197,7 +9255,12 @@ "404": { "description": "Not Found" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "delete": { "tags": [ @@ -8219,7 +9282,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "put": { "tags": [ @@ -8254,7 +9322,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/user/avatar/{id}": { @@ -8278,7 +9351,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] }, "post": { "tags": [ @@ -8323,7 +9401,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/user/change-password/{id}": { @@ -8360,7 +9443,299 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] + } + }, + "/umbraco/management/api/v1/user/current": { + "get": { + "tags": [ + "User" + ], + "operationId": "GetUserCurrent", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/CurrentUserResponseModel" + } + ] + } + } + } + } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] + } + }, + "/umbraco/management/api/v1/user/current/avatar": { + "post": { + "tags": [ + "User" + ], + "operationId": "PostUserCurrentAvatar", + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/SetAvatarRequestModel" + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "Success" + } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] + } + }, + "/umbraco/management/api/v1/user/current/change-password": { + "post": { + "tags": [ + "User" + ], + "operationId": "PostUserCurrentChangePassword", + "requestBody": { + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/ChangePasswordUserRequestModel" + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "Success" + } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] + } + }, + "/umbraco/management/api/v1/user/current/data": { + "get": { + "tags": [ + "User" + ], + "operationId": "GetUserCurrentData", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/UserDataResponseModel" + } + ] + } + } + } + } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] + } + }, + "/umbraco/management/api/v1/user/current/logins": { + "get": { + "tags": [ + "User" + ], + "operationId": "GetUserCurrentLogins", + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/LinkedLoginsRequestModel" + } + ] + } + } + } + } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] + } + }, + "/umbraco/management/api/v1/user/current/permissions": { + "get": { + "tags": [ + "User" + ], + "operationId": "GetUserCurrentPermissions", + "parameters": [ + { + "name": "id", + "in": "query", + "schema": { + "uniqueItems": true, + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/UserPermissionsResponseModel" + } + ] + } + } + } + } + } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] + } + }, + "/umbraco/management/api/v1/user/current/permissions/document": { + "get": { + "tags": [ + "User" + ], + "operationId": "GetUserCurrentPermissionsDocument", + "parameters": [ + { + "name": "id", + "in": "query", + "schema": { + "uniqueItems": true, + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/UserPermissionsResponseModel" + } + ] + } + } + } + } + } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] + } + }, + "/umbraco/management/api/v1/user/current/permissions/media": { + "get": { + "tags": [ + "User" + ], + "operationId": "GetUserCurrentPermissionsMedia", + "parameters": [ + { + "name": "id", + "in": "query", + "schema": { + "uniqueItems": true, + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + } + } + ], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/UserPermissionsResponseModel" + } + ] + } + } + } + } + } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/user/disable": { @@ -8396,7 +9771,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/user/enable": { @@ -8432,7 +9812,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/user/filter": { @@ -8510,7 +9895,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/user/invite": { @@ -8546,7 +9936,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/user/item": { @@ -8587,7 +9982,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/user/set-user-groups": { @@ -8613,7 +10013,12 @@ "200": { "description": "Success" } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } }, "/umbraco/management/api/v1/user/unlock": { @@ -8649,7 +10054,12 @@ } } } - } + }, + "security": [ + { + "Backoffice User": [ ] + } + ] } } }, @@ -9402,6 +10812,79 @@ }, "additionalProperties": false }, + "CurrentUserResponseModel": { + "required": [ + "$type" + ], + "type": "object", + "properties": { + "$type": { + "type": "string" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "email": { + "type": "string" + }, + "userName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "languageIsoCode": { + "type": "string", + "nullable": true + }, + "contentStartNodeIds": { + "uniqueItems": true, + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + }, + "mediaStartNodeIds": { + "uniqueItems": true, + "type": "array", + "items": { + "type": "string", + "format": "uuid" + } + }, + "avatarUrls": { + "type": "array", + "items": { + "type": "string" + } + }, + "languages": { + "type": "array", + "items": { + "type": "string" + } + }, + "hasAccessToAllLanguages": { + "type": "boolean" + }, + "permissions": { + "uniqueItems": true, + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "discriminator": { + "propertyName": "$type", + "mapping": { + "CurrentUserResponseModel": "#/components/schemas/CurrentUserResponseModel" + } + } + }, "DataTypeItemResponseModel": { "type": "object", "allOf": [ @@ -9772,6 +11255,10 @@ "icon": { "type": "string", "nullable": true + }, + "contentTypeId": { + "type": "string", + "format": "uuid" } }, "additionalProperties": false @@ -9836,6 +11323,20 @@ }, "isEdited": { "type": "boolean" + }, + "contentTypeId": { + "type": "string", + "format": "uuid" + }, + "variants": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/VariantTreeItemModel" + } + ] + } } }, "additionalProperties": false, @@ -10649,6 +12150,34 @@ }, "additionalProperties": false }, + "LinkedLoginModel": { + "type": "object", + "properties": { + "providerName": { + "type": "string" + }, + "providerKey": { + "type": "string" + } + }, + "additionalProperties": false + }, + "LinkedLoginsRequestModel": { + "type": "object", + "properties": { + "linkedLogins": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/LinkedLoginModel" + } + ] + } + } + }, + "additionalProperties": false + }, "LogLevelCountsReponseModel": { "type": "object", "properties": { @@ -12221,6 +13750,10 @@ "format": "uuid", "nullable": true }, + "sortOrder": { + "type": "integer", + "format": "int32" + }, "alias": { "type": "string" }, @@ -12279,6 +13812,16 @@ }, "additionalProperties": false }, + "PublishedStateModel": { + "enum": [ + "Published", + "Unpublished", + "Publishing", + "Unpublishing" + ], + "type": "integer", + "format": "int32" + }, "RecycleBinItemResponseModel": { "required": [ "$type" @@ -12675,6 +14218,15 @@ }, "additionalProperties": false }, + "SetTourStatusRequestModel": { + "type": "object", + "allOf": [ + { + "$ref": "#/components/schemas/TourStatusModel" + } + ], + "additionalProperties": false + }, "SnippetItemResponseModel": { "type": "object", "properties": { @@ -13093,6 +14645,21 @@ }, "additionalProperties": false }, + "TourStatusModel": { + "type": "object", + "properties": { + "alias": { + "type": "string" + }, + "completed": { + "type": "boolean" + }, + "disabled": { + "type": "boolean" + } + }, + "additionalProperties": false + }, "TreeItemPresentationModel": { "type": "object", "properties": { @@ -13415,6 +14982,34 @@ }, "additionalProperties": false }, + "UserDataModel": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "data": { + "type": "string" + } + }, + "additionalProperties": false + }, + "UserDataResponseModel": { + "type": "object", + "properties": { + "userData": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/UserDataModel" + } + ] + } + } + }, + "additionalProperties": false + }, "UserGroupBaseModel": { "type": "object", "properties": { @@ -13556,6 +15151,38 @@ "type": "integer", "format": "int32" }, + "UserPermissionModel": { + "type": "object", + "properties": { + "nodeKey": { + "type": "string", + "format": "uuid" + }, + "permissions": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false + }, + "UserPermissionsResponseModel": { + "type": "object", + "properties": { + "permissions": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/UserPermissionModel" + } + ] + } + } + }, + "additionalProperties": false + }, "UserPresentationBaseModel": { "type": "object", "properties": { @@ -13698,6 +15325,25 @@ "type": "integer", "format": "int32" }, + "UserTourStatusesResponseModel": { + "type": "object", + "properties": { + "tourStatuses": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/components/schemas/TourStatusModel" + }, + { + "$ref": "#/components/schemas/SetTourStatusRequestModel" + } + ] + } + } + }, + "additionalProperties": false + }, "ValueModelBaseModel": { "required": [ "$type" @@ -13801,6 +15447,22 @@ } } }, + "VariantTreeItemModel": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "culture": { + "type": "string", + "nullable": true + }, + "state": { + "$ref": "#/components/schemas/PublishedStateModel" + } + }, + "additionalProperties": false + }, "VersionResponseModel": { "type": "object", "properties": { @@ -13824,10 +15486,5 @@ } } } - }, - "security": [ - { - "OAuth": [ ] - } - ] + } } diff --git a/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilter.cs b/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilter.cs new file mode 100644 index 0000000000..4ae470704d --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/OpenApi/BackOfficeSecurityRequirementsOperationFilter.cs @@ -0,0 +1,42 @@ + +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using Umbraco.Cms.Api.Common.Attributes; +using Umbraco.Cms.Api.Management.DependencyInjection; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Api.Management.OpenApi; + +internal class BackOfficeSecurityRequirementsOperationFilter : IOperationFilter +{ + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + if (context.MethodInfo.HasMapToApiAttribute(ManagementApiConfiguration.ApiName) == false) + { + return; + } + + if (!context.MethodInfo.GetCustomAttributes(true).Any(x => x is AllowAnonymousAttribute) && + !(context.MethodInfo.DeclaringType?.GetCustomAttributes(true).Any(x => x is AllowAnonymousAttribute) ?? false)) + { + operation.Security = new List + { + new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme { + Reference = new OpenApiReference { + Type = ReferenceType.SecurityScheme, + Id = ManagementApiConfiguration.ApiSecurityName + } + }, new string[] { } + } + } + }; + } + } + + +} diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/User/Current/CurrentUserResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/User/Current/CurrentUserResponseModel.cs new file mode 100644 index 0000000000..dcabba86fe --- /dev/null +++ b/src/Umbraco.Cms.Api.Management/ViewModels/User/Current/CurrentUserResponseModel.cs @@ -0,0 +1,26 @@ +namespace Umbraco.Cms.Api.Management.ViewModels.User.Current; + +public class CurrentUserResponseModel : INamedEntityPresentationModel +{ + public required Guid Id { get; init; } + + public required string Email { get; init; } = string.Empty; + + public required string UserName { get; init; } = string.Empty; + + public required string Name { get; init; } = string.Empty; + + public required string? LanguageIsoCode { get; init; } + + public required ISet ContentStartNodeIds { get; init; } = new HashSet(); + + public required ISet MediaStartNodeIds { get; init; } = new HashSet(); + + public required IEnumerable AvatarUrls { get; init; } = Enumerable.Empty(); + + public required IEnumerable Languages { get; init; } = Enumerable.Empty(); + + public required bool HasAccessToAllLanguages { get; init; } + + public required ISet Permissions { get; init; } +} diff --git a/src/Umbraco.Cms.Api.Management/ViewModels/User/UserResponseModel.cs b/src/Umbraco.Cms.Api.Management/ViewModels/User/UserResponseModel.cs index 5e0d76a61d..1a5251227a 100644 --- a/src/Umbraco.Cms.Api.Management/ViewModels/User/UserResponseModel.cs +++ b/src/Umbraco.Cms.Api.Management/ViewModels/User/UserResponseModel.cs @@ -8,9 +8,9 @@ public class UserResponseModel : UserPresentationBase, INamedEntityPresentationM public string? LanguageIsoCode { get; set; } - public SortedSet ContentStartNodeIds { get; set; } = new(); + public ISet ContentStartNodeIds { get; set; } = new HashSet(); - public SortedSet MediaStartNodeIds { get; set; } = new(); + public ISet MediaStartNodeIds { get; set; } = new HashSet(); public IEnumerable AvatarUrls { get; set; } = Enumerable.Empty(); diff --git a/src/Umbraco.Core/Models/Membership/IReadOnlyUserGroup.cs b/src/Umbraco.Core/Models/Membership/IReadOnlyUserGroup.cs index 0064a7f454..e7095c9c8c 100644 --- a/src/Umbraco.Core/Models/Membership/IReadOnlyUserGroup.cs +++ b/src/Umbraco.Core/Models/Membership/IReadOnlyUserGroup.cs @@ -33,7 +33,7 @@ public interface IReadOnlyUserGroup /// flexible permissions structure in the future. /// IEnumerable? Permissions { get; set; } - + ISet PermissionNames { get; } IEnumerable AllowedSections { get; } IEnumerable AllowedLanguages => Enumerable.Empty(); diff --git a/src/Umbraco.Core/Models/Membership/ReadOnlyUserGroup.cs b/src/Umbraco.Core/Models/Membership/ReadOnlyUserGroup.cs index f7bc83d94d..9966b83d10 100644 --- a/src/Umbraco.Core/Models/Membership/ReadOnlyUserGroup.cs +++ b/src/Umbraco.Core/Models/Membership/ReadOnlyUserGroup.cs @@ -13,6 +13,7 @@ public class ReadOnlyUserGroup : IReadOnlyUserGroup, IEquatable allowedLanguages, IEnumerable allowedSections, IEnumerable? permissions, + ISet permissionNames, bool hasAccessToAllLanguages) { Name = name ?? string.Empty; @@ -28,6 +29,7 @@ public class ReadOnlyUserGroup : IReadOnlyUserGroup, IEquatable? Permissions { get; set; } public IEnumerable AllowedLanguages { get; private set; } + public ISet PermissionNames { get; private set; } public IEnumerable AllowedSections { get; private set; } public static bool operator ==(ReadOnlyUserGroup left, ReadOnlyUserGroup right) => Equals(left, right); diff --git a/src/Umbraco.Core/Models/Membership/UserGroupExtensions.cs b/src/Umbraco.Core/Models/Membership/UserGroupExtensions.cs index a9f3c70610..9b078cc814 100644 --- a/src/Umbraco.Core/Models/Membership/UserGroupExtensions.cs +++ b/src/Umbraco.Core/Models/Membership/UserGroupExtensions.cs @@ -25,6 +25,7 @@ public static class UserGroupExtensions group.AllowedLanguages, group.AllowedSections, group.Permissions, + group.PermissionNames, group.HasAccessToAllLanguages); } diff --git a/src/Umbraco.Core/Services/IUserService.cs b/src/Umbraco.Core/Services/IUserService.cs index 2942cdf45d..b33cded64b 100644 --- a/src/Umbraco.Core/Services/IUserService.cs +++ b/src/Umbraco.Core/Services/IUserService.cs @@ -203,6 +203,14 @@ public interface IUserService : IMembershipUserService /// Alias of the section to remove void DeleteSectionFromAllUserGroups(string sectionAlias); + /// + /// Get explicitly assigned permissions for a user and node keys. + /// + /// Key of user to retrieve permissions for. + /// The keys of the nodes to get permissions for. + /// An enumerable list of . + Task> GetPermissionsAsync(Guid userKey, IEnumerable nodeKeys); + /// /// Get explicitly assigned content permissions for a user and node keys. /// diff --git a/src/Umbraco.Core/Services/UserService.cs b/src/Umbraco.Core/Services/UserService.cs index 26f85c8f78..4ceed57104 100644 --- a/src/Umbraco.Core/Services/UserService.cs +++ b/src/Umbraco.Core/Services/UserService.cs @@ -1998,6 +1998,39 @@ internal class UserService : RepositoryService, IUserService return Attempt.Succeed?>(idKeys); } + /// + public async Task> GetPermissionsAsync(Guid userKey, IEnumerable nodeKeys) + { + using ICoreScope scope = ScopeProvider.CreateCoreScope(); + + IUser? user = await GetAsync(userKey); + + if (user is null) + { + throw new InvalidOperationException("No user with that ID"); + } + + Guid[] keys = nodeKeys.ToArray(); + if (keys.Length == 0) + { + return Enumerable.Empty(); + } + + // We don't know what the entity type may be, so we have to get the entire entity :( + var idKeyMap = keys.ToDictionary(key => _entityService.Get(key)!.Id); + + EntityPermissionCollection permissionCollection = _userGroupRepository.GetPermissions(user.Groups.ToArray(), true, idKeyMap.Keys.ToArray()); + + var results = new List(); + foreach (int nodeId in idKeyMap.Keys) + { + var permissions = permissionCollection.GetAllPermissions(nodeId).ToArray(); + results.Add(new NodePermissions { NodeKey = idKeyMap[nodeId], Permissions = permissions }); + } + + return results; + } + /// /// Get explicitly assigned permissions for a user and optional node ids /// diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_0_0/ResetCache.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_0_0/ResetCache.cs index a3ec96ad7e..bcdf782b2e 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_0_0/ResetCache.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_12_0_0/ResetCache.cs @@ -16,6 +16,7 @@ public class ResetCache : MigrationBase RebuildCache = true; var distCacheFolderAbsolutePath = _hostEnvironment.MapPathContentRoot(Constants.SystemDirectories.TempData + "/DistCache"); var nuCacheFolderAbsolutePath = _hostEnvironment.MapPathContentRoot(Constants.SystemDirectories.TempData + "/NuCache"); + DeleteAllFilesInFolder(distCacheFolderAbsolutePath); DeleteAllFilesInFolder(nuCacheFolderAbsolutePath); } diff --git a/src/Umbraco.Infrastructure/Persistence/Factories/UserFactory.cs b/src/Umbraco.Infrastructure/Persistence/Factories/UserFactory.cs index 4051c0d198..ebedc57b35 100644 --- a/src/Umbraco.Infrastructure/Persistence/Factories/UserFactory.cs +++ b/src/Umbraco.Infrastructure/Persistence/Factories/UserFactory.cs @@ -132,6 +132,7 @@ internal static class UserFactory group.UserGroup2LanguageDtos.Select(x => x.LanguageId), group.UserGroup2AppDtos.Select(x => x.AppAlias).WhereNotNull().ToArray(), permissions, + group.UserGroup2PermissionDtos.Select(x => x.Permission).ToHashSet(), group.HasAccessToAllLanguages); } } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs index b2e2ca5847..2d27843486 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/UserRepository.cs @@ -485,6 +485,25 @@ SELECT 4 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userDisabled = 0 groups2languages = new Dictionary>(); } + // get groups2permissions + sql = SqlContext.Sql() + .Select() + .From() + .WhereIn(x => x.UserGroupId, groupIds); + + Dictionary> groups2permissions; + try + { + groups2permissions = Database.Fetch(sql) + .GroupBy(x => x.UserGroupId) + .ToDictionary(x => x.Key, x => x); + } + catch + { + // If we get an error, the table has not been made in the database yet, set the list to an empty one + groups2permissions = new Dictionary>(); + } + // map groups foreach (User2UserGroupDto? user2Group in user2Groups) @@ -524,6 +543,16 @@ SELECT 4 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userDisabled = 0 group.UserGroup2LanguageDtos = list.ToList(); // groups2apps is distinct } } + + // map group permissions + foreach (UserGroupDto? group in groups.Values) + { + if (groups2permissions.TryGetValue(group.Id, out IGrouping? list)) + { + group.UserGroup2PermissionDtos = list.ToList(); // groups2apps is distinct + } + + } } #endregion diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Editors/UserEditorAuthorizationHelperTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Editors/UserEditorAuthorizationHelperTests.cs index fd9fd1ca05..d040afb137 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Editors/UserEditorAuthorizationHelperTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Editors/UserEditorAuthorizationHelperTests.cs @@ -116,7 +116,7 @@ public class UserEditorAuthorizationHelperTests { var currentUser = Mock.Of(user => user.Groups == new[] { - new ReadOnlyUserGroup(1, Guid.NewGuid(), "CurrentUser", "icon-user", null, null, groupAlias, new int[0], new string[0], new string[0], true), + new ReadOnlyUserGroup(1, Guid.NewGuid(), "CurrentUser", "icon-user", null, null, groupAlias, new int[0], new string[0], new string[0], new HashSet(), true), }); IUser savingUser = null; // This means it is a new created user