Skip to content

Commit

Permalink
Ensure correct access to all manifests + introduce "public" package m…
Browse files Browse the repository at this point in the history
…anifests (#15921)

* Introduce "public" package manifests

* Make sure "all manifests" are available to anyone with backoffice access

* review comments
  • Loading branch information
kjac committed Mar 21, 2024
1 parent 129e900 commit e750d29
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 17 deletions.
@@ -1,13 +1,16 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.ViewModels.Package;
using Umbraco.Cms.Core.Manifest;
using Umbraco.Cms.Core.Mapping;
using Umbraco.Cms.Web.Common.Authorization;

namespace Umbraco.Cms.Api.Management.Controllers.Package;

[ApiVersion("1.0")]
[Authorize(Policy = AuthorizationPolicies.BackOfficeAccess)]
public class AllPackageManifestController : PackageControllerBase
{
private readonly IPackageManifestService _packageManifestService;
Expand All @@ -25,7 +28,7 @@ public AllPackageManifestController(IPackageManifestService packageManifestServi
[ProducesResponseType(typeof(IEnumerable<PackageManifestResponseModel>), StatusCodes.Status200OK)]
public async Task<IActionResult> AllPackageManifests()
{
PackageManifest[] packageManifests = (await _packageManifestService.GetPackageManifestsAsync()).ToArray();
PackageManifest[] packageManifests = (await _packageManifestService.GetAllPackageManifestsAsync()).ToArray();
return Ok(_umbracoMapper.MapEnumerable<PackageManifest, PackageManifestResponseModel>(packageManifests));
}
}
@@ -0,0 +1,33 @@
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Umbraco.Cms.Api.Management.ViewModels.Package;
using Umbraco.Cms.Core.Manifest;
using Umbraco.Cms.Core.Mapping;

namespace Umbraco.Cms.Api.Management.Controllers.Package;

[ApiVersion("1.0")]
[AllowAnonymous]
public class PublicPackageManifestController : PackageControllerBase
{
private readonly IPackageManifestService _packageManifestService;
private readonly IUmbracoMapper _umbracoMapper;

public PublicPackageManifestController(IPackageManifestService packageManifestService, IUmbracoMapper umbracoMapper)
{
_packageManifestService = packageManifestService;
_umbracoMapper = umbracoMapper;
}

// NOTE: this endpoint is deliberately created as non-paginated to ensure the fastest possible client initialization
[HttpGet("manifest/public")]
[MapToApiVersion("1.0")]
[ProducesResponseType(typeof(IEnumerable<PackageManifestResponseModel>), StatusCodes.Status200OK)]
public async Task<IActionResult> PublicPackageManifests()
{
PackageManifest[] packageManifests = (await _packageManifestService.GetPublicPackageManifestsAsync()).ToArray();
return Ok(_umbracoMapper.MapEnumerable<PackageManifest, PackageManifestResponseModel>(packageManifests));
}
}
4 changes: 3 additions & 1 deletion src/Umbraco.Core/Manifest/IPackageManifestService.cs
Expand Up @@ -2,7 +2,9 @@

public interface IPackageManifestService
{
Task<IEnumerable<PackageManifest>> GetPackageManifestsAsync();
Task<IEnumerable<PackageManifest>> GetAllPackageManifestsAsync();

Task<IEnumerable<PackageManifest>> GetPublicPackageManifestsAsync();

Task<PackageManifestImportmap> GetPackageManifestImportmapAsync();
}
2 changes: 2 additions & 0 deletions src/Umbraco.Core/Manifest/PackageManifest.cs
Expand Up @@ -6,6 +6,8 @@ public class PackageManifest

public string? Version { get; set; }

public bool AllowPublicAccess { get; set; }

public bool AllowTelemetry { get; set; } = true;

public required object[] Extensions { get; set; }
Expand Down
7 changes: 5 additions & 2 deletions src/Umbraco.Infrastructure/Manifest/PackageManifestService.cs
Expand Up @@ -22,7 +22,7 @@ internal sealed class PackageManifestService : IPackageManifestService
_cache = appCaches.RuntimeCache;
}

public async Task<IEnumerable<PackageManifest>> GetPackageManifestsAsync()
public async Task<IEnumerable<PackageManifest>> GetAllPackageManifestsAsync()
=> await _cache.GetCacheItemAsync(
$"{nameof(PackageManifestService)}-PackageManifests",
async () =>
Expand All @@ -37,9 +37,12 @@ public async Task<IEnumerable<PackageManifest>> GetPackageManifestsAsync()
_packageManifestSettings.CacheTimeout)
?? Array.Empty<PackageManifest>();

public async Task<IEnumerable<PackageManifest>> GetPublicPackageManifestsAsync()
=> (await GetAllPackageManifestsAsync()).Where(manifest => manifest.AllowPublicAccess).ToArray();

public async Task<PackageManifestImportmap> GetPackageManifestImportmapAsync()
{
IEnumerable<PackageManifest> packageManifests = await GetPackageManifestsAsync();
IEnumerable<PackageManifest> packageManifests = await GetAllPackageManifestsAsync();
var manifests = packageManifests.Select(x => x.Importmap).WhereNotNull().ToList();

var importDict = manifests
Expand Down
Expand Up @@ -22,7 +22,8 @@ public void SetUp()
_readerMock.Setup(r => r.ReadPackageManifestsAsync()).ReturnsAsync(
new[]
{
new PackageManifest { Name = "Test", Extensions = Array.Empty<object>() }
new PackageManifest { Name = "Test", Extensions = Array.Empty<object>(), AllowPublicAccess = false },
new PackageManifest { Name = "Test Public", Extensions = Array.Empty<object>(), AllowPublicAccess = true}
});

_runtimeCache = new ObjectCacheAppCache();
Expand All @@ -37,33 +38,43 @@ public void SetUp()
[Test]
public async Task Caches_PackageManifests()
{
var result = await _service.GetPackageManifestsAsync();
Assert.AreEqual(1, result.Count());
var result = await _service.GetAllPackageManifestsAsync();
Assert.AreEqual(2, result.Count());

var result2 = await _service.GetPackageManifestsAsync();
Assert.AreEqual(1, result2.Count());
var result2 = await _service.GetAllPackageManifestsAsync();
Assert.AreEqual(2, result2.Count());

var result3 = await _service.GetPackageManifestsAsync();
Assert.AreEqual(1, result3.Count());
var result3 = await _service.GetAllPackageManifestsAsync();
Assert.AreEqual(2, result3.Count());

_readerMock.Verify(r => r.ReadPackageManifestsAsync(), Times.Exactly(1));
}

[Test]
public async Task Reloads_PackageManifest_After_Cache_Clear()
{
var result = await _service.GetPackageManifestsAsync();
Assert.AreEqual(1, result.Count());
var result = await _service.GetAllPackageManifestsAsync();
Assert.AreEqual(2, result.Count());
_runtimeCache.Clear();

var result2 = await _service.GetPackageManifestsAsync();
Assert.AreEqual(1, result2.Count());
var result2 = await _service.GetAllPackageManifestsAsync();
Assert.AreEqual(2, result2.Count());
_runtimeCache.Clear();

var result3 = await _service.GetPackageManifestsAsync();
Assert.AreEqual(1, result3.Count());
var result3 = await _service.GetAllPackageManifestsAsync();
Assert.AreEqual(2, result3.Count());
_runtimeCache.Clear();

_readerMock.Verify(r => r.ReadPackageManifestsAsync(), Times.Exactly(3));
}

[Test]
public async Task Supports_Public_PackageManifests()
{
var result = await _service.GetPublicPackageManifestsAsync();
Assert.AreEqual(1, result.Count());

result = await _service.GetAllPackageManifestsAsync();
Assert.AreEqual(2, result.Count());
}
}

0 comments on commit e750d29

Please sign in to comment.