From c26b45db59c9808991e66122fee18ec62bc6ebfe Mon Sep 17 00:00:00 2001 From: Kenn Jacobsen Date: Thu, 12 Dec 2024 10:46:25 +0100 Subject: [PATCH] Don't overwrite ancestor configurations for public access (#17797) * Don't overwrite ancestor configurations for public access (#17709) * Fix spacings --- .../GetPublicAccessDocumentController.cs | 2 +- .../Services/IPublicAccessService.cs | 15 +++++++ .../Services/PublicAccessService.cs | 41 +++++++++++++++++++ .../repository/public-access.server.data.ts | 6 ++- 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Cms.Api.Management/Controllers/Document/GetPublicAccessDocumentController.cs b/src/Umbraco.Cms.Api.Management/Controllers/Document/GetPublicAccessDocumentController.cs index dcb27ac42a..cdcdc1c85c 100644 --- a/src/Umbraco.Cms.Api.Management/Controllers/Document/GetPublicAccessDocumentController.cs +++ b/src/Umbraco.Cms.Api.Management/Controllers/Document/GetPublicAccessDocumentController.cs @@ -49,7 +49,7 @@ public class GetPublicAccessDocumentController : DocumentControllerBase } Attempt accessAttempt = - await _publicAccessService.GetEntryByContentKeyAsync(id); + await _publicAccessService.GetEntryByContentKeyWithoutAncestorsAsync(id); if (accessAttempt.Success is false || accessAttempt.Result is null) { diff --git a/src/Umbraco.Core/Services/IPublicAccessService.cs b/src/Umbraco.Core/Services/IPublicAccessService.cs index fc919baf37..b04ab230e4 100644 --- a/src/Umbraco.Core/Services/IPublicAccessService.cs +++ b/src/Umbraco.Core/Services/IPublicAccessService.cs @@ -85,8 +85,23 @@ public interface IPublicAccessService : IService /// /// /// Returns null if no entry is found + /// + /// This method supports inheritance by considering ancestor entries (if any), + /// if no entry is found for the specified content key. + /// Task> GetEntryByContentKeyAsync(Guid key); + /// + /// Gets the entry defined for the content item based on a content key, without taking ancestor entries into account. + /// + /// + /// Returns null if no entry is found + /// + /// This method does not support inheritance. Use to include ancestor entries (if any). + /// + Task> GetEntryByContentKeyWithoutAncestorsAsync(Guid key) + => Task.FromResult(Attempt.SucceedWithStatus(PublicAccessOperationStatus.EntryNotFound, null)); + /// /// Deletes the entry and all associated rules for a given key. /// diff --git a/src/Umbraco.Core/Services/PublicAccessService.cs b/src/Umbraco.Core/Services/PublicAccessService.cs index ab9675fb09..f44ba15022 100644 --- a/src/Umbraco.Core/Services/PublicAccessService.cs +++ b/src/Umbraco.Core/Services/PublicAccessService.cs @@ -1,5 +1,7 @@ using System.Globalization; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; @@ -16,7 +18,9 @@ internal class PublicAccessService : RepositoryService, IPublicAccessService private readonly IPublicAccessRepository _publicAccessRepository; private readonly IEntityService _entityService; private readonly IContentService _contentService; + private readonly IIdKeyMap _idKeyMap; + [Obsolete("Please use the constructor that accepts all parameter. Will be removed in V16.")] public PublicAccessService( ICoreScopeProvider provider, ILoggerFactory loggerFactory, @@ -24,11 +28,31 @@ internal class PublicAccessService : RepositoryService, IPublicAccessService IPublicAccessRepository publicAccessRepository, IEntityService entityService, IContentService contentService) + : this( + provider, + loggerFactory, + eventMessagesFactory, + publicAccessRepository, + entityService, + contentService, + StaticServiceProvider.Instance.GetRequiredService()) + { + } + + public PublicAccessService( + ICoreScopeProvider provider, + ILoggerFactory loggerFactory, + IEventMessagesFactory eventMessagesFactory, + IPublicAccessRepository publicAccessRepository, + IEntityService entityService, + IContentService contentService, + IIdKeyMap idKeyMap) : base(provider, loggerFactory, eventMessagesFactory) { _publicAccessRepository = publicAccessRepository; _entityService = entityService; _contentService = contentService; + _idKeyMap = idKeyMap; } /// @@ -381,6 +405,23 @@ internal class PublicAccessService : RepositoryService, IPublicAccessService return Task.FromResult(Attempt.SucceedWithStatus(PublicAccessOperationStatus.Success, entry)); } + public async Task> GetEntryByContentKeyWithoutAncestorsAsync(Guid key) + { + Attempt result = await GetEntryByContentKeyAsync(key); + if (result.Success is false || result.Result is null) + { + return result; + } + + Attempt idToKeyAttempt = _idKeyMap.GetKeyForId(result.Result.ProtectedNodeId, UmbracoObjectTypes.Document); + if (idToKeyAttempt.Success is false || idToKeyAttempt.Result != key) + { + return Attempt.SucceedWithStatus(PublicAccessOperationStatus.EntryNotFound, null); + } + + return result; + } + public async Task> DeleteAsync(Guid key) { using (ICoreScope scope = ScopeProvider.CreateCoreScope()) diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/public-access/repository/public-access.server.data.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/public-access/repository/public-access.server.data.ts index 693c751dff..7221bc40d0 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/public-access/repository/public-access.server.data.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/public-access/repository/public-access.server.data.ts @@ -1,7 +1,7 @@ import { DocumentService } from '@umbraco-cms/backoffice/external/backend-api'; import type { PublicAccessRequestModel } from '@umbraco-cms/backoffice/external/backend-api'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; -import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; +import { tryExecute, tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources'; /** * A data source for the Document Public Access that fetches data from the server @@ -41,7 +41,9 @@ export class UmbDocumentPublicAccessServerDataSource { */ async read(unique: string) { if (!unique) throw new Error('unique is missing'); - return tryExecuteAndNotify(this.#host, DocumentService.getDocumentByIdPublicAccess({ id: unique })); + // NOTE: The entity will not be present, when fetching Public Access for a descendant of a protected Document. + // This is a perfectly valid scenario, which is handled in the view. In other words, just use tryExecute here. + return tryExecute(DocumentService.getDocumentByIdPublicAccess({ id: unique })); } /**