migrates the custom content persmission helper and media permissions helper checks to authz policies and cleans up that code/class/namespaces
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Authorization
|
||||
{
|
||||
/// <summary>
|
||||
/// The user must have access to all descendant nodes of the content item in order to continue
|
||||
/// </summary>
|
||||
public class ContentPermissionPublishBranchHandler : AuthorizationHandler<ContentPermissionsPublishBranchRequirement, IContent>
|
||||
{
|
||||
private readonly IEntityService _entityService;
|
||||
private readonly ContentPermissions _contentPermissions;
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
|
||||
public ContentPermissionPublishBranchHandler(
|
||||
IEntityService entityService,
|
||||
ContentPermissions contentPermissions,
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor)
|
||||
{
|
||||
_entityService = entityService;
|
||||
_contentPermissions = contentPermissions;
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
}
|
||||
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ContentPermissionsPublishBranchRequirement requirement, IContent resource)
|
||||
{
|
||||
var denied = new List<IUmbracoEntity>();
|
||||
var page = 0;
|
||||
const int pageSize = 500;
|
||||
var total = long.MaxValue;
|
||||
while (page * pageSize < total)
|
||||
{
|
||||
var descendants = _entityService.GetPagedDescendants(resource.Id, UmbracoObjectTypes.Document, page++, pageSize, out total,
|
||||
//order by shallowest to deepest, this allows us to check permissions from top to bottom so we can exit
|
||||
//early if a permission higher up fails
|
||||
ordering: Ordering.By("path", Direction.Ascending));
|
||||
|
||||
foreach (var c in descendants)
|
||||
{
|
||||
//if this item's path has already been denied or if the user doesn't have access to it, add to the deny list
|
||||
if (denied.Any(x => c.Path.StartsWith($"{x.Path},"))
|
||||
|| (_contentPermissions.CheckPermissions(c,
|
||||
_backOfficeSecurityAccessor.BackOfficeSecurity.CurrentUser,
|
||||
requirement.Permission) == ContentPermissions.ContentAccess.Denied))
|
||||
{
|
||||
denied.Add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (denied.Count == 0)
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Fail();
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,45 +11,6 @@ using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Authorization
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to authorize if the user has the correct permission access to the content for the <see cref="IContent"/> specified
|
||||
/// </summary>
|
||||
public class ContentPermissionResourceHandler : AuthorizationHandler<ContentPermissionResourceRequirement, IContent>
|
||||
{
|
||||
private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
|
||||
private readonly IEntityService _entityService;
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public ContentPermissionResourceHandler(
|
||||
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
|
||||
IEntityService entityService,
|
||||
IUserService userService)
|
||||
{
|
||||
_backofficeSecurityAccessor = backofficeSecurityAccessor;
|
||||
_entityService = entityService;
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ContentPermissionResourceRequirement requirement, IContent resource)
|
||||
{
|
||||
var permissionResult = ContentPermissionsHelper.CheckPermissions(resource,
|
||||
_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser,
|
||||
_userService,
|
||||
_entityService,
|
||||
new[] { requirement.PermissionToCheck });
|
||||
|
||||
if (permissionResult == ContentPermissionsHelper.ContentAccess.Denied)
|
||||
{
|
||||
context.Fail();
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to authorize if the user has the correct permission access to the content for the content id specified in a query string
|
||||
@@ -59,21 +20,18 @@ namespace Umbraco.Web.BackOffice.Authorization
|
||||
private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly IEntityService _entityService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IContentService _contentService;
|
||||
private readonly ContentPermissions _contentPermissions;
|
||||
|
||||
public ContentPermissionQueryStringHandler(
|
||||
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IEntityService entityService,
|
||||
IUserService userService,
|
||||
IContentService contentService)
|
||||
ContentPermissions contentPermissions)
|
||||
{
|
||||
_backofficeSecurityAccessor = backofficeSecurityAccessor;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_entityService = entityService;
|
||||
_userService = userService;
|
||||
_contentService = contentService;
|
||||
_contentPermissions = contentPermissions;
|
||||
}
|
||||
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ContentPermissionsQueryStringRequirement requirement)
|
||||
@@ -118,20 +76,17 @@ namespace Umbraco.Web.BackOffice.Authorization
|
||||
nodeId = requirement.NodeId.Value;
|
||||
}
|
||||
|
||||
var permissionResult = ContentPermissionsHelper.CheckPermissions(nodeId,
|
||||
_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser,
|
||||
_userService,
|
||||
_contentService,
|
||||
_entityService,
|
||||
out var contentItem,
|
||||
var permissionResult = _contentPermissions.CheckPermissions(nodeId,
|
||||
_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser,
|
||||
out IContent contentItem,
|
||||
new[] { requirement.PermissionToCheck });
|
||||
|
||||
if (permissionResult == ContentPermissionsHelper.ContentAccess.NotFound)
|
||||
if (permissionResult == ContentPermissions.ContentAccess.NotFound)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (permissionResult == ContentPermissionsHelper.ContentAccess.Denied)
|
||||
if (permissionResult == ContentPermissions.ContentAccess.Denied)
|
||||
{
|
||||
context.Fail();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Authorization
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to authorize if the user has the correct permission access to the content for the <see cref="IContent"/> specified
|
||||
/// </summary>
|
||||
public class ContentPermissionResourceHandler : AuthorizationHandler<ContentPermissionResourceRequirement, IContent>
|
||||
{
|
||||
private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
|
||||
private readonly ContentPermissions _contentPermissions;
|
||||
|
||||
public ContentPermissionResourceHandler(
|
||||
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
|
||||
ContentPermissions contentPermissions)
|
||||
{
|
||||
_backofficeSecurityAccessor = backofficeSecurityAccessor;
|
||||
_contentPermissions = contentPermissions;
|
||||
}
|
||||
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ContentPermissionResourceRequirement requirement, IContent resource)
|
||||
{
|
||||
var permissionResult = _contentPermissions.CheckPermissions(resource,
|
||||
_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser,
|
||||
requirement.PermissionsToCheck);
|
||||
|
||||
if (permissionResult == ContentPermissions.ContentAccess.Denied)
|
||||
{
|
||||
context.Fail();
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Web.Actions;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Authorization
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// An authorization requirement for <see cref="ContentPermissionResourceHandler"/>
|
||||
/// </summary>
|
||||
@@ -13,9 +16,21 @@ namespace Umbraco.Web.BackOffice.Authorization
|
||||
/// <param name="permissionToCheck"></param>
|
||||
public ContentPermissionResourceRequirement(char permissionToCheck)
|
||||
{
|
||||
PermissionToCheck = permissionToCheck;
|
||||
PermissionsToCheck = new List<char> { permissionToCheck };
|
||||
}
|
||||
|
||||
public char PermissionToCheck { get; }
|
||||
public ContentPermissionResourceRequirement(IReadOnlyList<char> permissionToCheck)
|
||||
{
|
||||
PermissionsToCheck = permissionToCheck;
|
||||
}
|
||||
|
||||
public ContentPermissionResourceRequirement(int nodeId, IReadOnlyList<char> permissionToCheck)
|
||||
{
|
||||
NodeId = nodeId;
|
||||
PermissionsToCheck = permissionToCheck;
|
||||
}
|
||||
|
||||
public int? NodeId { get; }
|
||||
public IReadOnlyList<char> PermissionsToCheck { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Authorization
|
||||
{
|
||||
/// <summary>
|
||||
/// Authorization requirement for <see cref="ContentPermissionPublishBranchHandler"/>
|
||||
/// </summary>
|
||||
public class ContentPermissionsPublishBranchRequirement : IAuthorizationRequirement
|
||||
{
|
||||
public ContentPermissionsPublishBranchRequirement(char permission)
|
||||
{
|
||||
Permission = permission;
|
||||
}
|
||||
|
||||
public char Permission { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Authorization
|
||||
{
|
||||
public class MediaPermissionQueryStringHandler : AuthorizationHandler<MediaPermissionsQueryStringRequirement>
|
||||
{
|
||||
private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly MediaPermissions _mediaPermissions;
|
||||
private readonly IEntityService _entityService;
|
||||
|
||||
public MediaPermissionQueryStringHandler(
|
||||
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
MediaPermissions mediaPermissions)
|
||||
{
|
||||
_backofficeSecurityAccessor = backofficeSecurityAccessor;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_mediaPermissions = mediaPermissions;
|
||||
}
|
||||
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MediaPermissionsQueryStringRequirement requirement)
|
||||
{
|
||||
StringValues routeVal;
|
||||
foreach (var qs in requirement.QueryStringNames)
|
||||
{
|
||||
if (_httpContextAccessor.HttpContext.Request.Query.TryGetValue(qs, out routeVal))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (routeVal.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("No argument found for the current action with by names " + string.Join(", ", requirement.QueryStringNames));
|
||||
}
|
||||
|
||||
int nodeId;
|
||||
|
||||
var argument = routeVal.ToString();
|
||||
// if the argument is an int, it will parse and can be assigned to nodeId
|
||||
// if might be a udi, so check that next
|
||||
// otherwise treat it as a guid - unlikely we ever get here
|
||||
if (int.TryParse(argument, out int parsedId))
|
||||
{
|
||||
nodeId = parsedId;
|
||||
}
|
||||
else if (UdiParser.TryParse(argument, true, out var udi))
|
||||
{
|
||||
nodeId = _entityService.GetId(udi).Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
Guid.TryParse(argument, out Guid key);
|
||||
nodeId = _entityService.GetId(key, UmbracoObjectTypes.Document).Result;
|
||||
}
|
||||
|
||||
var permissionResult = _mediaPermissions.CheckPermissions(
|
||||
_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser,
|
||||
nodeId,
|
||||
out var mediaItem);
|
||||
|
||||
if (permissionResult == MediaPermissions.MediaAccess.NotFound)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (permissionResult == MediaPermissions.MediaAccess.Denied)
|
||||
{
|
||||
context.Fail();
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
|
||||
if (mediaItem != null)
|
||||
{
|
||||
//store the content item in request cache so it can be resolved in the controller without re-looking it up
|
||||
_httpContextAccessor.HttpContext.Items[typeof(IMedia).ToString()] = mediaItem;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Security;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Authorization
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to authorize if the user has the correct permission access to the content for the <see cref="IContent"/> specified
|
||||
/// </summary>
|
||||
public class MediaPermissionResourceHandler : AuthorizationHandler<MediaPermissionResourceRequirement, IMedia>
|
||||
{
|
||||
private readonly IBackOfficeSecurityAccessor _backofficeSecurityAccessor;
|
||||
private readonly MediaPermissions _mediaPermissions;
|
||||
|
||||
public MediaPermissionResourceHandler(
|
||||
IBackOfficeSecurityAccessor backofficeSecurityAccessor,
|
||||
MediaPermissions mediaPermissions)
|
||||
{
|
||||
_backofficeSecurityAccessor = backofficeSecurityAccessor;
|
||||
_mediaPermissions = mediaPermissions;
|
||||
}
|
||||
|
||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MediaPermissionResourceRequirement requirement, IMedia resource)
|
||||
{
|
||||
var permissionResult = MediaPermissions.MediaAccess.NotFound;
|
||||
|
||||
if (resource != null)
|
||||
{
|
||||
permissionResult = _mediaPermissions.CheckPermissions(
|
||||
resource,
|
||||
_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser);
|
||||
}
|
||||
else if (requirement.NodeId.HasValue)
|
||||
{
|
||||
permissionResult = _mediaPermissions.CheckPermissions(
|
||||
_backofficeSecurityAccessor.BackOfficeSecurity.CurrentUser,
|
||||
requirement.NodeId.Value,
|
||||
out _);
|
||||
}
|
||||
|
||||
if (permissionResult == MediaPermissions.MediaAccess.Denied)
|
||||
{
|
||||
context.Fail();
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Authorization
|
||||
{
|
||||
/// <summary>
|
||||
/// An authorization requirement for <see cref="MediaPermissionResourceHandler"/>
|
||||
/// </summary>
|
||||
public class MediaPermissionResourceRequirement : IAuthorizationRequirement
|
||||
{
|
||||
public MediaPermissionResourceRequirement()
|
||||
{
|
||||
}
|
||||
|
||||
public MediaPermissionResourceRequirement(int nodeId)
|
||||
{
|
||||
NodeId = nodeId;
|
||||
}
|
||||
|
||||
public int? NodeId { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Authorization
|
||||
{
|
||||
public class MediaPermissionsQueryStringRequirement : IAuthorizationRequirement
|
||||
{
|
||||
public MediaPermissionsQueryStringRequirement(string[] paramNames)
|
||||
{
|
||||
QueryStringNames = paramNames;
|
||||
}
|
||||
|
||||
public string[] QueryStringNames { get; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user