diff --git a/src/Umbraco.Core/Models/UserExtensions.cs b/src/Umbraco.Core/Models/UserExtensions.cs index d989876607..6c29ab56bb 100644 --- a/src/Umbraco.Core/Models/UserExtensions.cs +++ b/src/Umbraco.Core/Models/UserExtensions.cs @@ -193,19 +193,6 @@ namespace Umbraco.Core.Models // check for a start node in the path return startNodeIds.Any(x => formattedPath.Contains(string.Concat(",", x, ","))); - } - - internal static bool IsInBranchOfStartNode(this IUser user, IUmbracoEntity entity, IEntityService entityService, int recycleBinId, out bool hasPathAccess) - { - switch (recycleBinId) - { - case Constants.System.RecycleBinMedia: - return IsInBranchOfStartNode(entity.Path, user.CalculateMediaStartNodeIds(entityService), user.GetMediaStartNodePaths(entityService), out hasPathAccess); - case Constants.System.RecycleBinContent: - return IsInBranchOfStartNode(entity.Path, user.CalculateContentStartNodeIds(entityService), user.GetContentStartNodePaths(entityService), out hasPathAccess); - default: - throw new NotSupportedException("Path access is only determined on content or media"); - } } internal static bool IsInBranchOfStartNode(string path, int[] startNodeIds, string[] startNodePaths, out bool hasPathAccess) diff --git a/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs b/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs index 86594836db..f12af4d27a 100644 --- a/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs @@ -12,14 +12,18 @@ namespace Umbraco.Core.Persistence.Factories { internal class UmbracoEntityFactory { + private static readonly Lazy EntityProperties = new Lazy(() => typeof(IUmbracoEntity).GetPublicProperties().Select(x => x.Name).ToArray()); + + /// + /// Figure out what extra properties we have that are not on the IUmbracoEntity and add them to additional data + /// + /// + /// internal void AddAdditionalData(UmbracoEntity entity, IDictionary originalEntityProperties) - { - var entityProps = typeof(IUmbracoEntity).GetPublicProperties().Select(x => x.Name).ToArray(); - - //figure out what extra properties we have that are not on the IUmbracoEntity and add them to additional data + { foreach (var k in originalEntityProperties.Keys .Select(x => new { orig = x, title = x.ToCleanString(CleanStringType.PascalCase | CleanStringType.Ascii | CleanStringType.ConvertCase) }) - .Where(x => entityProps.InvariantContains(x.title) == false)) + .Where(x => EntityProperties.Value.InvariantContains(x.title) == false)) { entity.AdditionalData[k.title] = originalEntityProperties[k.orig]; } @@ -78,4 +82,4 @@ namespace Umbraco.Core.Persistence.Factories } } -} \ No newline at end of file +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 49f5e8b5d2..3b5d0aaff6 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -377,7 +377,7 @@ - + diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index 4e4086a3c6..d5c7c04235 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -87,11 +87,12 @@ namespace Umbraco.Web.Trees /// /// /// - internal TreeNode GetSingleTreeNodeWithAccessCheck(IUmbracoEntity e, string parentId, FormDataCollection queryStrings) + internal TreeNode GetSingleTreeNodeWithAccessCheck(IUmbracoEntity e, string parentId, FormDataCollection queryStrings, + int[] startNodeIds, string[] startNodePaths, bool ignoreUserStartNodes) { bool hasPathAccess; - var entityIsAncestorOfStartNodes = Security.CurrentUser.IsInBranchOfStartNode(e, Services.EntityService, RecycleBinId, out hasPathAccess); - if (IgnoreUserStartNodes(queryStrings) == false && entityIsAncestorOfStartNodes == false) + var entityIsAncestorOfStartNodes = UserExtensions.IsInBranchOfStartNode(e.Path, startNodeIds, startNodePaths, out hasPathAccess); + if (ignoreUserStartNodes == false && entityIsAncestorOfStartNodes == false) return null; var treeNode = GetSingleTreeNode(e, parentId, queryStrings); @@ -101,13 +102,30 @@ namespace Umbraco.Web.Trees //the node so we need to return null; return null; } - if (IgnoreUserStartNodes(queryStrings) == false && hasPathAccess == false) + if (ignoreUserStartNodes == false && hasPathAccess == false) { treeNode.AdditionalData["noAccess"] = true; } return treeNode; } + private void GetUserStartNodes(out int[] startNodeIds, out string[] startNodePaths) + { + switch (RecycleBinId) + { + case Constants.System.RecycleBinMedia: + startNodeIds = Security.CurrentUser.CalculateMediaStartNodeIds(Services.EntityService); + startNodePaths = Security.CurrentUser.GetMediaStartNodePaths(Services.EntityService); + break; + case Constants.System.RecycleBinContent: + startNodeIds = Security.CurrentUser.CalculateContentStartNodeIds(Services.EntityService); + startNodePaths = Security.CurrentUser.GetContentStartNodePaths(Services.EntityService); + break; + default: + throw new NotSupportedException("Path access is only determined on content or media"); + } + } + /// /// Returns the /// @@ -134,6 +152,8 @@ namespace Umbraco.Web.Trees ? queryStrings.GetValue(TreeQueryStringParameters.StartNodeId) : string.Empty; + var ignoreUserStartNodes = IgnoreUserStartNodes(queryStrings); + if (string.IsNullOrEmpty(startNodeId) == false && startNodeId != "undefined" && startNodeId != rootIdString) { // request has been made to render from a specific, non-root, start node @@ -141,7 +161,7 @@ namespace Umbraco.Web.Trees // ensure that the user has access to that node, otherwise return the empty tree nodes collection // TODO: in the future we could return a validation statement so we can have some UI to notify the user they don't have access - if (IgnoreUserStartNodes(queryStrings) == false && HasPathAccess(id, queryStrings) == false) + if (ignoreUserStartNodes == false && HasPathAccess(id, queryStrings) == false) { LogHelper.Warn("User " + Security.CurrentUser.Username + " does not have access to node with id " + id); return nodes; @@ -159,7 +179,11 @@ namespace Umbraco.Web.Trees // get child entities - if id is root, but user's start nodes do not contain the // root node, this returns the start nodes instead of root's children var entities = GetChildEntities(id, queryStrings).ToList(); - nodes.AddRange(entities.Select(x => GetSingleTreeNodeWithAccessCheck(x, id, queryStrings)).Where(x => x != null)); + + //get the current user start node/paths + GetUserStartNodes(out var userStartNodes, out var userStartNodePaths); + + nodes.AddRange(entities.Select(x => GetSingleTreeNodeWithAccessCheck(x, id, queryStrings, userStartNodes, userStartNodePaths, ignoreUserStartNodes)).Where(x => x != null)); // if the user does not have access to the root node, what we have is the start nodes, // but to provide some context we also need to add their topmost nodes when they are not @@ -170,7 +194,7 @@ namespace Umbraco.Web.Trees if (topNodeIds.Length > 0) { var topNodes = Services.EntityService.GetAll(UmbracoObjectType, topNodeIds.ToArray()); - nodes.AddRange(topNodes.Select(x => GetSingleTreeNodeWithAccessCheck(x, id, queryStrings)).Where(x => x != null)); + nodes.AddRange(topNodes.Select(x => GetSingleTreeNodeWithAccessCheck(x, id, queryStrings, userStartNodes, userStartNodePaths, ignoreUserStartNodes)).Where(x => x != null)); } } @@ -482,5 +506,21 @@ namespace Umbraco.Web.Trees } private readonly ConcurrentDictionary _entityCache = new ConcurrentDictionary(); + + /// + /// If the request should allows a user to choose nodes that they normally don't have access to + /// + /// + /// + internal bool IgnoreUserStartNodes(FormDataCollection queryStrings) + { + var dataTypeId = queryStrings.GetValue(TreeQueryStringParameters.DataTypeId); + if (dataTypeId.HasValue) + { + return Services.DataTypeService.IsDataTypeIgnoringUserStartNodes(dataTypeId.Value); + } + + return false; + } } } diff --git a/src/Umbraco.Web/Trees/TreeControllerBase.cs b/src/Umbraco.Web/Trees/TreeControllerBase.cs index 995cb031d0..8962a9ca10 100644 --- a/src/Umbraco.Web/Trees/TreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/TreeControllerBase.cs @@ -350,22 +350,6 @@ namespace Umbraco.Web.Trees return queryStrings.GetValue(TreeQueryStringParameters.IsDialog); } - /// - /// If the request should allows a user to choose nodes that they normally don't have access to - /// - /// - /// - protected bool IgnoreUserStartNodes(FormDataCollection queryStrings) - { - var dataTypeId = queryStrings.GetValue(TreeQueryStringParameters.DataTypeId); - if (dataTypeId.HasValue) - { - return Services.DataTypeService.IsDataTypeIgnoringUserStartNodes(dataTypeId.Value); - } - - return false; - } - /// /// An event that allows developers to modify the tree node collection that is being rendered ///