fix #1367 - provides support for multiple entities in auth policy and makes parameter names more intuitive - backward compatible with entityid

This commit is contained in:
Shaun Walker
2021-05-23 10:29:05 -04:00
parent 6f123c0fff
commit 3f48c1f8fe
7 changed files with 126 additions and 42 deletions

View File

@ -1,21 +1,35 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using Oqtane.Infrastructure;
using System.Collections.Generic;
using System;
namespace Oqtane.Controllers
{
public class ModuleControllerBase : Controller
{
protected readonly ILogManager _logger;
protected int _entityId = -1; // passed as a querystring parameter for policy authorization and used for validation
// querystring parameters for policy authorization and validation
protected Dictionary<string, int> _authEntityId = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
protected int _entityId = -1; // deprecated
public ModuleControllerBase(ILogManager logger, IHttpContextAccessor accessor)
{
_logger = logger;
int value;
foreach (var param in accessor.HttpContext.Request.Query)
{
if (param.Key.StartsWith("auth") && param.Key.EndsWith("id") && int.TryParse(param.Value, out value))
{
_authEntityId.Add(param.Key.Substring(4, param.Key.Length - 6), int.Parse(param.Value));
}
}
// entityid is deprecated
if (accessor.HttpContext.Request.Query.ContainsKey("entityid"))
{
_entityId = int.Parse(accessor.HttpContext.Request.Query["entityid"]);
}
}
}
}

View File

@ -19,17 +19,19 @@ namespace Oqtane.Controllers
private readonly IPageRepository _pages;
private readonly IModuleRepository _modules;
private readonly IPageModuleRepository _pageModules;
private readonly IPermissionRepository _permissionRepository;
private readonly ISettingRepository _settings;
private readonly IUserPermissions _userPermissions;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
public PageController(IPageRepository pages, IModuleRepository modules, IPageModuleRepository pageModules, ISettingRepository settings, IUserPermissions userPermissions, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
public PageController(IPageRepository pages, IModuleRepository modules, IPageModuleRepository pageModules, IPermissionRepository permissionRepository, ISettingRepository settings, IUserPermissions userPermissions, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
{
_pages = pages;
_modules = modules;
_pageModules = pageModules;
_permissionRepository = permissionRepository;
_settings = settings;
_userPermissions = userPermissions;
_syncManager = syncManager;
@ -227,7 +229,54 @@ namespace Oqtane.Controllers
{
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Page, page.PageId, PermissionNames.Edit))
{
// preserve page permissions
var oldPermissions = _permissionRepository.GetPermissions(EntityNames.Page, page.PageId).ToList();
page = _pages.UpdatePage(page);
// get differences between old and new page permissions
var newPermissions = _permissionRepository.DecodePermissions(page.Permissions, page.SiteId, EntityNames.Page, page.PageId).ToList();
var added = GetPermissionsDifferences(newPermissions, oldPermissions);
var removed = GetPermissionsDifferences(oldPermissions, newPermissions);
// synchronize module permissions
if (added.Count > 0 || removed.Count > 0)
{
foreach (PageModule pageModule in _pageModules.GetPageModules(page.PageId, "").ToList())
{
var modulePermissions = _permissionRepository.GetPermissions(EntityNames.Module, pageModule.Module.ModuleId).ToList();
//var modulePermissions = _permissionRepository.DecodePermissions(pageModule.Module.Permissions, page.SiteId, EntityNames.Module, pageModule.ModuleId).ToList();
// permissions added
foreach(Permission permission in added)
{
if (!modulePermissions.Any(item => item.PermissionName == permission.PermissionName
&& item.RoleId == permission.RoleId && item.UserId == permission.UserId && item.IsAuthorized == permission.IsAuthorized))
{
_permissionRepository.AddPermission(new Permission
{
SiteId = page.SiteId,
EntityName = EntityNames.Module,
EntityId = pageModule.ModuleId,
PermissionName = permission.PermissionName,
RoleId = permission.RoleId,
UserId = permission.UserId,
IsAuthorized = permission.IsAuthorized
});
}
}
// permissions removed
foreach (Permission permission in removed)
{
var modulePermission = modulePermissions.FirstOrDefault(item => item.PermissionName == permission.PermissionName
&& item.RoleId == permission.RoleId && item.UserId == permission.UserId && item.IsAuthorized == permission.IsAuthorized);
if (modulePermission != null)
{
_permissionRepository.DeletePermission(modulePermission.PermissionId);
}
}
}
}
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Updated {Page}", page);
}
@ -240,6 +289,19 @@ namespace Oqtane.Controllers
return page;
}
private List<Permission> GetPermissionsDifferences(List<Permission> permissions1, List<Permission> permissions2)
{
var differences = new List<Permission>();
foreach (Permission p in permissions1)
{
if (!permissions2.Any(item => item.PermissionName == p.PermissionName && item.RoleId == p.RoleId && item.UserId == p.UserId && item.IsAuthorized == p.IsAuthorized))
{
differences.Add(p);
}
}
return differences;
}
// PUT api/<controller>/?siteid=x&pageid=y&parentid=z
[HttpPut]
[Authorize(Roles = RoleNames.Registered)]
@ -287,4 +349,5 @@ namespace Oqtane.Controllers
}
}
}
}

View File

@ -30,7 +30,7 @@ namespace Oqtane.Modules.HtmlText.Controllers
try
{
Models.HtmlText htmlText = null;
if (_entityId == id)
if (_authEntityId[EntityNames.Module] == id)
{
htmlText = _htmlText.GetHtmlText(id);
list.Add(htmlText);
@ -51,7 +51,7 @@ namespace Oqtane.Modules.HtmlText.Controllers
{
try
{
if (ModelState.IsValid && htmlText.ModuleId == _entityId)
if (ModelState.IsValid && htmlText.ModuleId == _authEntityId[EntityNames.Module])
{
htmlText = _htmlText.AddHtmlText(htmlText);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Html/Text Added {HtmlText}", htmlText);
@ -72,7 +72,7 @@ namespace Oqtane.Modules.HtmlText.Controllers
{
try
{
if (ModelState.IsValid && htmlText.ModuleId == _entityId)
if (ModelState.IsValid && htmlText.ModuleId == _authEntityId[EntityNames.Module])
{
htmlText = _htmlText.UpdateHtmlText(htmlText);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Html/Text Updated {HtmlText}", htmlText);
@ -93,7 +93,7 @@ namespace Oqtane.Modules.HtmlText.Controllers
{
try
{
if (id == _entityId)
if (id == _authEntityId[EntityNames.Module])
{
_htmlText.DeleteHtmlText(id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Html/Text Deleted {HtmlTextId}", id);

View File

@ -1,4 +1,4 @@
using System.Threading.Tasks;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Oqtane.Enums;
@ -22,11 +22,19 @@ namespace Oqtane.Security
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
// permission is scoped based on EntityId which must be passed as a querystring parameter
// permission is scoped based on auth{entityname}id (ie ?authmoduleid ) which must be passed as a querystring parameter
var ctx = _httpContextAccessor.HttpContext;
if (ctx != null && ctx.Request.Query.ContainsKey("entityid"))
if (ctx != null)
{
int entityId = int.Parse(ctx.Request.Query["entityid"]);
int entityId = -1;
if (ctx.Request.Query.ContainsKey("auth" + requirement.EntityName.ToLower() + "id"))
{
entityId = int.Parse(ctx.Request.Query["auth" + requirement.EntityName.ToLower() + "id"]);
}
if (entityId == -1 && ctx.Request.Query.ContainsKey("entityid"))
{
entityId = int.Parse(ctx.Request.Query["entityid"]);
}
if (_userPermissions.IsAuthorized(context.User, requirement.EntityName, entityId, requirement.PermissionName))
{
context.Succeed(requirement);
@ -39,4 +47,4 @@ namespace Oqtane.Security
return Task.CompletedTask;
}
}
}
}