Updates the user auth helper and adds unit tests for it's scenarios
This commit is contained in:
@@ -196,6 +196,7 @@
|
||||
<Compile Include="TestHelpers\ControllerTesting\TestRunner.cs" />
|
||||
<Compile Include="TestHelpers\ControllerTesting\TestStartup.cs" />
|
||||
<Compile Include="TestHelpers\ControllerTesting\TraceExceptionLogger.cs" />
|
||||
<Compile Include="Web\Controllers\UserEditorAuthorizationHelperTests.cs" />
|
||||
<Compile Include="Web\Controllers\UsersControllerTests.cs" />
|
||||
<Compile Include="Dependencies\NuGet.cs" />
|
||||
<Compile Include="CallContextTests.cs" />
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Editors;
|
||||
|
||||
namespace Umbraco.Tests.Web.Controllers
|
||||
{
|
||||
[TestFixture]
|
||||
public class UserEditorAuthorizationHelperTests
|
||||
{
|
||||
[Test]
|
||||
public void Admin_Is_Authorized()
|
||||
{
|
||||
var currentUser = GetAdminUser();
|
||||
var savingUser = Mock.Of<IUser>();
|
||||
|
||||
var contentService = new Mock<IContentService>();
|
||||
var mediaService = new Mock<IMediaService>();
|
||||
var userService = new Mock<IUserService>();
|
||||
var entityService = new Mock<IEntityService>();
|
||||
|
||||
var authHelper = new UserEditorAuthorizationHelper(
|
||||
contentService.Object,
|
||||
mediaService.Object,
|
||||
userService.Object,
|
||||
entityService.Object);
|
||||
|
||||
var result = authHelper.IsAuthorized(currentUser, savingUser, new int[0], new int[0], new string[0]);
|
||||
|
||||
Assert.IsTrue(result.Success);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Non_Admin_Cannot_Save_Admin()
|
||||
{
|
||||
var currentUser = Mock.Of<IUser>();
|
||||
var savingUser = GetAdminUser();
|
||||
|
||||
var contentService = new Mock<IContentService>();
|
||||
var mediaService = new Mock<IMediaService>();
|
||||
var userService = new Mock<IUserService>();
|
||||
var entityService = new Mock<IEntityService>();
|
||||
|
||||
var authHelper = new UserEditorAuthorizationHelper(
|
||||
contentService.Object,
|
||||
mediaService.Object,
|
||||
userService.Object,
|
||||
entityService.Object);
|
||||
|
||||
var result = authHelper.IsAuthorized(currentUser, savingUser, new int[0], new int[0], new string[0]);
|
||||
|
||||
Assert.IsFalse(result.Success);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Cannot_Grant_Group_Membership_Without_Being_A_Member()
|
||||
{
|
||||
var currentUser = Mock.Of<IUser>(user => user.Groups == new[]
|
||||
{
|
||||
new ReadOnlyUserGroup(1, "Test", "icon-user", null, null, "test", new string[0], new string[0])
|
||||
});
|
||||
var savingUser = Mock.Of<IUser>();
|
||||
|
||||
var contentService = new Mock<IContentService>();
|
||||
var mediaService = new Mock<IMediaService>();
|
||||
var userService = new Mock<IUserService>();
|
||||
var entityService = new Mock<IEntityService>();
|
||||
|
||||
var authHelper = new UserEditorAuthorizationHelper(
|
||||
contentService.Object,
|
||||
mediaService.Object,
|
||||
userService.Object,
|
||||
entityService.Object);
|
||||
|
||||
var result = authHelper.IsAuthorized(currentUser, savingUser, new int[0], new int[0], new[] {"FunGroup"});
|
||||
|
||||
Assert.IsFalse(result.Success);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Grant_Group_Membership_With_Being_A_Member()
|
||||
{
|
||||
var currentUser = Mock.Of<IUser>(user => user.Groups == new[]
|
||||
{
|
||||
new ReadOnlyUserGroup(1, "Test", "icon-user", null, null, "test", new string[0], new string[0])
|
||||
});
|
||||
var savingUser = Mock.Of<IUser>();
|
||||
|
||||
var contentService = new Mock<IContentService>();
|
||||
var mediaService = new Mock<IMediaService>();
|
||||
var userService = new Mock<IUserService>();
|
||||
var entityService = new Mock<IEntityService>();
|
||||
|
||||
var authHelper = new UserEditorAuthorizationHelper(
|
||||
contentService.Object,
|
||||
mediaService.Object,
|
||||
userService.Object,
|
||||
entityService.Object);
|
||||
|
||||
var result = authHelper.IsAuthorized(currentUser, savingUser, new int[0], new int[0], new[] { "test" });
|
||||
|
||||
Assert.IsTrue(result.Success);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Cannot_Grant_Content_Start_Node_On_User_Without_Access()
|
||||
{
|
||||
var currentUser = Mock.Of<IUser>(user => user.StartContentIds == new[]{9876});
|
||||
var savingUser = Mock.Of<IUser>();
|
||||
|
||||
var contentService = new Mock<IContentService>();
|
||||
contentService.Setup(x => x.GetById(It.IsAny<int>())).Returns(Mock.Of<IContent>(content => content.Path == "-1,1234"));
|
||||
var mediaService = new Mock<IMediaService>();
|
||||
var userService = new Mock<IUserService>();
|
||||
var entityService = new Mock<IEntityService>();
|
||||
entityService.Setup(service => service.GetAllPaths(It.IsAny<UmbracoObjectTypes>(), It.IsAny<int[]>()))
|
||||
.Returns(new[] { new EntityPath() { Path = "-1,9876", Id = 9876 } });
|
||||
|
||||
var authHelper = new UserEditorAuthorizationHelper(
|
||||
contentService.Object,
|
||||
mediaService.Object,
|
||||
userService.Object,
|
||||
entityService.Object);
|
||||
|
||||
var result = authHelper.IsAuthorized(currentUser, savingUser, new []{1234}, new int[0], new string[0]);
|
||||
|
||||
Assert.IsFalse(result.Success);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Grant_Content_Start_Node_On_User_With_Access()
|
||||
{
|
||||
var currentUser = Mock.Of<IUser>(user => user.StartContentIds == new[] { 9876 });
|
||||
var savingUser = Mock.Of<IUser>();
|
||||
|
||||
var contentService = new Mock<IContentService>();
|
||||
contentService.Setup(x => x.GetById(It.IsAny<int>()))
|
||||
.Returns(Mock.Of<IContent>(content => content.Path == "-1,9876,5555"));
|
||||
var mediaService = new Mock<IMediaService>();
|
||||
var userService = new Mock<IUserService>();
|
||||
var entityService = new Mock<IEntityService>();
|
||||
entityService.Setup(service => service.GetAllPaths(It.IsAny<UmbracoObjectTypes>(), It.IsAny<int[]>()))
|
||||
.Returns(new[] { new EntityPath() { Path = "-1,9876", Id = 9876 } });
|
||||
|
||||
var authHelper = new UserEditorAuthorizationHelper(
|
||||
contentService.Object,
|
||||
mediaService.Object,
|
||||
userService.Object,
|
||||
entityService.Object);
|
||||
|
||||
var result = authHelper.IsAuthorized(currentUser, savingUser, new[] { 5555 }, new int[0], new string[0]);
|
||||
|
||||
Assert.IsTrue(result.Success);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Cannot_Grant_Media_Start_Node_On_User_Without_Access()
|
||||
{
|
||||
var currentUser = Mock.Of<IUser>(user => user.StartMediaIds == new[] { 9876 });
|
||||
var savingUser = Mock.Of<IUser>();
|
||||
|
||||
var contentService = new Mock<IContentService>();
|
||||
var mediaService = new Mock<IMediaService>();
|
||||
mediaService.Setup(x => x.GetById(It.IsAny<int>())).Returns(Mock.Of<IMedia>(content => content.Path == "-1,1234"));
|
||||
var userService = new Mock<IUserService>();
|
||||
var entityService = new Mock<IEntityService>();
|
||||
entityService.Setup(service => service.GetAllPaths(It.IsAny<UmbracoObjectTypes>(), It.IsAny<int[]>()))
|
||||
.Returns(new[] { new EntityPath() { Path = "-1,9876", Id = 9876 } });
|
||||
|
||||
var authHelper = new UserEditorAuthorizationHelper(
|
||||
contentService.Object,
|
||||
mediaService.Object,
|
||||
userService.Object,
|
||||
entityService.Object);
|
||||
|
||||
var result = authHelper.IsAuthorized(currentUser, savingUser, new int[0], new[] {1234}, new string[0]);
|
||||
|
||||
Assert.IsFalse(result.Success);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Grant_Media_Start_Node_On_User_With_Access()
|
||||
{
|
||||
var currentUser = Mock.Of<IUser>(user => user.StartMediaIds == new[] { 9876 });
|
||||
var savingUser = Mock.Of<IUser>();
|
||||
|
||||
var contentService = new Mock<IContentService>();
|
||||
var mediaService = new Mock<IMediaService>();
|
||||
mediaService.Setup(x => x.GetById(It.IsAny<int>()))
|
||||
.Returns(Mock.Of<IMedia>(content => content.Path == "-1,9876,5555"));
|
||||
var userService = new Mock<IUserService>();
|
||||
var entityService = new Mock<IEntityService>();
|
||||
entityService.Setup(service => service.GetAllPaths(It.IsAny<UmbracoObjectTypes>(), It.IsAny<int[]>()))
|
||||
.Returns(new[] { new EntityPath() { Path = "-1,9876", Id = 9876 } });
|
||||
|
||||
var authHelper = new UserEditorAuthorizationHelper(
|
||||
contentService.Object,
|
||||
mediaService.Object,
|
||||
userService.Object,
|
||||
entityService.Object);
|
||||
|
||||
var result = authHelper.IsAuthorized(currentUser, savingUser, new int[0], new[] { 5555 }, new string[0]);
|
||||
|
||||
Assert.IsTrue(result.Success);
|
||||
}
|
||||
|
||||
private IUser GetAdminUser()
|
||||
{
|
||||
var admin = Mock.Of<IUser>(user => user.Groups == new[]
|
||||
{
|
||||
new ReadOnlyUserGroup(1, "Admin", "icon-user", null, null, Constants.Security.AdminGroupAlias, new string[0], new string[0])
|
||||
});
|
||||
return admin;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,27 +74,17 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
if (userGroupsChanged)
|
||||
{
|
||||
var userGroups = _userService.GetUserGroupsByAlias(newGroups).ToArray();
|
||||
// d) A user cannot assign a group to another user that they do not belong to
|
||||
|
||||
//TODO: pretty sure d + e can be done by just checking if the newGroups being added are groups that the current user is a member of
|
||||
|
||||
// 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)
|
||||
var currentUserGroups = currentUser.Groups.Select(x => x.Alias).ToArray();
|
||||
|
||||
foreach (var group in newGroups)
|
||||
{
|
||||
pathResult = AuthorizePath(currentUser,
|
||||
group.StartContentId.HasValue ? new[] { group.StartContentId.Value } : null,
|
||||
group.StartMediaId.HasValue ? new[] { group.StartMediaId.Value } : null);
|
||||
if (pathResult == false)
|
||||
return pathResult;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
return Attempt.Fail("The current user does not have access to sections " + string.Join(",", missingSectionAccess));
|
||||
}
|
||||
if (currentUserGroups.Contains(group) == false)
|
||||
{
|
||||
return Attempt.Fail("Cannot assign the group " + group + ", the current user is not a member");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user