Merge pull request #1387 from sbwalker/dev
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:
commit
35aaf476d0
|
@ -2,7 +2,6 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Oqtane.Modules.HtmlText.Models;
|
|
||||||
using Oqtane.Services;
|
using Oqtane.Services;
|
||||||
using Oqtane.Shared;
|
using Oqtane.Shared;
|
||||||
|
|
||||||
|
@ -21,23 +20,23 @@ namespace Oqtane.Modules.HtmlText.Services
|
||||||
|
|
||||||
public async Task<Models.HtmlText> GetHtmlTextAsync(int moduleId)
|
public async Task<Models.HtmlText> GetHtmlTextAsync(int moduleId)
|
||||||
{
|
{
|
||||||
var htmltext = await GetJsonAsync<List<Models.HtmlText>>(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", moduleId));
|
var htmltext = await GetJsonAsync<List<Models.HtmlText>>(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", new Dictionary<string, int>() { { EntityNames.Module, moduleId } }));
|
||||||
return htmltext.FirstOrDefault();
|
return htmltext.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task AddHtmlTextAsync(Models.HtmlText htmlText)
|
public async Task AddHtmlTextAsync(Models.HtmlText htmlText)
|
||||||
{
|
{
|
||||||
await PostJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}", htmlText.ModuleId), htmlText);
|
await PostJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}", new Dictionary<string, int>() { { EntityNames.Module, htmlText.ModuleId } }), htmlText);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateHtmlTextAsync(Models.HtmlText htmlText)
|
public async Task UpdateHtmlTextAsync(Models.HtmlText htmlText)
|
||||||
{
|
{
|
||||||
await PutJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{htmlText.HtmlTextId}", htmlText.ModuleId), htmlText);
|
await PutJsonAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{htmlText.HtmlTextId}", new Dictionary<string, int>() { { EntityNames.Module, htmlText.ModuleId } }), htmlText);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteHtmlTextAsync(int moduleId)
|
public async Task DeleteHtmlTextAsync(int moduleId)
|
||||||
{
|
{
|
||||||
await DeleteAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", moduleId));
|
await DeleteAsync(CreateAuthorizationPolicyUrl($"{ApiUrl}/{moduleId}", new Dictionary<string, int>() { { EntityNames.Module, moduleId } }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
|
@ -54,10 +55,15 @@ namespace Oqtane.Services
|
||||||
return apiurl;
|
return apiurl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// add entityid parameter to url for custom authorization policy
|
// add authentityid parameters to url for custom authorization policy - args in form of entityname = entityid
|
||||||
public string CreateAuthorizationPolicyUrl(string url, int entityId)
|
public string CreateAuthorizationPolicyUrl(string url, Dictionary<string, int> args)
|
||||||
{
|
{
|
||||||
string qs = "entityid=" + entityId.ToString();
|
string qs = "";
|
||||||
|
foreach (KeyValuePair<string, int> kvp in args)
|
||||||
|
{
|
||||||
|
qs += (qs != "") ? "&" : "";
|
||||||
|
qs += "auth" + kvp.Key.ToLower() + "id=" + kvp.Value.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
if (url.Contains("?"))
|
if (url.Contains("?"))
|
||||||
{
|
{
|
||||||
|
@ -204,5 +210,20 @@ namespace Oqtane.Services
|
||||||
{
|
{
|
||||||
return CreateApiUrl(serviceName, alias, ControllerRoutes.Default);
|
return CreateApiUrl(serviceName, alias, ControllerRoutes.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Obsolete("This method is obsolete. Use CreateAuthorizationPolicyUrl(string url, Dictionary<string, int> args) instead - in conjunction with _authEntityId in Server Controller.", false)]
|
||||||
|
public string CreateAuthorizationPolicyUrl(string url, int entityId)
|
||||||
|
{
|
||||||
|
string qs = "entityid=" + entityId.ToString();
|
||||||
|
|
||||||
|
if (url.Contains("?"))
|
||||||
|
{
|
||||||
|
return url + "&" + qs;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return url + "?" + qs;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -513,28 +513,7 @@
|
||||||
{
|
{
|
||||||
List<PermissionString> permissions;
|
List<PermissionString> permissions;
|
||||||
|
|
||||||
if (action == "publish")
|
// publish/unpublish page
|
||||||
{
|
|
||||||
// publish all modules
|
|
||||||
foreach (var module in PageState.Modules.Where(item => item.PageId == PageState.Page.PageId))
|
|
||||||
{
|
|
||||||
permissions = UserSecurity.GetPermissionStrings(module.Permissions);
|
|
||||||
foreach (var permissionstring in permissions)
|
|
||||||
{
|
|
||||||
if (permissionstring.PermissionName == PermissionNames.View)
|
|
||||||
{
|
|
||||||
List<string> ids = permissionstring.Permissions.Split(';').ToList();
|
|
||||||
if (!ids.Contains(RoleNames.Everyone)) ids.Add(RoleNames.Everyone);
|
|
||||||
if (!ids.Contains(RoleNames.Registered)) ids.Add(RoleNames.Registered);
|
|
||||||
permissionstring.Permissions = string.Join(";", ids.ToArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
module.Permissions = UserSecurity.SetPermissionStrings(permissions);
|
|
||||||
await ModuleService.UpdateModuleAsync(module);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// publish page
|
|
||||||
var page = PageState.Page;
|
var page = PageState.Page;
|
||||||
permissions = UserSecurity.GetPermissionStrings(page.Permissions);
|
permissions = UserSecurity.GetPermissionStrings(page.Permissions);
|
||||||
foreach (var permissionstring in permissions)
|
foreach (var permissionstring in permissions)
|
||||||
|
|
|
@ -1,21 +1,35 @@
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Oqtane.Infrastructure;
|
using Oqtane.Infrastructure;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Oqtane.Controllers
|
namespace Oqtane.Controllers
|
||||||
{
|
{
|
||||||
public class ModuleControllerBase : Controller
|
public class ModuleControllerBase : Controller
|
||||||
{
|
{
|
||||||
protected readonly ILogManager _logger;
|
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)
|
public ModuleControllerBase(ILogManager logger, IHttpContextAccessor accessor)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_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"))
|
if (accessor.HttpContext.Request.Query.ContainsKey("entityid"))
|
||||||
{
|
{
|
||||||
_entityId = int.Parse(accessor.HttpContext.Request.Query["entityid"]);
|
_entityId = int.Parse(accessor.HttpContext.Request.Query["entityid"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,17 +19,19 @@ namespace Oqtane.Controllers
|
||||||
private readonly IPageRepository _pages;
|
private readonly IPageRepository _pages;
|
||||||
private readonly IModuleRepository _modules;
|
private readonly IModuleRepository _modules;
|
||||||
private readonly IPageModuleRepository _pageModules;
|
private readonly IPageModuleRepository _pageModules;
|
||||||
|
private readonly IPermissionRepository _permissionRepository;
|
||||||
private readonly ISettingRepository _settings;
|
private readonly ISettingRepository _settings;
|
||||||
private readonly IUserPermissions _userPermissions;
|
private readonly IUserPermissions _userPermissions;
|
||||||
private readonly ISyncManager _syncManager;
|
private readonly ISyncManager _syncManager;
|
||||||
private readonly ILogManager _logger;
|
private readonly ILogManager _logger;
|
||||||
private readonly Alias _alias;
|
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;
|
_pages = pages;
|
||||||
_modules = modules;
|
_modules = modules;
|
||||||
_pageModules = pageModules;
|
_pageModules = pageModules;
|
||||||
|
_permissionRepository = permissionRepository;
|
||||||
_settings = settings;
|
_settings = settings;
|
||||||
_userPermissions = userPermissions;
|
_userPermissions = userPermissions;
|
||||||
_syncManager = syncManager;
|
_syncManager = syncManager;
|
||||||
|
@ -227,7 +229,54 @@ namespace Oqtane.Controllers
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Page, page.PageId, PermissionNames.Edit))
|
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);
|
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);
|
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Updated {Page}", page);
|
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Updated {Page}", page);
|
||||||
}
|
}
|
||||||
|
@ -240,6 +289,19 @@ namespace Oqtane.Controllers
|
||||||
return page;
|
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
|
// PUT api/<controller>/?siteid=x&pageid=y&parentid=z
|
||||||
[HttpPut]
|
[HttpPut]
|
||||||
[Authorize(Roles = RoleNames.Registered)]
|
[Authorize(Roles = RoleNames.Registered)]
|
||||||
|
@ -287,4 +349,5 @@ namespace Oqtane.Controllers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ namespace Oqtane.Modules.HtmlText.Controllers
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Models.HtmlText htmlText = null;
|
Models.HtmlText htmlText = null;
|
||||||
if (_entityId == id)
|
if (_authEntityId[EntityNames.Module] == id)
|
||||||
{
|
{
|
||||||
htmlText = _htmlText.GetHtmlText(id);
|
htmlText = _htmlText.GetHtmlText(id);
|
||||||
list.Add(htmlText);
|
list.Add(htmlText);
|
||||||
|
@ -51,7 +51,7 @@ namespace Oqtane.Modules.HtmlText.Controllers
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid && htmlText.ModuleId == _entityId)
|
if (ModelState.IsValid && htmlText.ModuleId == _authEntityId[EntityNames.Module])
|
||||||
{
|
{
|
||||||
htmlText = _htmlText.AddHtmlText(htmlText);
|
htmlText = _htmlText.AddHtmlText(htmlText);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Html/Text Added {HtmlText}", htmlText);
|
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Html/Text Added {HtmlText}", htmlText);
|
||||||
|
@ -72,7 +72,7 @@ namespace Oqtane.Modules.HtmlText.Controllers
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (ModelState.IsValid && htmlText.ModuleId == _entityId)
|
if (ModelState.IsValid && htmlText.ModuleId == _authEntityId[EntityNames.Module])
|
||||||
{
|
{
|
||||||
htmlText = _htmlText.UpdateHtmlText(htmlText);
|
htmlText = _htmlText.UpdateHtmlText(htmlText);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Html/Text Updated {HtmlText}", htmlText);
|
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Html/Text Updated {HtmlText}", htmlText);
|
||||||
|
@ -93,7 +93,7 @@ namespace Oqtane.Modules.HtmlText.Controllers
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (id == _entityId)
|
if (id == _authEntityId[EntityNames.Module])
|
||||||
{
|
{
|
||||||
_htmlText.DeleteHtmlText(id);
|
_htmlText.DeleteHtmlText(id);
|
||||||
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Html/Text Deleted {HtmlTextId}", id);
|
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Html/Text Deleted {HtmlTextId}", id);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Oqtane.Enums;
|
using Oqtane.Enums;
|
||||||
|
@ -22,11 +22,19 @@ namespace Oqtane.Security
|
||||||
|
|
||||||
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
|
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;
|
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))
|
if (_userPermissions.IsAuthorized(context.User, requirement.EntityName, entityId, requirement.PermissionName))
|
||||||
{
|
{
|
||||||
context.Succeed(requirement);
|
context.Succeed(requirement);
|
||||||
|
@ -39,4 +47,4 @@ namespace Oqtane.Security
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user