Skip to content

Commit 449c94f

Browse files
Merge branch 'v15/dev' into contrib
2 parents b7bf307 + 83b9266 commit 449c94f

File tree

168 files changed

+4285
-1268
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

168 files changed

+4285
-1268
lines changed

src/Umbraco.Cms.Api.Management/Controllers/Security/SecurityControllerBase.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ protected IActionResult UserOperationStatusResult(UserOperationStatus status, Er
2121
.WithTitle("The password reset token was invalid")
2222
.WithDetail("The specified password reset token was either used already or wrong.")
2323
.Build()),
24+
UserOperationStatus.CancelledByNotification => BadRequest(problemDetailsBuilder
25+
.WithTitle("Cancelled by notification")
26+
.WithDetail("A notification handler prevented the user operation.")
27+
.Build()),
2428
UserOperationStatus.UnknownFailure => BadRequest(problemDetailsBuilder
2529
.WithTitle("Unknown failure")
2630
.WithDetail(errorMessageResult?.Error?.ErrorMessage ?? "The error was unknown")

src/Umbraco.Cms.Api.Management/Controllers/TemporaryFile/TemporaryFileControllerBase.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Microsoft.AspNetCore.Http;
1+
using Microsoft.AspNetCore.Http;
22
using Microsoft.AspNetCore.Mvc;
33
using Umbraco.Cms.Api.Common.Builders;
44
using Umbraco.Cms.Api.Management.Routing;
@@ -17,6 +17,9 @@ protected IActionResult TemporaryFileStatusResult(TemporaryFileOperationStatus o
1717
.WithTitle("File extension not allowed")
1818
.WithDetail("The file extension is not allowed.")
1919
.Build()),
20+
TemporaryFileOperationStatus.InvalidFileName => BadRequest(problemDetailsBuilder
21+
.WithTitle("The provided file name is not valid")
22+
.Build()),
2023
TemporaryFileOperationStatus.KeyAlreadyUsed => BadRequest(problemDetailsBuilder
2124
.WithTitle("Key already used")
2225
.WithDetail("The specified key is already used.")

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public async Task<DocumentVersionItemResponseModel> CreateAsync(ContentVersionMe
2626
new ReferenceByIdModel(_entityService.GetKey(contentVersion.ContentTypeId, UmbracoObjectTypes.DocumentType)
2727
.Result),
2828
new ReferenceByIdModel(await _userIdKeyResolver.GetAsync(contentVersion.UserId)),
29-
new DateTimeOffset(contentVersion.VersionDate, TimeSpan.Zero), // todo align with datetime offset rework
29+
new DateTimeOffset(contentVersion.VersionDate),
3030
contentVersion.CurrentPublishedVersion,
3131
contentVersion.CurrentDraftVersion,
3232
contentVersion.PreventCleanup);

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

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1-
using Umbraco.Cms.Api.Management.Routing;
1+
using Microsoft.Extensions.DependencyInjection;
22
using Microsoft.Extensions.Options;
3+
using Umbraco.Cms.Api.Management.Routing;
34
using Umbraco.Cms.Api.Management.Security;
45
using Umbraco.Cms.Api.Management.ViewModels;
56
using Umbraco.Cms.Api.Management.ViewModels.User;
67
using Umbraco.Cms.Api.Management.ViewModels.User.Current;
7-
using Umbraco.Cms.Core;
88
using Umbraco.Cms.Api.Management.ViewModels.User.Item;
9+
using Umbraco.Cms.Api.Management.ViewModels.UserGroup;
10+
using Umbraco.Cms.Api.Management.ViewModels.UserGroup.Permissions;
11+
using Umbraco.Cms.Core;
912
using Umbraco.Cms.Core.Cache;
1013
using Umbraco.Cms.Core.Configuration.Models;
14+
using Umbraco.Cms.Core.DependencyInjection;
1115
using Umbraco.Cms.Core.IO;
1216
using Umbraco.Cms.Core.Mail;
1317
using Umbraco.Cms.Core.Media;
@@ -20,7 +24,6 @@ namespace Umbraco.Cms.Api.Management.Factories;
2024

2125
public class UserPresentationFactory : IUserPresentationFactory
2226
{
23-
2427
private readonly IEntityService _entityService;
2528
private readonly AppCaches _appCaches;
2629
private readonly MediaFileManager _mediaFileManager;
@@ -31,7 +34,10 @@ public class UserPresentationFactory : IUserPresentationFactory
3134
private readonly IPasswordConfigurationPresentationFactory _passwordConfigurationPresentationFactory;
3235
private readonly IBackOfficeExternalLoginProviders _externalLoginProviders;
3336
private readonly SecuritySettings _securitySettings;
37+
private readonly IUserService _userService;
38+
private readonly IContentService _contentService;
3439

40+
[Obsolete("Please use the constructor taking all parameters. Scheduled for removal in Umbraco 17.")]
3541
public UserPresentationFactory(
3642
IEntityService entityService,
3743
AppCaches appCaches,
@@ -43,6 +49,35 @@ public UserPresentationFactory(
4349
IPasswordConfigurationPresentationFactory passwordConfigurationPresentationFactory,
4450
IOptionsSnapshot<SecuritySettings> securitySettings,
4551
IBackOfficeExternalLoginProviders externalLoginProviders)
52+
: this(
53+
entityService,
54+
appCaches,
55+
mediaFileManager,
56+
imageUrlGenerator,
57+
userGroupPresentationFactory,
58+
absoluteUrlBuilder,
59+
emailSender,
60+
passwordConfigurationPresentationFactory,
61+
securitySettings,
62+
externalLoginProviders,
63+
StaticServiceProvider.Instance.GetRequiredService<IUserService>(),
64+
StaticServiceProvider.Instance.GetRequiredService<IContentService>())
65+
{
66+
}
67+
68+
public UserPresentationFactory(
69+
IEntityService entityService,
70+
AppCaches appCaches,
71+
MediaFileManager mediaFileManager,
72+
IImageUrlGenerator imageUrlGenerator,
73+
IUserGroupPresentationFactory userGroupPresentationFactory,
74+
IAbsoluteUrlBuilder absoluteUrlBuilder,
75+
IEmailSender emailSender,
76+
IPasswordConfigurationPresentationFactory passwordConfigurationPresentationFactory,
77+
IOptionsSnapshot<SecuritySettings> securitySettings,
78+
IBackOfficeExternalLoginProviders externalLoginProviders,
79+
IUserService userService,
80+
IContentService contentService)
4681
{
4782
_entityService = entityService;
4883
_appCaches = appCaches;
@@ -54,6 +89,8 @@ public UserPresentationFactory(
5489
_externalLoginProviders = externalLoginProviders;
5590
_securitySettings = securitySettings.Value;
5691
_absoluteUrlBuilder = absoluteUrlBuilder;
92+
_userService = userService;
93+
_contentService = contentService;
5794
}
5895

5996
public UserResponseModel CreateResponseModel(IUser user)
@@ -195,7 +232,7 @@ public async Task<CurrentUserResponseModel> CreateCurrentUserResponseModelAsync(
195232
var contentStartNodeIds = user.CalculateContentStartNodeIds(_entityService, _appCaches);
196233
var documentStartNodeKeys = GetKeysFromIds(contentStartNodeIds, UmbracoObjectTypes.Document);
197234

198-
var permissions = presentationGroups.SelectMany(x => x.Permissions).ToHashSet();
235+
var permissions = GetAggregatedGranularPermissions(user, presentationGroups);
199236
var fallbackPermissions = presentationGroups.SelectMany(x => x.FallbackPermissions).ToHashSet();
200237

201238
var hasAccessToAllLanguages = presentationGroups.Any(x => x.HasAccessToAllLanguages);
@@ -225,6 +262,42 @@ public async Task<CurrentUserResponseModel> CreateCurrentUserResponseModelAsync(
225262
});
226263
}
227264

265+
private HashSet<IPermissionPresentationModel> GetAggregatedGranularPermissions(IUser user, IEnumerable<UserGroupResponseModel> presentationGroups)
266+
{
267+
var permissions = presentationGroups.SelectMany(x => x.Permissions).ToHashSet();
268+
269+
// The raw permission data consists of several permissions for each document. We want to aggregate this server-side so
270+
// we return one set of aggregate permissions per document that the client will use.
271+
272+
// Get the unique document keys that have granular permissions.
273+
IEnumerable<Guid> documentKeysWithGranularPermissions = permissions
274+
.Where(x => x is DocumentPermissionPresentationModel)
275+
.Cast<DocumentPermissionPresentationModel>()
276+
.Select(x => x.Document.Id)
277+
.Distinct();
278+
279+
var aggregatedPermissions = new HashSet<IPermissionPresentationModel>();
280+
foreach (Guid documentKey in documentKeysWithGranularPermissions)
281+
{
282+
// Retrieve the path of the document.
283+
var path = _contentService.GetById(documentKey)?.Path;
284+
if (string.IsNullOrEmpty(path))
285+
{
286+
continue;
287+
}
288+
289+
// With the path we can call the same logic as used server-side for authorizing access to resources.
290+
EntityPermissionSet permissionsForPath = _userService.GetPermissionsForPath(user, path);
291+
aggregatedPermissions.Add(new DocumentPermissionPresentationModel
292+
{
293+
Document = new ReferenceByIdModel(documentKey),
294+
Verbs = permissionsForPath.GetAllPermissions()
295+
});
296+
}
297+
298+
return aggregatedPermissions;
299+
}
300+
228301
public async Task<CalculatedUserStartNodesResponseModel> CreateCalculatedUserStartNodesResponseModelAsync(IUser user)
229302
{
230303
var mediaStartNodeIds = user.CalculateMediaStartNodeIds(_entityService, _appCaches);

0 commit comments

Comments
 (0)