Compare commits

...
Author SHA1 Message Date
Andy Butland fd5b8de533 Bumped version to 13.7.2 2025-03-12 07:25:35 +01:00
Andy Butland e3d9b042c2 Avoids collection was modified issue when flowing identities to the authenticated user's principal. (#18527) 2025-03-12 07:24:50 +01:00
5b54bed406 Merge commit from fork
* Fixed parsing of node if in content and media permission querystring handlers to retrieve expected value when multiple are provided in the querystring.

* Add HttpPost attributes to backoffice endpoints that should only accept post requests.

* Bumped version to 13.6.1.

* Narrow PermissionQueryString parsing to the releveant UmbracoObjectType

* Add missed update from v10

---------

Co-authored-by: Sven Geusens <sge@umbraco.dk>
2025-03-11 05:11:08 +01:00
twoday-rolandkockandAndy Butland 2ec6ff4ebc bumped imagesharp to prevent CVE-2025-27598 (#18602) 2025-03-09 08:43:52 +01:00
Andy Butland 451b5f96e7 Use windows for build agent to avoid hanging issues with Linux. 2025-02-28 06:57:35 +01:00
Andy Butland 15ebae5025 Bumped version to 13.7.1. 2025-02-28 06:57:00 +01:00
Andy Butland eaea7a67ba Bump version to 13.7.0. 2025-02-26 08:14:26 +01:00
13 changed files with 113 additions and 37 deletions
+1 -1
View File
@@ -73,7 +73,7 @@
<PackageVersion Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageVersion Include="Serilog.Sinks.Map" Version="1.0.2" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.6" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.7" />
<PackageVersion Include="SixLabors.ImageSharp.Web" Version="3.1.3" />
<PackageVersion Include="Smidge.InMemory" Version="4.4.0" />
<PackageVersion Include="Smidge.Nuglify" Version="4.5.1" />
+1 -1
View File
@@ -71,7 +71,7 @@ stages:
- job: A
displayName: Build Umbraco CMS
pool:
vmImage: 'ubuntu-latest'
vmImage: 'windows-latest'
steps:
- checkout: self
submodules: false
@@ -5,7 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SixLabors.ImageSharp" VersionOverride="[2.1.9, 3)" />
<PackageReference Include="SixLabors.ImageSharp" VersionOverride="[2.1.10, 3)" />
<PackageReference Include="SixLabors.ImageSharp.Web" VersionOverride="[2.0.2, 3)" />
</ItemGroup>
@@ -20,6 +20,8 @@ public class
{
private readonly ContentPermissions _contentPermissions;
protected override UmbracoObjectTypes KeyParsingFilterType => UmbracoObjectTypes.Document;
/// <summary>
/// Initializes a new instance of the <see cref="ContentPermissionsQueryStringHandler" /> class.
/// </summary>
@@ -48,7 +50,11 @@ public class
return Task.FromResult(true);
}
var argument = routeVal.ToString();
// Handle case where the incoming querystring could contain more than one value (e.g. ?id=1000&id=1001).
// It's the first one that'll be processed by the protected method so we should verify that.
var argument = routeVal.Count == 1
? routeVal.ToString()
: routeVal.FirstOrDefault()?.ToString() ?? string.Empty;
if (!TryParseNodeId(argument, out nodeId))
{
@@ -18,6 +18,8 @@ public class MediaPermissionsQueryStringHandler : PermissionsQueryStringHandler<
{
private readonly MediaPermissions _mediaPermissions;
protected override UmbracoObjectTypes KeyParsingFilterType => UmbracoObjectTypes.Media;
/// <summary>
/// Initializes a new instance of the <see cref="MediaPermissionsQueryStringHandler" /> class.
/// </summary>
@@ -44,7 +46,11 @@ public class MediaPermissionsQueryStringHandler : PermissionsQueryStringHandler<
return Task.FromResult(true);
}
var argument = routeVal.ToString();
// Handle case where the incoming querystring could contain more than one value (e.g. ?id=1000&id=1001).
// It's the first one that'll be processed by the protected method so we should verify that.
var argument = routeVal.Count == 1
? routeVal.ToString()
: routeVal.FirstOrDefault()?.ToString() ?? string.Empty;
if (!TryParseNodeId(argument, out var nodeId))
{
@@ -49,12 +49,18 @@ public abstract class PermissionsQueryStringHandler<T> : MustSatisfyRequirementA
/// </summary>
protected IEntityService EntityService { get; set; }
/// <summary>
/// Defaults to Unknown so all types are allowed, since Keys are unique across all node types this works,
/// but it if you are certain you are looking for a specific type this should be overwritten for DB query performance.
/// </summary>
protected virtual UmbracoObjectTypes KeyParsingFilterType => UmbracoObjectTypes.Unknown;
/// <summary>
/// Attempts to parse a node ID from a string representation found in a querystring value.
/// </summary>
/// <param name="argument">Querystring value.</param>
/// <param name="nodeId">Output parsed Id.</param>
/// <returns>True of node ID could be parased, false it not.</returns>
/// <returns>True of node ID could be parsed, false it not.</returns>
protected bool TryParseNodeId(string argument, out int nodeId)
{
// If the argument is an int, it will parse and can be assigned to nodeId.
@@ -75,7 +81,7 @@ public abstract class PermissionsQueryStringHandler<T> : MustSatisfyRequirementA
if (Guid.TryParse(argument, out Guid key))
{
nodeId = EntityService.GetId(key, UmbracoObjectTypes.Document).Result;
nodeId = EntityService.GetId(key, KeyParsingFilterType).Result;
return true;
}
@@ -256,6 +256,7 @@ public class ContentController : ContentControllerBase
/// Permission check is done for letter 'R' which is for <see cref="ActionRights" /> which the user must have access to
/// update
/// </remarks>
[HttpPost]
public async Task<ActionResult<IEnumerable<AssignedUserGroupPermissions?>?>> PostSaveUserGroupPermissions(
UserGroupPermissionsSave saveModel)
{
@@ -902,6 +903,7 @@ public class ContentController : ContentControllerBase
[Authorize(Policy = AuthorizationPolicies.TreeAccessDocumentTypes)]
[FileUploadCleanupFilter]
[ContentSaveValidation(skipUserAccessValidation:true)] // skip user access validation because we "only" require Settings access to create new blueprints from scratch
[HttpPost]
public async Task<ActionResult<ContentItemDisplay<ContentVariantDisplay>?>?> PostSaveBlueprint(
[ModelBinder(typeof(BlueprintItemBinder))] ContentItemSave contentItem)
{
@@ -939,6 +941,7 @@ public class ContentController : ContentControllerBase
[FileUploadCleanupFilter]
[ContentSaveValidation]
[OutgoingEditorModelEvent]
[HttpPost]
public async Task<ActionResult<ContentItemDisplay<ContentVariantScheduleDisplay>?>> PostSave(
[ModelBinder(typeof(ContentItemBinder))] ContentItemSave contentItem)
{
@@ -2089,6 +2092,7 @@ public class ContentController : ContentControllerBase
/// does not have Publish access to this node.
/// </remarks>
[Authorize(Policy = AuthorizationPolicies.ContentPermissionPublishById)]
[HttpPost]
public IActionResult PostPublishById(int id)
{
IContent? foundContent = GetObjectFromRequest(() => _contentService.GetById(id));
@@ -2120,6 +2124,7 @@ public class ContentController : ContentControllerBase
/// does not have Publish access to this node.
/// </remarks>
[Authorize(Policy = AuthorizationPolicies.ContentPermissionPublishById)]
[HttpPost]
public IActionResult PostPublishByIdAndCulture(PublishContent model)
{
var languageCount = _allLangs.Value.Count();
@@ -2243,6 +2248,7 @@ public class ContentController : ContentControllerBase
/// </summary>
/// <param name="sorted"></param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> PostSort(ContentSortOrder sorted)
{
if (sorted == null)
@@ -2294,6 +2300,7 @@ public class ContentController : ContentControllerBase
/// </summary>
/// <param name="move"></param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult?> PostMove(MoveOrCopy move)
{
// Authorize...
@@ -2333,6 +2340,7 @@ public class ContentController : ContentControllerBase
/// </summary>
/// <param name="copy"></param>
/// <returns></returns>
[HttpPost]
public async Task<ActionResult<IContent>?> PostCopy(MoveOrCopy copy)
{
// Authorize...
@@ -2372,6 +2380,7 @@ public class ContentController : ContentControllerBase
/// <param name="model">The content and variants to unpublish</param>
/// <returns></returns>
[OutgoingEditorModelEvent]
[HttpPost]
public async Task<ActionResult<ContentItemDisplayWithSchedule?>> PostUnpublish(UnpublishContent model)
{
IContent? foundContent = _contentService.GetById(model.Id);
@@ -3096,6 +3105,7 @@ public class ContentController : ContentControllerBase
return notifications;
}
[HttpPost]
public IActionResult PostNotificationOptions(
int contentId,
[FromQuery(Name = "notifyOptions[]")] string[] notifyOptions)
@@ -386,6 +386,7 @@ public class MediaController : ContentControllerBase
/// </summary>
/// <param name="move"></param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> PostMove(MoveOrCopy move)
{
// Authorize...
@@ -436,6 +437,7 @@ public class MediaController : ContentControllerBase
[FileUploadCleanupFilter]
[MediaItemSaveValidation]
[OutgoingEditorModelEvent]
[HttpPost]
public ActionResult<MediaItemDisplay?>? PostSave(
[ModelBinder(typeof(MediaItemBinder))] MediaItemSave contentItem)
{
@@ -551,6 +553,7 @@ public class MediaController : ContentControllerBase
/// </summary>
/// <param name="sorted"></param>
/// <returns></returns>
[HttpPost]
public async Task<IActionResult> PostSort(ContentSortOrder sorted)
{
if (sorted == null)
@@ -595,6 +598,7 @@ public class MediaController : ContentControllerBase
}
}
[HttpPost]
public async Task<ActionResult<MediaItemDisplay?>> PostAddFolder(PostedFolder folder)
{
ActionResult<int?>? parentIdResult = await GetParentIdAsIntAsync(folder.ParentId, true);
@@ -628,6 +632,7 @@ public class MediaController : ContentControllerBase
/// <remarks>
/// We cannot validate this request with attributes (nicely) due to the nature of the multi-part for data.
/// </remarks>
[HttpPost]
public async Task<IActionResult> PostAddFile([FromForm] string path, [FromForm] string currentFolder,
[FromForm] string contentTypeAlias, List<IFormFile> file)
{
@@ -68,7 +68,13 @@ public static class HttpContextExtensions
// Otherwise we can't log in as both a member and a backoffice user
// For instance if you've enabled basic auth.
ClaimsPrincipal? authenticatedPrincipal = result.Principal;
IEnumerable<ClaimsIdentity> existingIdentities = httpContext.User.Identities.Where(x => x.IsAuthenticated && x.AuthenticationType != authenticatedPrincipal.Identity.AuthenticationType);
// Make sure to copy into a list before attempting to update the authenticated principal, so we don't attempt to modify
// the collection while iterating it.
// See: https://github.com/umbraco/Umbraco-CMS/issues/18509
var existingIdentities = httpContext.User.Identities
.Where(x => x.IsAuthenticated && x.AuthenticationType != authenticatedPrincipal.Identity.AuthenticationType)
.ToList();
authenticatedPrincipal.AddIdentities(existingIdentities);
httpContext.User = authenticatedPrincipal;
@@ -1,9 +1,7 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
@@ -34,7 +32,7 @@ public class ContentPermissionsQueryStringHandlerTests
public async Task Node_Id_From_Requirement_With_Permission_Is_Authorized()
{
var authHandlerContext = CreateAuthorizationHandlerContext(NodeId);
var mockHttpContextAccessor = CreateMockHttpContextAccessor();
var mockHttpContextAccessor = CreateMockHttpContextAccessorWithQueryStringValue();
var sut = CreateHandler(mockHttpContextAccessor.Object, NodeId, new[] { "A" });
await sut.HandleAsync(authHandlerContext);
@@ -46,7 +44,7 @@ public class ContentPermissionsQueryStringHandlerTests
public async Task Node_Id_From_Requirement_Without_Permission_Is_Not_Authorized()
{
var authHandlerContext = CreateAuthorizationHandlerContext(NodeId);
var mockHttpContextAccessor = CreateMockHttpContextAccessor();
var mockHttpContextAccessor = CreateMockHttpContextAccessorWithQueryStringValue();
var sut = CreateHandler(mockHttpContextAccessor.Object, NodeId, new[] { "B" });
await sut.HandleAsync(authHandlerContext);
@@ -59,7 +57,7 @@ public class ContentPermissionsQueryStringHandlerTests
public async Task Node_Id_Missing_From_Requirement_And_QueryString_Is_Authorized()
{
var authHandlerContext = CreateAuthorizationHandlerContext();
var mockHttpContextAccessor = CreateMockHttpContextAccessor("xxx");
var mockHttpContextAccessor = CreateMockHttpContextAccessorWithQueryStringValue("xxx");
var sut = CreateHandler(mockHttpContextAccessor.Object, NodeId, new[] { "A" });
await sut.HandleAsync(authHandlerContext);
@@ -71,7 +69,7 @@ public class ContentPermissionsQueryStringHandlerTests
public async Task Node_Integer_Id_From_QueryString_With_Permission_Is_Authorized()
{
var authHandlerContext = CreateAuthorizationHandlerContext();
var mockHttpContextAccessor = CreateMockHttpContextAccessor(queryStringValue: NodeId.ToString());
var mockHttpContextAccessor = CreateMockHttpContextAccessorWithQueryStringValue(queryStringValue: NodeId.ToString());
var sut = CreateHandler(mockHttpContextAccessor.Object, NodeId, new[] { "A" });
await sut.HandleAsync(authHandlerContext);
@@ -84,7 +82,21 @@ public class ContentPermissionsQueryStringHandlerTests
public async Task Node_Integer_Id_From_QueryString_Without_Permission_Is_Not_Authorized()
{
var authHandlerContext = CreateAuthorizationHandlerContext();
var mockHttpContextAccessor = CreateMockHttpContextAccessor(queryStringValue: NodeId.ToString());
var mockHttpContextAccessor = CreateMockHttpContextAccessorWithQueryStringValue(queryStringValue: NodeId.ToString());
var sut = CreateHandler(mockHttpContextAccessor.Object, NodeId, new[] { "B" });
await sut.HandleAsync(authHandlerContext);
Assert.IsFalse(authHandlerContext.HasSucceeded);
AssertContentCached(mockHttpContextAccessor);
}
[Test]
public async Task Node_Integer_Id_From_QueryString_Without_Permission_Is_Not_Authorized_Even_When_Additional_Parameter_For_Id_With_Permission_Is_Provided()
{
// Provides initially failing test and verifies fix for advisory https://github.com/umbraco/Umbraco-CMS/security/advisories/GHSA-wx5h-wqfq-v698
var authHandlerContext = CreateAuthorizationHandlerContext();
var mockHttpContextAccessor = CreateMockHttpContextAccessorWithQueryStringValues(queryStringValues: [NodeId.ToString(), 1001.ToString()]);
var sut = CreateHandler(mockHttpContextAccessor.Object, NodeId, new[] { "B" });
await sut.HandleAsync(authHandlerContext);
@@ -97,7 +109,7 @@ public class ContentPermissionsQueryStringHandlerTests
public async Task Node_Udi_Id_From_QueryString_With_Permission_Is_Authorized()
{
var authHandlerContext = CreateAuthorizationHandlerContext();
var mockHttpContextAccessor = CreateMockHttpContextAccessor(queryStringValue: s_nodeUdi.ToString());
var mockHttpContextAccessor = CreateMockHttpContextAccessorWithQueryStringValue(queryStringValue: s_nodeUdi.ToString());
var sut = CreateHandler(mockHttpContextAccessor.Object, NodeId, new[] { "A" });
await sut.HandleAsync(authHandlerContext);
@@ -110,7 +122,7 @@ public class ContentPermissionsQueryStringHandlerTests
public async Task Node_Udi_Id_From_QueryString_Without_Permission_Is_Not_Authorized()
{
var authHandlerContext = CreateAuthorizationHandlerContext();
var mockHttpContextAccessor = CreateMockHttpContextAccessor(queryStringValue: s_nodeUdi.ToString());
var mockHttpContextAccessor = CreateMockHttpContextAccessorWithQueryStringValue(queryStringValue: s_nodeUdi.ToString());
var sut = CreateHandler(mockHttpContextAccessor.Object, NodeId, new[] { "B" });
await sut.HandleAsync(authHandlerContext);
@@ -123,7 +135,7 @@ public class ContentPermissionsQueryStringHandlerTests
public async Task Node_Guid_Id_From_QueryString_With_Permission_Is_Authorized()
{
var authHandlerContext = CreateAuthorizationHandlerContext();
var mockHttpContextAccessor = CreateMockHttpContextAccessor(queryStringValue: s_nodeGuid.ToString());
var mockHttpContextAccessor = CreateMockHttpContextAccessorWithQueryStringValue(queryStringValue: s_nodeGuid.ToString());
var sut = CreateHandler(mockHttpContextAccessor.Object, NodeId, new[] { "A" });
await sut.HandleAsync(authHandlerContext);
@@ -136,7 +148,7 @@ public class ContentPermissionsQueryStringHandlerTests
public async Task Node_Guid_Id_From_QueryString_Without_Permission_Is_Not_Authorized()
{
var authHandlerContext = CreateAuthorizationHandlerContext();
var mockHttpContextAccessor = CreateMockHttpContextAccessor(queryStringValue: s_nodeGuid.ToString());
var mockHttpContextAccessor = CreateMockHttpContextAccessorWithQueryStringValue(queryStringValue: s_nodeGuid.ToString());
var sut = CreateHandler(mockHttpContextAccessor.Object, NodeId, new[] { "B" });
await sut.HandleAsync(authHandlerContext);
@@ -149,7 +161,7 @@ public class ContentPermissionsQueryStringHandlerTests
public async Task Node_Invalid_Id_From_QueryString_Is_Authorized()
{
var authHandlerContext = CreateAuthorizationHandlerContext();
var mockHttpContextAccessor = CreateMockHttpContextAccessor(queryStringValue: "invalid");
var mockHttpContextAccessor = CreateMockHttpContextAccessorWithQueryStringValue(queryStringValue: "invalid");
var sut = CreateHandler(mockHttpContextAccessor.Object, NodeId, new[] { "A" });
await sut.HandleAsync(authHandlerContext);
@@ -168,14 +180,20 @@ public class ContentPermissionsQueryStringHandlerTests
return new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement }, user, resource);
}
private static Mock<IHttpContextAccessor> CreateMockHttpContextAccessor(
private static Mock<IHttpContextAccessor> CreateMockHttpContextAccessorWithQueryStringValue(
string queryStringName = QueryStringName,
string queryStringValue = "")
=> CreateMockHttpContextAccessorWithQueryStringValues(queryStringName, [queryStringValue]);
private static Mock<IHttpContextAccessor> CreateMockHttpContextAccessorWithQueryStringValues(
string queryStringName = QueryStringName,
string[]? queryStringValues = null)
{
queryStringValues ??= [];
var mockHttpContextAccessor = new Mock<IHttpContextAccessor>();
var mockHttpContext = new Mock<HttpContext>();
var mockHttpRequest = new Mock<HttpRequest>();
var queryParams = new Dictionary<string, StringValues> { { queryStringName, queryStringValue } };
var queryParams = new Dictionary<string, StringValues> { { queryStringName, new StringValues(queryStringValues) } };
mockHttpRequest.SetupGet(x => x.Query).Returns(new QueryCollection(queryParams));
mockHttpContext.SetupGet(x => x.Request).Returns(mockHttpRequest.Object);
mockHttpContext.SetupGet(x => x.Items).Returns(new Dictionary<object, object>());
@@ -1,9 +1,7 @@
// Copyright (c) Umbraco.
// See LICENSE for more details.
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
@@ -33,7 +31,7 @@ public class MediaPermissionsQueryStringHandlerTests
public async Task Node_Id_Missing_From_QueryString_Is_Authorized()
{
var authHandlerContext = CreateAuthorizationHandlerContext();
var mockHttpContextAccessor = CreateMockHttpContextAccessor("xxx");
var mockHttpContextAccessor = CreateMockHttpContextAccessorWithQueryStringValue("xxx");
var sut = CreateHandler(mockHttpContextAccessor.Object, NodeId);
await sut.HandleAsync(authHandlerContext);
@@ -45,7 +43,7 @@ public class MediaPermissionsQueryStringHandlerTests
public async Task Node_Integer_Id_From_QueryString_With_Permission_Is_Authorized()
{
var authHandlerContext = CreateAuthorizationHandlerContext();
var mockHttpContextAccessor = CreateMockHttpContextAccessor(queryStringValue: NodeId.ToString());
var mockHttpContextAccessor = CreateMockHttpContextAccessorWithQueryStringValue(queryStringValue: NodeId.ToString());
var sut = CreateHandler(mockHttpContextAccessor.Object, NodeId);
await sut.HandleAsync(authHandlerContext);
@@ -58,7 +56,21 @@ public class MediaPermissionsQueryStringHandlerTests
public async Task Node_Integer_Id_From_QueryString_Without_Permission_Is_Not_Authorized()
{
var authHandlerContext = CreateAuthorizationHandlerContext();
var mockHttpContextAccessor = CreateMockHttpContextAccessor(queryStringValue: NodeId.ToString());
var mockHttpContextAccessor = CreateMockHttpContextAccessorWithQueryStringValue(queryStringValue: NodeId.ToString());
var sut = CreateHandler(mockHttpContextAccessor.Object, NodeId, 1001);
await sut.HandleAsync(authHandlerContext);
Assert.IsFalse(authHandlerContext.HasSucceeded);
AssertMediaCached(mockHttpContextAccessor);
}
[Test]
public async Task Node_Integer_Id_From_QueryString_Without_Permission_Is_Not_Authorized_Even_When_Additional_Parameter_For_Id_With_Permission_Is_Provided()
{
// Provides initially failing test and verifies fix for advisory https://github.com/umbraco/Umbraco-CMS/security/advisories/GHSA-wx5h-wqfq-v698
var authHandlerContext = CreateAuthorizationHandlerContext();
var mockHttpContextAccessor = CreateMockHttpContextAccessorWithQueryStringValues(queryStringValues: [NodeId.ToString(), 1001.ToString()]);
var sut = CreateHandler(mockHttpContextAccessor.Object, NodeId, 1001);
await sut.HandleAsync(authHandlerContext);
@@ -71,7 +83,7 @@ public class MediaPermissionsQueryStringHandlerTests
public async Task Node_Udi_Id_From_QueryString_With_Permission_Is_Authorized()
{
var authHandlerContext = CreateAuthorizationHandlerContext();
var mockHttpContextAccessor = CreateMockHttpContextAccessor(queryStringValue: s_nodeUdi.ToString());
var mockHttpContextAccessor = CreateMockHttpContextAccessorWithQueryStringValue(queryStringValue: s_nodeUdi.ToString());
var sut = CreateHandler(mockHttpContextAccessor.Object, NodeId);
await sut.HandleAsync(authHandlerContext);
@@ -84,7 +96,7 @@ public class MediaPermissionsQueryStringHandlerTests
public async Task Node_Udi_Id_From_QueryString_Without_Permission_Is_Not_Authorized()
{
var authHandlerContext = CreateAuthorizationHandlerContext();
var mockHttpContextAccessor = CreateMockHttpContextAccessor(queryStringValue: s_nodeUdi.ToString());
var mockHttpContextAccessor = CreateMockHttpContextAccessorWithQueryStringValue(queryStringValue: s_nodeUdi.ToString());
var sut = CreateHandler(mockHttpContextAccessor.Object, NodeId, 1001);
await sut.HandleAsync(authHandlerContext);
@@ -97,7 +109,7 @@ public class MediaPermissionsQueryStringHandlerTests
public async Task Node_Guid_Id_From_QueryString_With_Permission_Is_Authorized()
{
var authHandlerContext = CreateAuthorizationHandlerContext();
var mockHttpContextAccessor = CreateMockHttpContextAccessor(queryStringValue: s_nodeGuid.ToString());
var mockHttpContextAccessor = CreateMockHttpContextAccessorWithQueryStringValue(queryStringValue: s_nodeGuid.ToString());
var sut = CreateHandler(mockHttpContextAccessor.Object, NodeId);
await sut.HandleAsync(authHandlerContext);
@@ -110,7 +122,7 @@ public class MediaPermissionsQueryStringHandlerTests
public async Task Node_Guid_Id_From_QueryString_Without_Permission_Is_Not_Authorized()
{
var authHandlerContext = CreateAuthorizationHandlerContext();
var mockHttpContextAccessor = CreateMockHttpContextAccessor(queryStringValue: s_nodeGuid.ToString());
var mockHttpContextAccessor = CreateMockHttpContextAccessorWithQueryStringValue(queryStringValue: s_nodeGuid.ToString());
var sut = CreateHandler(mockHttpContextAccessor.Object, NodeId, 1001);
await sut.HandleAsync(authHandlerContext);
@@ -123,7 +135,7 @@ public class MediaPermissionsQueryStringHandlerTests
public async Task Node_Invalid_Id_From_QueryString_Is_Authorized()
{
var authHandlerContext = CreateAuthorizationHandlerContext();
var mockHttpContextAccessor = CreateMockHttpContextAccessor(queryStringValue: "invalid");
var mockHttpContextAccessor = CreateMockHttpContextAccessorWithQueryStringValue(queryStringValue: "invalid");
var sut = CreateHandler(mockHttpContextAccessor.Object, NodeId);
await sut.HandleAsync(authHandlerContext);
@@ -139,14 +151,21 @@ public class MediaPermissionsQueryStringHandlerTests
return new AuthorizationHandlerContext(new List<IAuthorizationRequirement> { requirement }, user, resource);
}
private static Mock<IHttpContextAccessor> CreateMockHttpContextAccessor(
private static Mock<IHttpContextAccessor> CreateMockHttpContextAccessorWithQueryStringValue(
string queryStringName = QueryStringName,
string queryStringValue = "")
=> CreateMockHttpContextAccessorWithQueryStringValues(queryStringName, [queryStringValue]);
private static Mock<IHttpContextAccessor> CreateMockHttpContextAccessorWithQueryStringValues(
string queryStringName = QueryStringName,
string[]? queryStringValues = null)
{
queryStringValues ??= [];
var mockHttpContextAccessor = new Mock<IHttpContextAccessor>();
var mockHttpContext = new Mock<HttpContext>();
var mockHttpRequest = new Mock<HttpRequest>();
var queryParams = new Dictionary<string, StringValues> { { queryStringName, queryStringValue } };
var queryParams = new Dictionary<string, StringValues> { { queryStringName, new StringValues(queryStringValues) } };
mockHttpRequest.SetupGet(x => x.Query).Returns(new QueryCollection(queryParams));
mockHttpContext.SetupGet(x => x.Request).Returns(mockHttpRequest.Object);
mockHttpContext.SetupGet(x => x.Items).Returns(new Dictionary<object, object>());
@@ -178,7 +197,7 @@ public class MediaPermissionsQueryStringHandlerTests
mockEntityService
.Setup(x => x.GetId(
It.Is<Guid>(y => y == s_nodeGuid),
It.Is<UmbracoObjectTypes>(y => y == UmbracoObjectTypes.Document)))
It.Is<UmbracoObjectTypes>(y => y == UmbracoObjectTypes.Media)))
.Returns(Attempt<int>.Succeed(NodeId));
return mockEntityService;
}
@@ -44,7 +44,7 @@ public class MediaPermissionsResourceHandlerTests
}
[Test]
public async Task Resource_With_Node_Id_Withou_Permission_Is_Not_Authorized()
public async Task Resource_With_Node_Id_Without_Permission_Is_Not_Authorized()
{
var authHandlerContext = CreateAuthorizationHandlerContext(NodeId, true);
var sut = CreateHandler(NodeId, 1001);
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json",
"version": "13.7.0-rc",
"version": "13.7.2",
"assemblyVersion": {
"precision": "build"
},