Skip to content

Commit c26b45d

Browse files
authored
Don't overwrite ancestor configurations for public access (#17797)
* Don't overwrite ancestor configurations for public access (#17709) * Fix spacings
1 parent 64982a1 commit c26b45d

File tree

4 files changed

+61
-3
lines changed

4 files changed

+61
-3
lines changed

src/Umbraco.Cms.Api.Management/Controllers/Document/GetPublicAccessDocumentController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public async Task<IActionResult> GetPublicAccess(CancellationToken cancellationT
4949
}
5050

5151
Attempt<PublicAccessEntry?, PublicAccessOperationStatus> accessAttempt =
52-
await _publicAccessService.GetEntryByContentKeyAsync(id);
52+
await _publicAccessService.GetEntryByContentKeyWithoutAncestorsAsync(id);
5353

5454
if (accessAttempt.Success is false || accessAttempt.Result is null)
5555
{

src/Umbraco.Core/Services/IPublicAccessService.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,23 @@ public interface IPublicAccessService : IService
8585
/// </summary>
8686
/// <param name="key"></param>
8787
/// <returns>Returns null if no entry is found</returns>
88+
/// <remarks>
89+
/// This method supports inheritance by considering ancestor entries (if any),
90+
/// if no entry is found for the specified content key.
91+
/// </remarks>
8892
Task<Attempt<PublicAccessEntry?, PublicAccessOperationStatus>> GetEntryByContentKeyAsync(Guid key);
8993

94+
/// <summary>
95+
/// Gets the entry defined for the content item based on a content key, without taking ancestor entries into account.
96+
/// </summary>
97+
/// <param name="key"></param>
98+
/// <returns>Returns null if no entry is found</returns>
99+
/// <remarks>
100+
/// This method does not support inheritance. Use <see cref="GetEntryByContentKeyAsync"/> to include ancestor entries (if any).
101+
/// </remarks>
102+
Task<Attempt<PublicAccessEntry?, PublicAccessOperationStatus>> GetEntryByContentKeyWithoutAncestorsAsync(Guid key)
103+
=> Task.FromResult(Attempt.SucceedWithStatus<PublicAccessEntry?, PublicAccessOperationStatus>(PublicAccessOperationStatus.EntryNotFound, null));
104+
90105
/// <summary>
91106
/// Deletes the entry and all associated rules for a given key.
92107
/// </summary>

src/Umbraco.Core/Services/PublicAccessService.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System.Globalization;
2+
using Microsoft.Extensions.DependencyInjection;
23
using Microsoft.Extensions.Logging;
4+
using Umbraco.Cms.Core.DependencyInjection;
35
using Umbraco.Cms.Core.Events;
46
using Umbraco.Cms.Core.Models;
57
using Umbraco.Cms.Core.Models.Entities;
@@ -16,19 +18,41 @@ internal class PublicAccessService : RepositoryService, IPublicAccessService
1618
private readonly IPublicAccessRepository _publicAccessRepository;
1719
private readonly IEntityService _entityService;
1820
private readonly IContentService _contentService;
21+
private readonly IIdKeyMap _idKeyMap;
1922

23+
[Obsolete("Please use the constructor that accepts all parameter. Will be removed in V16.")]
2024
public PublicAccessService(
2125
ICoreScopeProvider provider,
2226
ILoggerFactory loggerFactory,
2327
IEventMessagesFactory eventMessagesFactory,
2428
IPublicAccessRepository publicAccessRepository,
2529
IEntityService entityService,
2630
IContentService contentService)
31+
: this(
32+
provider,
33+
loggerFactory,
34+
eventMessagesFactory,
35+
publicAccessRepository,
36+
entityService,
37+
contentService,
38+
StaticServiceProvider.Instance.GetRequiredService<IIdKeyMap>())
39+
{
40+
}
41+
42+
public PublicAccessService(
43+
ICoreScopeProvider provider,
44+
ILoggerFactory loggerFactory,
45+
IEventMessagesFactory eventMessagesFactory,
46+
IPublicAccessRepository publicAccessRepository,
47+
IEntityService entityService,
48+
IContentService contentService,
49+
IIdKeyMap idKeyMap)
2750
: base(provider, loggerFactory, eventMessagesFactory)
2851
{
2952
_publicAccessRepository = publicAccessRepository;
3053
_entityService = entityService;
3154
_contentService = contentService;
55+
_idKeyMap = idKeyMap;
3256
}
3357

3458
/// <summary>
@@ -381,6 +405,23 @@ private Attempt<PublicAccessNodesValidationResult, PublicAccessOperationStatus>
381405
return Task.FromResult(Attempt.SucceedWithStatus<PublicAccessEntry?, PublicAccessOperationStatus>(PublicAccessOperationStatus.Success, entry));
382406
}
383407

408+
public async Task<Attempt<PublicAccessEntry?, PublicAccessOperationStatus>> GetEntryByContentKeyWithoutAncestorsAsync(Guid key)
409+
{
410+
Attempt<PublicAccessEntry?, PublicAccessOperationStatus> result = await GetEntryByContentKeyAsync(key);
411+
if (result.Success is false || result.Result is null)
412+
{
413+
return result;
414+
}
415+
416+
Attempt<Guid> idToKeyAttempt = _idKeyMap.GetKeyForId(result.Result.ProtectedNodeId, UmbracoObjectTypes.Document);
417+
if (idToKeyAttempt.Success is false || idToKeyAttempt.Result != key)
418+
{
419+
return Attempt.SucceedWithStatus<PublicAccessEntry?, PublicAccessOperationStatus>(PublicAccessOperationStatus.EntryNotFound, null);
420+
}
421+
422+
return result;
423+
}
424+
384425
public async Task<Attempt<PublicAccessOperationStatus>> DeleteAsync(Guid key)
385426
{
386427
using (ICoreScope scope = ScopeProvider.CreateCoreScope())

src/Umbraco.Web.UI.Client/src/packages/documents/documents/entity-actions/public-access/repository/public-access.server.data.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { DocumentService } from '@umbraco-cms/backoffice/external/backend-api';
22
import type { PublicAccessRequestModel } from '@umbraco-cms/backoffice/external/backend-api';
33
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
4-
import { tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
4+
import { tryExecute, tryExecuteAndNotify } from '@umbraco-cms/backoffice/resources';
55

66
/**
77
* A data source for the Document Public Access that fetches data from the server
@@ -41,7 +41,9 @@ export class UmbDocumentPublicAccessServerDataSource {
4141
*/
4242
async read(unique: string) {
4343
if (!unique) throw new Error('unique is missing');
44-
return tryExecuteAndNotify(this.#host, DocumentService.getDocumentByIdPublicAccess({ id: unique }));
44+
// NOTE: The entity will not be present, when fetching Public Access for a descendant of a protected Document.
45+
// This is a perfectly valid scenario, which is handled in the view. In other words, just use tryExecute here.
46+
return tryExecute(DocumentService.getDocumentByIdPublicAccess({ id: unique }));
4547
}
4648

4749
/**

0 commit comments

Comments
 (0)