From 8ad1d247fa5aea715e8bc128fb356375912166d1 Mon Sep 17 00:00:00 2001
From: Shannon
Date: Wed, 6 Sep 2017 11:49:00 +1000
Subject: [PATCH] U4-10389 Some webforms editors do not authorize on the user's
path access or permissions set for the editing node
---
src/Umbraco.Core/ActionsResolver.cs | 27 +-
src/Umbraco.Core/Models/UmbracoEntity.cs | 2 +
src/Umbraco.Core/Models/UserExtensions.cs | 7 +-
.../Trees/ContentTreeControllerBase.cs | 5 +-
.../UI/Pages/UmbracoEnsuredPage.cs | 36 ++
src/Umbraco.Web/Umbraco.Web.csproj | 3 -
.../umbraco/dialogs/AssignDomain2.aspx.cs | 19 +-
.../umbraco/dialogs/moveOrCopy.aspx.cs | 442 ------------------
.../umbraco/dialogs/sort.aspx.cs | 40 +-
.../BasePages/UmbracoEnsuredPage.cs | 38 +-
src/umbraco.cms/Actions/Action.cs | 25 +-
11 files changed, 155 insertions(+), 489 deletions(-)
delete mode 100644 src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/moveOrCopy.aspx.cs
diff --git a/src/Umbraco.Core/ActionsResolver.cs b/src/Umbraco.Core/ActionsResolver.cs
index 206182c6f2..ff2eaa8553 100644
--- a/src/Umbraco.Core/ActionsResolver.cs
+++ b/src/Umbraco.Core/ActionsResolver.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Linq;
using System.Reflection;
using Umbraco.Core.Logging;
@@ -34,7 +35,31 @@ namespace Umbraco.Core
{
return Values;
}
- }
+ }
+
+ ///
+ /// This method will return a list of IAction's based on a string (letter) list. Each character in the list may represent
+ /// an IAction. This will associate any found IActions based on the Letter property of the IAction with the character being referenced.
+ ///
+ ///
+ /// returns a list of actions that have an associated letter found in the action string list
+ public IEnumerable FromActionSymbols(IEnumerable actions)
+ {
+ var allActions = Actions.ToArray();
+ return actions
+ .Select(c => allActions.FirstOrDefault(a => a.Letter.ToString(CultureInfo.InvariantCulture) == c))
+ .WhereNotNull()
+ .ToArray();
+ }
+
+ ///
+ /// Returns the string (letter) representation of the actions that make up the actions collection
+ ///
+ ///
+ public IEnumerable ToActionSymbols(IEnumerable actions)
+ {
+ return actions.Select(x => x.Letter.ToString(CultureInfo.InvariantCulture)).ToArray();
+ }
///
/// Gets an Action if it exists.
diff --git a/src/Umbraco.Core/Models/UmbracoEntity.cs b/src/Umbraco.Core/Models/UmbracoEntity.cs
index 48752468d5..b789698704 100644
--- a/src/Umbraco.Core/Models/UmbracoEntity.cs
+++ b/src/Umbraco.Core/Models/UmbracoEntity.cs
@@ -51,6 +51,8 @@ namespace Umbraco.Core.Models
private string _contentTypeIcon;
private string _contentTypeThumbnail;
+ public static readonly UmbracoEntity Root = new UmbracoEntity(false) {Path = "-1", Name = "root", HasChildren = true};
+
public UmbracoEntity()
{
AdditionalData = new Dictionary();
diff --git a/src/Umbraco.Core/Models/UserExtensions.cs b/src/Umbraco.Core/Models/UserExtensions.cs
index b842fb8f54..7455d840c6 100644
--- a/src/Umbraco.Core/Models/UserExtensions.cs
+++ b/src/Umbraco.Core/Models/UserExtensions.cs
@@ -13,7 +13,12 @@ using Umbraco.Core.Services;
namespace Umbraco.Core.Models
{
public static class UserExtensions
- {
+ {
+ public static IEnumerable GetPermissions(this IUser user, string path, IUserService userService)
+ {
+ return userService.GetPermissionsForPath(user, path).GetAllPermissions();
+ }
+
public static bool HasSectionAccess(this IUser user, string app)
{
var apps = user.AllowedSections;
diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs
index fa18e703bd..1232aaf832 100644
--- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs
+++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs
@@ -271,8 +271,9 @@ namespace Umbraco.Web.Trees
internal IEnumerable
" + ui.Text("closeThisWindow") + "";
- feedback.type = uicontrols.Feedback.feedbacktype.success;
-
- // refresh tree
- ClientTools.MoveNode(currContent.Id.ToString(), currContent.Path);
- }
- else
- {
- //NOTE: We ONLY support Copy on content not media for some reason.
-
- var newContent = (IContent)currContent;
- Services.ContentService.Copy(newContent, Request.GetItemAs("copyTo"), RelateDocuments.Checked, UmbracoUser.Id);
-
- feedback.Text = ui.Text("moveOrCopy", "copyDone", nodes, UmbracoUser) + "
" + ui.Text("closeThisWindow") + "";
- feedback.type = uicontrols.Feedback.feedbacktype.success;
-
- // refresh tree
- ClientTools.CopyNode(currContent.Id.ToString(), newContent.Path);
- }
- }
- }
- }
-
- ///
- /// JsInclude1 control.
- ///
- ///
- /// Auto-generated field.
- /// To modify move field declaration from designer file to code-behind file.
- ///
- protected global::ClientDependency.Core.Controls.JsInclude JsInclude1;
-
- ///
- /// feedback control.
- ///
- ///
- /// Auto-generated field.
- /// To modify move field declaration from designer file to code-behind file.
- ///
- protected global::umbraco.uicontrols.Feedback feedback;
-
- ///
- /// pane_form control.
- ///
- ///
- /// Auto-generated field.
- /// To modify move field declaration from designer file to code-behind file.
- ///
- protected global::umbraco.uicontrols.Pane pane_form;
-
- ///
- /// JTree control.
- ///
- ///
- /// Auto-generated field.
- /// To modify move field declaration from designer file to code-behind file.
- ///
- protected global::umbraco.controls.Tree.TreeControl JTree;
-
- ///
- /// pp_relate control.
- ///
- ///
- /// Auto-generated field.
- /// To modify move field declaration from designer file to code-behind file.
- ///
- protected global::umbraco.uicontrols.PropertyPanel pp_relate;
-
- ///
- /// RelateDocuments control.
- ///
- ///
- /// Auto-generated field.
- /// To modify move field declaration from designer file to code-behind file.
- ///
- protected global::System.Web.UI.WebControls.CheckBox RelateDocuments;
-
- ///
- /// pane_form_notice control.
- ///
- ///
- /// Auto-generated field.
- /// To modify move field declaration from designer file to code-behind file.
- ///
- protected global::System.Web.UI.WebControls.PlaceHolder pane_form_notice;
-
- ///
- /// pane_settings control.
- ///
- ///
- /// Auto-generated field.
- /// To modify move field declaration from designer file to code-behind file.
- ///
- protected global::umbraco.uicontrols.Pane pane_settings;
-
- ///
- /// PropertyPanel1 control.
- ///
- ///
- /// Auto-generated field.
- /// To modify move field declaration from designer file to code-behind file.
- ///
- protected global::umbraco.uicontrols.PropertyPanel PropertyPanel1;
-
- ///
- /// masterType control.
- ///
- ///
- /// Auto-generated field.
- /// To modify move field declaration from designer file to code-behind file.
- ///
- protected global::System.Web.UI.WebControls.ListBox masterType;
-
- ///
- /// rename control.
- ///
- ///
- /// Auto-generated field.
- /// To modify move field declaration from designer file to code-behind file.
- ///
- protected global::System.Web.UI.WebControls.TextBox rename;
-
- ///
- /// RequiredFieldValidator1 control.
- ///
- ///
- /// Auto-generated field.
- /// To modify move field declaration from designer file to code-behind file.
- ///
- protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator1;
-
- ///
- /// panel_buttons control.
- ///
- ///
- /// Auto-generated field.
- /// To modify move field declaration from designer file to code-behind file.
- ///
- protected global::System.Web.UI.WebControls.Panel panel_buttons;
-
- ///
- /// ok control.
- ///
- ///
- /// Auto-generated field.
- /// To modify move field declaration from designer file to code-behind file.
- ///
- protected global::System.Web.UI.WebControls.Button ok;
-
- }
-}
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/sort.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/sort.aspx.cs
index f3dbb43519..f3bb4c8fdc 100644
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/sort.aspx.cs
+++ b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/sort.aspx.cs
@@ -13,6 +13,8 @@ using umbraco.cms.businesslogic.media;
using umbraco.cms.businesslogic.web;
using System.Web.UI;
using System.Collections.Generic;
+using umbraco.businesslogic.Exceptions;
+using Umbraco.Core.Models;
namespace umbraco.cms.presentation
{
@@ -20,7 +22,13 @@ namespace umbraco.cms.presentation
/// Summary description for sort.
///
public partial class sort : UmbracoEnsuredPage
- {
+ {
+ ///
+ /// The Parent Id being sorted
+ ///
+ protected int? ParentIdAsInt { get; private set; }
+ protected string ParentIdAsString { get; private set; }
+
private readonly List _nodes = new List();
protected bool HideDateColumn
@@ -33,6 +41,21 @@ namespace umbraco.cms.presentation
{
CurrentApp = helper.Request("app");
+ ParentIdAsString = Request.GetItemAsString("ID");
+ int parentId;
+ if (int.TryParse(ParentIdAsString, out parentId))
+ {
+ ParentIdAsInt = parentId;
+
+ if (CurrentApp == Constants.Applications.Content || CurrentApp == Constants.Applications.Media)
+ {
+ CheckPathAndPermissions(
+ ParentIdAsInt.Value,
+ CurrentApp == Constants.Applications.Content ? UmbracoObjectTypes.Document : UmbracoObjectTypes.Media,
+ ActionSort.Instance);
+ }
+ }
+
base.OnInit(e);
}
@@ -50,23 +73,22 @@ namespace umbraco.cms.presentation
var app = Request.GetItemAsString("app");
var icon = "../images/umbraco/doc.gif";
-
- int parentId;
- if (int.TryParse(Request.GetItemAsString("ID"), out parentId))
+
+ if (ParentIdAsInt.HasValue)
{
if (app == Constants.Applications.Media)
{
icon = "../images/umbraco/mediaPhoto.gif";
var mediaService = ApplicationContext.Current.Services.MediaService;
- if (parentId == -1)
+ if (ParentIdAsInt.Value == -1)
{
foreach (var child in mediaService.GetRootMedia().ToList().OrderBy(x => x.SortOrder))
_nodes.Add(CreateNode(child.Id.ToInvariantString(), child.SortOrder, child.Name, child.CreateDate, icon));
}
else
{
- var children = mediaService.GetChildren(parentId);
+ var children = mediaService.GetChildren(ParentIdAsInt.Value);
foreach (var child in children.OrderBy(x => x.SortOrder))
_nodes.Add(CreateNode(child.Id.ToInvariantString(), child.SortOrder, child.Name, child.CreateDate, icon));
}
@@ -76,14 +98,14 @@ namespace umbraco.cms.presentation
{
var contentService = ApplicationContext.Current.Services.ContentService;
- if (parentId == -1)
+ if (ParentIdAsInt.Value == -1)
{
foreach (var child in contentService.GetRootContent().ToList().OrderBy(x => x.SortOrder))
_nodes.Add(CreateNode(child.Id.ToInvariantString(), child.SortOrder, child.Name, child.CreateDate, icon));
}
else
{
- var children = contentService.GetChildren(parentId);
+ var children = contentService.GetChildren(ParentIdAsInt.Value);
foreach (var child in children)
_nodes.Add(CreateNode(child.Id.ToInvariantString(), child.SortOrder, child.Name, child.CreateDate, icon));
}
@@ -100,7 +122,7 @@ namespace umbraco.cms.presentation
HideDateColumn = true;
- var stylesheetName = Request.GetItemAsString("ID");
+ var stylesheetName = ParentIdAsString;
if (stylesheetName.IsNullOrWhiteSpace())throw new NullReferenceException("No Id passed in to editor");
var stylesheet = Services.FileService.GetStylesheetByName(stylesheetName.EnsureEndsWith(".css"));
if (stylesheet == null) throw new InvalidOperationException("No stylesheet found by name " + stylesheetName);
diff --git a/src/umbraco.businesslogic/BasePages/UmbracoEnsuredPage.cs b/src/umbraco.businesslogic/BasePages/UmbracoEnsuredPage.cs
index 58362d50f2..36a0f63f36 100644
--- a/src/umbraco.businesslogic/BasePages/UmbracoEnsuredPage.cs
+++ b/src/umbraco.businesslogic/BasePages/UmbracoEnsuredPage.cs
@@ -7,6 +7,8 @@ using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using umbraco.BusinessLogic;
using umbraco.businesslogic.Exceptions;
+using umbraco.interfaces;
+using Umbraco.Core.Models;
using Umbraco.Core.Security;
namespace umbraco.BasePages
@@ -16,7 +18,41 @@ namespace umbraco.BasePages
///
[Obsolete("This class has been superceded by Umbraco.Web.UI.Pages.UmbracoEnsuredPage")]
public class UmbracoEnsuredPage : BasePage
- {
+ {
+ ///
+ /// Performs an authorization check for the user against the requested entity/path and permission set, this is only relevant to content and media
+ ///
+ ///
+ ///
+ ///
+ protected void CheckPathAndPermissions(int entityId, UmbracoObjectTypes objectType, IAction actionToCheck)
+ {
+ if (objectType == UmbracoObjectTypes.Document || objectType == UmbracoObjectTypes.Media)
+ {
+ //check path access
+
+ var entity = entityId == Constants.System.Root
+ ? UmbracoEntity.Root
+ : Services.EntityService.Get(
+ entityId,
+ objectType);
+ var hasAccess = CurrentUser.UserEntity.HasPathAccess(
+ entity,
+ Services.EntityService,
+ objectType == UmbracoObjectTypes.Document ? Constants.System.RecycleBinContent : Constants.System.RecycleBinMedia);
+ if (hasAccess == false)
+ throw new UserAuthorizationException(string.Format("The current user doesn't have access to the path '{0}'", entity.Path));
+
+ //only documents have action permissions
+ if (objectType == UmbracoObjectTypes.Document)
+ {
+ var allowedActions = ActionsResolver.Current.FromActionSymbols(CurrentUser.UserEntity.GetPermissions(entity.Path, Services.UserService)).ToArray();
+ if (allowedActions.Contains(actionToCheck) == false)
+ throw new UserAuthorizationException(string.Format("The current user doesn't have permission to {0} on the path '{1}'", actionToCheck.Alias, entity.Path));
+ }
+ }
+ }
+
///
/// Checks if the page exists outside of the /umbraco route, in which case the request will not have been authenticated for the back office
/// so we'll force authentication.
diff --git a/src/umbraco.cms/Actions/Action.cs b/src/umbraco.cms/Actions/Action.cs
index 81c78ade1b..c7a9a6242b 100644
--- a/src/umbraco.cms/Actions/Action.cs
+++ b/src/umbraco.cms/Actions/Action.cs
@@ -128,30 +128,20 @@ namespace umbraco.BusinessLogic.Actions
///
///
/// returns a list of actions that have an associated letter found in the action string list
+ [Obsolete("Use ActionsResolver.Current.FromActionSymbols instead")]
public static List FromString(string actions)
{
- List list = new List();
- foreach (char c in actions.ToCharArray())
- {
- IAction action = ActionsResolver.Current.Actions.ToList().Find(
- delegate(IAction a)
- {
- return a.Letter == c;
- }
- );
- if (action != null)
- list.Add(action);
- }
- return list;
+ return ActionsResolver.Current.FromActionSymbols(actions.ToCharArray().Select(x => x.ToString())).ToList();
}
///
/// Returns the string representation of the actions that make up the actions collection
///
///
+ [Obsolete("Use ActionsResolver.Current.ToActionSymbols instead")]
public static string ToString(List actions)
{
- string[] strMenu = Array.ConvertAll(actions.ToArray(), delegate(IAction a) { return (a.Letter.ToString(CultureInfo.InvariantCulture)); });
+ string[] strMenu = Array.ConvertAll(actions.ToArray(), a => (a.Letter.ToString(CultureInfo.InvariantCulture)));
return string.Join("", strMenu);
}
@@ -161,12 +151,7 @@ namespace umbraco.BusinessLogic.Actions
///
public static List GetPermissionAssignable()
{
- return ActionsResolver.Current.Actions.ToList().FindAll(
- delegate(IAction a)
- {
- return (a.CanBePermissionAssigned);
- }
- );
+ return ActionsResolver.Current.Actions.ToList().FindAll(a => (a.CanBePermissionAssigned));
}
///