diff --git a/src/Umbraco.Core/Security/ContentPermissions.cs b/src/Umbraco.Core/Security/ContentPermissions.cs index b598897133..25ecc546c0 100644 --- a/src/Umbraco.Core/Security/ContentPermissions.cs +++ b/src/Umbraco.Core/Security/ContentPermissions.cs @@ -59,7 +59,7 @@ namespace Umbraco.Core.Security return ContentAccess.Granted; //get the implicit/inherited permissions for the user for this path - return CheckPermissionsPath(content.Path, user) + return CheckPermissionsPath(content.Path, user, permissionsToCheck) ? ContentAccess.Granted : ContentAccess.Denied; } @@ -87,7 +87,7 @@ namespace Umbraco.Core.Security return ContentAccess.Granted; //get the implicit/inherited permissions for the user for this path - return CheckPermissionsPath(entity.Path, user) + return CheckPermissionsPath(entity.Path, user, permissionsToCheck) ? ContentAccess.Granted : ContentAccess.Denied; } diff --git a/src/Umbraco.Tests.Common/Builders/ContentBuilder.cs b/src/Umbraco.Tests.Common/Builders/ContentBuilder.cs index 283b2b6c04..8cd95e8baf 100644 --- a/src/Umbraco.Tests.Common/Builders/ContentBuilder.cs +++ b/src/Umbraco.Tests.Common/Builders/ContentBuilder.cs @@ -175,9 +175,10 @@ namespace Umbraco.Tests.Common.Builders return content; } - public static Content CreateBasicContent(IContentType contentType) + public static Content CreateBasicContent(IContentType contentType, int id = 0) { return new ContentBuilder() + .WithId(id) .WithContentType(contentType) .WithName("Home") .Build(); diff --git a/src/Umbraco.Tests.Common/Builders/MediaBuilder.cs b/src/Umbraco.Tests.Common/Builders/MediaBuilder.cs index 8a0c8c4b4a..d9ca6480bc 100644 --- a/src/Umbraco.Tests.Common/Builders/MediaBuilder.cs +++ b/src/Umbraco.Tests.Common/Builders/MediaBuilder.cs @@ -123,9 +123,10 @@ namespace Umbraco.Tests.Common.Builders return media; } - public static Media CreateSimpleMedia(IMediaType mediaType, string name, int parentId) + public static Media CreateSimpleMedia(IMediaType mediaType, string name, int parentId, int id = 0) { return new MediaBuilder() + .WithId(id) .WithName(name) .WithMediaType(mediaType) .WithParentId(parentId) diff --git a/src/Umbraco.Tests.Common/Builders/UserBuilder.cs b/src/Umbraco.Tests.Common/Builders/UserBuilder.cs index 46f45b0c8c..0995622841 100644 --- a/src/Umbraco.Tests.Common/Builders/UserBuilder.cs +++ b/src/Umbraco.Tests.Common/Builders/UserBuilder.cs @@ -100,12 +100,24 @@ namespace Umbraco.Tests.Common.Builders return this; } + public UserBuilder WithStartContentId(int startContentId) + { + _startContentIds = new[] { startContentId }; + return this; + } + public UserBuilder WithStartContentIds(int[] startContentIds) { _startContentIds = startContentIds; return this; } + public UserBuilder WithStartMediaId(int startMediaId) + { + _startMediaIds = new[] { startMediaId }; + return this; + } + public UserBuilder WithStartMediaIds(int[] startMediaIds) { _startMediaIds = startMediaIds; @@ -151,8 +163,8 @@ namespace Umbraco.Tests.Common.Builders var lastPasswordChangeDate = _lastPasswordChangeDate ?? DateTime.Now; var comments = _comments ?? string.Empty; var sessionTimeout = _sessionTimeout ?? 0; - var startContentIds = _startContentIds ?? new int[0]; - var startMediaIds = _startMediaIds ?? new int[0]; + var startContentIds = _startContentIds ?? new[] { -1 }; + var startMediaIds = _startMediaIds ?? new[] { -1 }; var groups = _userGroupBuilders.Select(x => x.Build()); var result = new User( diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResourceHandlerTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResourceHandlerTests.cs new file mode 100644 index 0000000000..ea6dd7f0bd --- /dev/null +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResourceHandlerTests.cs @@ -0,0 +1,123 @@ +using System.Collections.Generic; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Moq; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Security; +using Umbraco.Core.Services; +using Umbraco.Tests.Common.Builders; +using Umbraco.Web.BackOffice.Authorization; + +namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Authorization +{ + public class ContentPermissionsResourceHandlerTests + { + private const int NodeId = 1000; + + [Test] + public async Task Resource_With_Node_Id_With_Permission_Is_Authorized() + { + var authHandlerContext = CreateAuthorizationHandlerContext(NodeId, createWithNodeId: true); + var sut = CreateHandler(NodeId, new string[] { "A" }); + + await sut.HandleAsync(authHandlerContext); + + Assert.IsTrue(authHandlerContext.HasSucceeded); + } + + [Test] + public async Task Resource_With_Content_With_Permission_Is_Authorized() + { + var authHandlerContext = CreateAuthorizationHandlerContext(NodeId); + var sut = CreateHandler(NodeId, new string[] { "A" }); + + await sut.HandleAsync(authHandlerContext); + + Assert.IsTrue(authHandlerContext.HasSucceeded); + } + + [Test] + public async Task Resource_With_Node_Id_Withou_Permission_Is_Not_Authorized() + { + var authHandlerContext = CreateAuthorizationHandlerContext(NodeId, createWithNodeId: true); + var sut = CreateHandler(NodeId, new string[] { "B" }); + + await sut.HandleAsync(authHandlerContext); + + Assert.IsFalse(authHandlerContext.HasSucceeded); + } + + [Test] + public async Task Resource_With_Content_Without_Permission_Is_Not_Authorized() + { + var authHandlerContext = CreateAuthorizationHandlerContext(NodeId); + var sut = CreateHandler(NodeId, new string[] { "B" }); + + await sut.HandleAsync(authHandlerContext); + + Assert.IsFalse(authHandlerContext.HasSucceeded); + } + + private static AuthorizationHandlerContext CreateAuthorizationHandlerContext(int nodeId, bool createWithNodeId = false) + { + var requirement = new ContentPermissionsResourceRequirement(); + var user = new ClaimsPrincipal(new ClaimsIdentity(new List())); + var content = CreateContent(nodeId); + var permissions = new List { 'A' }.AsReadOnly(); + var resource = createWithNodeId + ? new ContentPermissionsResource(content, nodeId, permissions) + : new ContentPermissionsResource(content, permissions); + return new AuthorizationHandlerContext(new List { requirement }, user, resource); + } + + private static IContent CreateContent(int nodeId) + { + var contentType = ContentTypeBuilder.CreateBasicContentType(); + return ContentBuilder.CreateBasicContent(contentType, nodeId); + } + + private ContentPermissionsResourceHandler CreateHandler(int nodeId, string[] permissionsForPath) + { + var mockBackOfficeSecurityAccessor = CreateMockBackOfficeSecurityAccessor(); + var contentPermissions = CreateContentPermissions(nodeId, permissionsForPath); + return new ContentPermissionsResourceHandler(mockBackOfficeSecurityAccessor.Object, contentPermissions); + } + + private static Mock CreateMockBackOfficeSecurityAccessor() + { + var user = CreateUser(); + var mockBackOfficeSecurity = new Mock(); + mockBackOfficeSecurity.SetupGet(x => x.CurrentUser).Returns(user); + var mockBackOfficeSecurityAccessor = new Mock(); + mockBackOfficeSecurityAccessor.Setup(x => x.BackOfficeSecurity).Returns(mockBackOfficeSecurity.Object); + return mockBackOfficeSecurityAccessor; + } + + private static User CreateUser() + { + return new UserBuilder() + .Build(); + } + + private static ContentPermissions CreateContentPermissions(int nodeId, string[] permissionsForPath) + { + var mockUserService = new Mock(); + + mockUserService + .Setup(x => x.GetPermissionsForPath(It.IsAny(), It.Is(y => y == $"{Constants.System.Root},{nodeId}"))) + .Returns(new EntityPermissionSet(nodeId, new EntityPermissionCollection(new List { new EntityPermission(1, nodeId, permissionsForPath) }))); + + var mockContentService = new Mock(); + mockContentService + .Setup(x => x.GetById(It.Is(y => y == nodeId))) + .Returns(CreateContent(nodeId)); + + var mockEntityService = new Mock(); + return new ContentPermissions(mockUserService.Object, mockContentService.Object, mockEntityService.Object); + } + } +} diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Authorization/DenyLocalLoginHandlerTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Authorization/DenyLocalLoginHandlerTests.cs index 03fcc5e665..8cf091e3f6 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Authorization/DenyLocalLoginHandlerTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Authorization/DenyLocalLoginHandlerTests.cs @@ -4,10 +4,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; using Moq; using NUnit.Framework; -using Umbraco.Core; -using Umbraco.Core.Models.Membership; -using Umbraco.Core.Security; -using Umbraco.Tests.Common.Builders; using Umbraco.Web.BackOffice.Authorization; using Umbraco.Web.Common.Security; diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResourceHandlerTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResourceHandlerTests.cs new file mode 100644 index 0000000000..8db98dc081 --- /dev/null +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Authorization/MediaPermissionsResourceHandlerTests.cs @@ -0,0 +1,116 @@ +using System.Collections.Generic; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Security; +using Umbraco.Core.Services; +using Umbraco.Tests.Common.Builders; +using Umbraco.Web.BackOffice.Authorization; + +namespace Umbraco.Tests.UnitTests.Umbraco.Web.BackOffice.Authorization +{ + public class MediaPermissionsResourceHandlerTests + { + private const int NodeId = 1000; + + [Test] + public async Task Resource_With_Node_Id_With_Permission_Is_Authorized() + { + var authHandlerContext = CreateAuthorizationHandlerContext(NodeId, createWithNodeId: true); + var sut = CreateHandler(NodeId); + + await sut.HandleAsync(authHandlerContext); + + Assert.IsTrue(authHandlerContext.HasSucceeded); + } + + [Test] + public async Task Resource_With_Media_With_Permission_Is_Authorized() + { + var authHandlerContext = CreateAuthorizationHandlerContext(NodeId); + var sut = CreateHandler(NodeId); + + await sut.HandleAsync(authHandlerContext); + + Assert.IsTrue(authHandlerContext.HasSucceeded); + } + + [Test] + public async Task Resource_With_Node_Id_Withou_Permission_Is_Not_Authorized() + { + var authHandlerContext = CreateAuthorizationHandlerContext(NodeId, createWithNodeId: true); + var sut = CreateHandler(NodeId, startMediaId: 1001); + + await sut.HandleAsync(authHandlerContext); + + Assert.IsFalse(authHandlerContext.HasSucceeded); + } + + [Test] + public async Task Resource_With_Media_Without_Permission_Is_Not_Authorized() + { + var authHandlerContext = CreateAuthorizationHandlerContext(NodeId); + var sut = CreateHandler(NodeId, startMediaId: 1001); + + await sut.HandleAsync(authHandlerContext); + + Assert.IsFalse(authHandlerContext.HasSucceeded); + } + + private static AuthorizationHandlerContext CreateAuthorizationHandlerContext(int nodeId, bool createWithNodeId = false) + { + var requirement = new MediaPermissionsResourceRequirement(); + var user = new ClaimsPrincipal(new ClaimsIdentity(new List())); + var media = CreateMedia(nodeId); + var resource = createWithNodeId + ? new MediaPermissionsResource(nodeId) + : new MediaPermissionsResource(media); + return new AuthorizationHandlerContext(new List { requirement }, user, resource); + } + + private static IMedia CreateMedia(int nodeId) + { + var mediaType = MediaTypeBuilder.CreateSimpleMediaType("image", "Image"); + return MediaBuilder.CreateSimpleMedia(mediaType, "Test image", -1); + } + + private MediaPermissionsResourceHandler CreateHandler(int nodeId, int startMediaId = -1) + { + var mockBackOfficeSecurityAccessor = CreateMockBackOfficeSecurityAccessor(startMediaId); + var contentPermissions = CreateMediaPermissions(nodeId, new string[0]); + return new MediaPermissionsResourceHandler(mockBackOfficeSecurityAccessor.Object, contentPermissions); + } + + private static Mock CreateMockBackOfficeSecurityAccessor(int startMediaId) + { + var user = CreateUser(startMediaId); + var mockBackOfficeSecurity = new Mock(); + mockBackOfficeSecurity.SetupGet(x => x.CurrentUser).Returns(user); + var mockBackOfficeSecurityAccessor = new Mock(); + mockBackOfficeSecurityAccessor.Setup(x => x.BackOfficeSecurity).Returns(mockBackOfficeSecurity.Object); + return mockBackOfficeSecurityAccessor; + } + + private static User CreateUser(int startMediaId) + { + return new UserBuilder() + .WithStartMediaId(startMediaId) + .Build(); + } + + private static MediaPermissions CreateMediaPermissions(int nodeId, string[] permissionsForPath) + { + var mockMediaService = new Mock(); + mockMediaService + .Setup(x => x.GetById(It.Is(y => y == nodeId))) + .Returns(CreateMedia(nodeId)); + + var mockEntityService = new Mock(); + return new MediaPermissions(mockMediaService.Object, mockEntityService.Object); + } + } +} diff --git a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResourceHandler.cs b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResourceHandler.cs index bcd8ef9add..3bade3c5fe 100644 --- a/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResourceHandler.cs +++ b/src/Umbraco.Web.BackOffice/Authorization/ContentPermissionsResourceHandler.cs @@ -1,13 +1,12 @@ -using Microsoft.AspNetCore.Authorization; -using System.Threading.Tasks; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; using Umbraco.Core.Models; using Umbraco.Core.Security; -using Umbraco.Core.Services; namespace Umbraco.Web.BackOffice.Authorization { /// - /// Used to authorize if the user has the correct permission access to the content for the specified + /// Used to authorize if the user has the correct permission access to the content for the specified. /// public class ContentPermissionsResourceHandler : MustSatisfyRequirementAuthorizationHandler {