diff --git a/src/Umbraco.Web/Editors/SectionController.cs b/src/Umbraco.Web/Editors/SectionController.cs
index ae69d9da0a..0e96551d6f 100644
--- a/src/Umbraco.Web/Editors/SectionController.cs
+++ b/src/Umbraco.Web/Editors/SectionController.cs
@@ -3,7 +3,9 @@ using AutoMapper;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using System.Linq;
+using Umbraco.Core.Models;
using Umbraco.Web.Trees;
+using Section = Umbraco.Web.Models.ContentEditing.Section;
namespace Umbraco.Web.Editors
{
@@ -54,11 +56,19 @@ namespace Umbraco.Web.Editors
return sectionModels;
}
-
+
+ ///
+ /// Returns all the sections that the user has access to
+ ///
+ ///
public IEnumerable GetAllSections()
{
var sections = Services.SectionService.GetSections();
- return sections.Select(Mapper.Map);
+ var mapped = sections.Select(Mapper.Map);
+ if (Security.CurrentUser.IsAdmin())
+ return mapped;
+
+ return mapped.Where(x => Security.CurrentUser.AllowedSections.Contains(x.Alias)).ToArray();
}
}
diff --git a/src/Umbraco.Web/Editors/UserEditorAuthorizationHelper.cs b/src/Umbraco.Web/Editors/UserEditorAuthorizationHelper.cs
index ce3be3eb55..b43ea26797 100644
--- a/src/Umbraco.Web/Editors/UserEditorAuthorizationHelper.cs
+++ b/src/Umbraco.Web/Editors/UserEditorAuthorizationHelper.cs
@@ -31,18 +31,12 @@ namespace Umbraco.Web.Editors
/// The start media ids of the user being saved (can be null or empty)
/// The user aliases of the user being saved (can be null or empty)
///
- public Attempt AuthorizeActions(IUser currentUser,
+ public Attempt IsAuthorized(IUser currentUser,
IUser savingUser,
IEnumerable startContentIds, IEnumerable startMediaIds,
IEnumerable userGroupAliases)
{
- var currentIsAdmin = currentUser.IsAdmin();
-
- // a1) An admin can edit anything
- if (currentIsAdmin)
- return Attempt.Succeed();
-
- // a2) A non-admin cannot save an admin
+ // a) A non-admin cannot save an admin
if (savingUser != null)
{
@@ -50,17 +44,23 @@ namespace Umbraco.Web.Editors
return Attempt.Fail("The current user is not an administrator");
}
- // b0) A user cannot set a start node on another user that they don't have access to
+ // b) A user cannot set a start node on another user that they don't have access to, this even goes for admins
var pathResult = AuthorizePath(currentUser, startContentIds, startMediaIds);
if (pathResult == false)
return pathResult;
+
+ // c) an admin can manage any group or section access
+
+ var currentIsAdmin = currentUser.IsAdmin();
+ if (currentIsAdmin)
+ return Attempt.Succeed();
if (userGroupAliases != null)
{
var userGroups = _userService.GetUserGroupsByAlias(userGroupAliases.ToArray()).ToArray();
- // b1) A user cannot assign a group to another user that grants them access to a start node they don't have access to
+ // d) A user cannot assign a group to another user that grants them access to a start node they don't have access to
foreach (var group in userGroups)
{
pathResult = AuthorizePath(currentUser,
@@ -70,7 +70,7 @@ namespace Umbraco.Web.Editors
return pathResult;
}
- // c) A user cannot set a section on another user that they don't have access to
+ // e) A user cannot set a section on another user that they don't have access to
var allGroupSections = userGroups.SelectMany(x => x.AllowedSections).Distinct();
var missingSectionAccess = allGroupSections.Except(currentUser.AllowedSections).ToArray();
if (missingSectionAccess.Length > 0)
diff --git a/src/Umbraco.Web/Editors/UserGroupAuthorizationAttribute.cs b/src/Umbraco.Web/Editors/UserGroupAuthorizationAttribute.cs
index 70ec52a0bb..a7da90c740 100644
--- a/src/Umbraco.Web/Editors/UserGroupAuthorizationAttribute.cs
+++ b/src/Umbraco.Web/Editors/UserGroupAuthorizationAttribute.cs
@@ -1,11 +1,10 @@
using System;
using System.Linq;
+using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Controllers;
using Umbraco.Core;
-using Umbraco.Core.Models;
-using Umbraco.Core.Models.Membership;
namespace Umbraco.Web.Editors
{
@@ -41,29 +40,22 @@ namespace Umbraco.Web.Editors
protected override bool IsAuthorized(HttpActionContext actionContext)
{
- var currentUser = GetUmbracoContext().Security.CurrentUser;
-
- //admins can access any group
- if (currentUser.IsAdmin())
- return base.IsAuthorized(actionContext);
-
- var queryString = actionContext.Request
- .GetQueryNameValuePairs();
+ var umbCtx = GetUmbracoContext();
+ var currentUser = umbCtx.Security.CurrentUser;
+
+ var queryString = actionContext.Request.GetQueryNameValuePairs();
var ids = queryString.Where(x => x.Key == _paramName).ToArray();
if (ids.Length == 0)
return base.IsAuthorized(actionContext);
var intIds = ids.Select(x => x.Value.TryConvertTo()).Where(x => x.Success).Select(x => x.Result).ToArray();
- return AuthorizeGroupAccess(currentUser, intIds);
- }
-
- private bool AuthorizeGroupAccess(IUser currentUser, params int[] groupIds)
- {
- var groups = GetUmbracoContext().Application.Services.UserService.GetAllUserGroups(groupIds);
- var groupAliases = groups.Select(x => x.Alias).ToArray();
- var userGroups = currentUser.Groups.Select(x => x.Alias).ToArray();
- return userGroups.ContainsAll(groupAliases);
+ var authHelper = new UserGroupEditorAuthorizationHelper(
+ umbCtx.Application.Services.UserService,
+ umbCtx.Application.Services.ContentService,
+ umbCtx.Application.Services.MediaService,
+ umbCtx.Application.Services.EntityService);
+ return authHelper.AuthorizeGroupAccess(currentUser, intIds);
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Editors/UserGroupEditorAuthorizationHelper.cs b/src/Umbraco.Web/Editors/UserGroupEditorAuthorizationHelper.cs
new file mode 100644
index 0000000000..6bc39b376d
--- /dev/null
+++ b/src/Umbraco.Web/Editors/UserGroupEditorAuthorizationHelper.cs
@@ -0,0 +1,122 @@
+using System.Collections.Generic;
+using System.Linq;
+using Umbraco.Core;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.Membership;
+using Umbraco.Core.Services;
+
+namespace Umbraco.Web.Editors
+{
+ internal class UserGroupEditorAuthorizationHelper
+ {
+ private readonly IUserService _userService;
+ private readonly IContentService _contentService;
+ private readonly IMediaService _mediaService;
+ private readonly IEntityService _entityService;
+
+ public UserGroupEditorAuthorizationHelper(IUserService userService, IContentService contentService, IMediaService mediaService, IEntityService entityService)
+ {
+ _userService = userService;
+ _contentService = contentService;
+ _mediaService = mediaService;
+ _entityService = entityService;
+ }
+
+ ///
+ /// Authorize that the current user belongs to these groups
+ ///
+ ///
+ ///
+ ///
+ public Attempt AuthorizeGroupAccess(IUser currentUser, params int[] groupIds)
+ {
+ if (currentUser.IsAdmin())
+ return Attempt.Succeed();
+
+ var groups = _userService.GetAllUserGroups(groupIds.ToArray());
+ var groupAliases = groups.Select(x => x.Alias).ToArray();
+ var userGroups = currentUser.Groups.Select(x => x.Alias).ToArray();
+ var missingAccess = groupAliases.Except(userGroups).ToArray();
+ return missingAccess.Length == 0
+ ? Attempt.Succeed()
+ : Attempt.Fail("User is not a member of " + string.Join(", ", missingAccess));
+ }
+
+ ///
+ /// Authorize that the current user belongs to these groups
+ ///
+ ///
+ ///
+ ///
+ public Attempt AuthorizeGroupAccess(IUser currentUser, params string[] groupAliases)
+ {
+ if (currentUser.IsAdmin())
+ return Attempt.Succeed();
+
+ var userGroups = currentUser.Groups.Select(x => x.Alias).ToArray();
+ var missingAccess = groupAliases.Except(userGroups).ToArray();
+ return missingAccess.Length == 0
+ ? Attempt.Succeed()
+ : Attempt.Fail("User is not a member of " + string.Join(", ", missingAccess));
+ }
+
+ ///
+ /// Authorize that the user is not adding a section to the group that they don't have access to
+ ///
+ ///
+ ///
+ ///
+ ///
+ public Attempt AuthorizeSectionChanges(IUser currentUser,
+ IEnumerable currentAllowedSections,
+ IEnumerable proposedAllowedSections)
+ {
+ if (currentUser.IsAdmin())
+ return Attempt.Succeed();
+
+ var sectionsAdded = currentAllowedSections.Except(proposedAllowedSections).ToArray();
+ var sectionAccessMissing = sectionsAdded.Except(currentUser.AllowedSections).ToArray();
+ return sectionAccessMissing.Length > 0
+ ? Attempt.Fail("Current user doesn't have access to add these sections " + string.Join(", ", sectionAccessMissing))
+ : Attempt.Succeed();
+ }
+
+ ///
+ /// Authorize that the user is not changing to a start node that they don't have access to (including admins)
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public Attempt AuthorizeStartNodeChanges(IUser currentUser,
+ int? currentContentStartId,
+ int? proposedContentStartId,
+ int? currentMediaStartId,
+ int? proposedMediaStartId)
+ {
+ if (currentContentStartId != proposedContentStartId && proposedContentStartId.HasValue)
+ {
+ var content = _contentService.GetById(proposedContentStartId.Value);
+ if (content != null)
+ {
+ if (currentUser.HasPathAccess(content, _entityService) == false)
+ return Attempt.Fail("Current user doesn't have access to the content path " + content.Path);
+ }
+ }
+
+ if (currentMediaStartId != proposedMediaStartId && proposedMediaStartId.HasValue)
+ {
+ var media = _mediaService.GetById(proposedMediaStartId.Value);
+ if (media != null)
+ {
+ if (currentUser.HasPathAccess(media, _entityService) == false)
+ return Attempt.Fail("Current user doesn't have access to the media path " + media.Path);
+ }
+ }
+
+ return Attempt.Succeed();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Editors/UserGroupsController.cs b/src/Umbraco.Web/Editors/UserGroupsController.cs
index 5934ee63eb..9db73a82a0 100644
--- a/src/Umbraco.Web/Editors/UserGroupsController.cs
+++ b/src/Umbraco.Web/Editors/UserGroupsController.cs
@@ -6,6 +6,7 @@ using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Filters;
using AutoMapper;
+using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Services;
using Umbraco.Web.Models.ContentEditing;
@@ -26,9 +27,32 @@ namespace Umbraco.Web.Editors
{
if (userGroupSave == null) throw new ArgumentNullException("userGroupSave");
+ //authorize that the user has access to save this user group
+ var authHelper = new UserGroupEditorAuthorizationHelper(
+ Services.UserService, Services.ContentService, Services.MediaService, Services.EntityService);
+ var isAuthorized = authHelper.AuthorizeGroupAccess(Security.CurrentUser, userGroupSave.Alias);
+ if (isAuthorized == false)
+ throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, isAuthorized.Result));
+
+ //if sections were added we need to check that the current user has access to that section
+ isAuthorized = authHelper.AuthorizeSectionChanges(Security.CurrentUser,
+ userGroupSave.PersistedUserGroup.AllowedSections,
+ userGroupSave.Sections);
+ if (isAuthorized == false)
+ throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, isAuthorized.Result));
+
+ //if start nodes were changed we need to check that the current user has access to them
+ isAuthorized = authHelper.AuthorizeStartNodeChanges(Security.CurrentUser,
+ userGroupSave.PersistedUserGroup.StartContentId,
+ userGroupSave.StartContentId,
+ userGroupSave.PersistedUserGroup.StartMediaId,
+ userGroupSave.StartMediaId);
+ if (isAuthorized == false)
+ throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, isAuthorized.Result));
+
//save the group
Services.UserService.Save(userGroupSave.PersistedUserGroup, userGroupSave.Users.ToArray());
-
+
//deal with permissions
//remove ones that have been removed
@@ -71,6 +95,11 @@ namespace Umbraco.Web.Editors
public IEnumerable GetUserGroups()
{
var allGroups = Mapper.Map, IEnumerable>(Services.UserService.GetAllUserGroups());
+
+ //if admin, return all groups
+ if (Security.CurrentUser.IsAdmin())
+ return allGroups;
+
//we cannot return user groups that this user does not have access to
var currentUserGroups = Security.CurrentUser.Groups.Select(x => x.Alias).ToArray();
return allGroups.Where(x => currentUserGroups.Contains(x.Alias)).ToArray();
diff --git a/src/Umbraco.Web/Editors/UsersController.cs b/src/Umbraco.Web/Editors/UsersController.cs
index d304112cb5..0bcc6b7cf9 100644
--- a/src/Umbraco.Web/Editors/UsersController.cs
+++ b/src/Umbraco.Web/Editors/UsersController.cs
@@ -271,7 +271,7 @@ namespace Umbraco.Web.Editors
//Perform authorization here to see if the current user can actually save this user with the info being requested
var authHelper = new UserEditorAuthorizationHelper(Services.ContentService, Services.MediaService, Services.UserService, Services.EntityService);
- var canSaveUser = authHelper.AuthorizeActions(Security.CurrentUser, null, null, null, userSave.UserGroups);
+ var canSaveUser = authHelper.IsAuthorized(Security.CurrentUser, null, null, null, userSave.UserGroups);
if (canSaveUser == false)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, canSaveUser.Result));
@@ -358,7 +358,7 @@ namespace Umbraco.Web.Editors
//Perform authorization here to see if the current user can actually save this user with the info being requested
var authHelper = new UserEditorAuthorizationHelper(Services.ContentService, Services.MediaService, Services.UserService, Services.EntityService);
- var canSaveUser = authHelper.AuthorizeActions(Security.CurrentUser, user, null, null, userSave.UserGroups);
+ var canSaveUser = authHelper.IsAuthorized(Security.CurrentUser, user, null, null, userSave.UserGroups);
if (canSaveUser == false)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, canSaveUser.Result));
@@ -471,7 +471,7 @@ namespace Umbraco.Web.Editors
//Perform authorization here to see if the current user can actually save this user with the info being requested
var authHelper = new UserEditorAuthorizationHelper(Services.ContentService, Services.MediaService, Services.UserService, Services.EntityService);
- var canSaveUser = authHelper.AuthorizeActions(Security.CurrentUser, found, userSave.StartContentIds, userSave.StartMediaIds, userSave.UserGroups);
+ var canSaveUser = authHelper.IsAuthorized(Security.CurrentUser, found, userSave.StartContentIds, userSave.StartMediaIds, userSave.UserGroups);
if (canSaveUser == false)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, canSaveUser.Result));
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index aa9957b329..a210c269d0 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -330,6 +330,7 @@
+