Merge remote-tracking branch 'oqtane/dev' into dev

This commit is contained in:
Leigh Pointer 2023-03-01 10:34:47 +01:00
commit da3a4d5855
28 changed files with 304 additions and 480 deletions

View File

@ -99,7 +99,7 @@
private string _imagesizes = string.Empty;
private string _capacity = "0";
private bool _isSystem;
private string _permissions = string.Empty;
private List<Permission> _permissions;
private string _createdBy;
private DateTime _createdOn;
private string _modifiedBy;

View File

@ -206,7 +206,7 @@
private string _contact = "";
private string _license = "";
private string _runtimes = "";
private string _permissions;
private List<Permission> _permissions;
private string _createdby;
private DateTime _createdon;
private string _modifiedby;

View File

@ -101,7 +101,7 @@
private string _containerType;
private string _allPages = "false";
private string _permissionNames = "";
private string _permissions = null;
private List<Permission> _permissions;
private string _pageId;
private PermissionGrid _permissionGrid;
private Type _moduleSettingsType;

View File

@ -183,7 +183,7 @@
private string _themetype = string.Empty;
private string _containertype = string.Empty;
private string _icon = string.Empty;
private string _permissions = string.Empty;
private List<Permission> _permissions = null;
private PermissionGrid _permissionGrid;
private Type _themeSettingsType;
private object _themeSettings;
@ -202,7 +202,6 @@
_containers = ThemeService.GetContainerControls(_themeList, _themetype);
_containertype = PageState.Site.DefaultContainerType;
_children = PageState.Pages.Where(item => item.ParentId == null).ToList();
_permissions = string.Empty;
ThemeSettings();
}
catch (Exception ex)

View File

@ -221,7 +221,7 @@
private string _themetype;
private string _containertype = "-";
private string _icon;
private string _permissions = null;
private List<Permission> _permissions = null;
private string _createdby;
private DateTime _createdon;
private string _modifiedby;

View File

@ -40,7 +40,7 @@
@code {
private bool _visible = false;
private string _permissions = string.Empty;
private List<Permission> _permissions;
private bool _editmode = false;
private bool _authorized = false;
private string _iconSpan = string.Empty;
@ -61,7 +61,7 @@
public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel
[Parameter]
public string Permissions { get; set; } // optional - can be used to specify a permission string
public List<Permission> Permissions { get; set; } // optional - can be used to specify permissions
[Parameter]
public string Class { get; set; } // optional
@ -109,7 +109,7 @@
Header = Localize(nameof(Header), Header);
Message = Localize(nameof(Message), Message);
_permissions = (string.IsNullOrEmpty(Permissions)) ? ModuleState.Permissions : Permissions;
_permissions = (Permissions == null) ? ModuleState.Permissions : Permissions;
_authorized = IsAuthorized();
}

View File

@ -26,7 +26,7 @@
private string _text = string.Empty;
private string _parameters = string.Empty;
private string _url = string.Empty;
private string _permissions = string.Empty;
private List<Permission> _permissions;
private bool _editmode = false;
private bool _authorized = false;
private string _classname = "btn btn-primary";
@ -52,7 +52,7 @@
public SecurityAccessLevel? Security { get; set; } // optional - can be used to explicitly specify SecurityAccessLevel
[Parameter]
public string Permissions { get; set; } // optional - can be used to specify a permission string
public List<Permission> Permissions { get; set; } // optional - can be used to specify permissions
[Parameter]
public bool Disabled { get; set; } // optional
@ -119,7 +119,7 @@
_iconSpan = $"<span class=\"{IconName}\"></span>{(IconOnly ? "" : "&nbsp")}";
}
_permissions = (string.IsNullOrEmpty(Permissions)) ? ModuleState.Permissions : Permissions;
_permissions = (Permissions == null) ? ModuleState.Permissions : Permissions;
_text = Localize(nameof(Text), _text);
_url = (ModuleId == -1) ? EditUrl(Action, _parameters) : EditUrl(ModuleId, Action, _parameters);
if (!string.IsNullOrEmpty(ReturnUrl))

View File

@ -15,20 +15,19 @@
<tbody>
<tr>
<th scope="col">@Localizer["Role"]</th>
@foreach (PermissionString permission in _permissions)
@foreach (var permissionname in _permissionnames)
{
<th style="text-align: center; width: 1px;">@((MarkupString)GetPermissionName(permission).Replace(" ", "<br />"))</th>
<th style="text-align: center; width: 1px;">@((MarkupString)DisplayPermissionName(permissionname).Replace(" ", "<br />"))</th>
}
</tr>
@foreach (Role role in _roles)
{
<tr>
<td>@role.Name</td>
@foreach (PermissionString permission in _permissions)
@foreach (var permissionname in _permissionnames)
{
var p = permission;
<td style="text-align: center;">
<TriStateCheckBox Value=@GetPermissionValue(p.Permissions, role.Name) Disabled="@GetPermissionDisabled(p.EntityName, p.PermissionName, role.Name)" OnChange="@(e => PermissionChanged(e, p.EntityName, p.PermissionName, role.Name))" />
<TriStateCheckBox Value=@GetPermissionValue(permissionname, role.Name, -1) Disabled="@GetPermissionDisabled(permissionname, role.Name)" OnChange="@(e => PermissionChanged(e, permissionname, role.Name, -1))" />
</td>
}
</tr>
@ -50,23 +49,21 @@
<thead>
<tr>
<th scope="col">@Localizer["User"]</th>
@foreach (PermissionString permission in _permissions)
{
<th style="text-align: center; width: 1px;">@Localizer[permission.PermissionName]</th>
}
</tr>
@foreach (var permissionname in _permissionnames)
{
<th style="text-align: center; width: 1px;">@((MarkupString)DisplayPermissionName(permissionname).Replace(" ", "<br />"))</th>
}
</tr>
</thead>
<tbody>
@foreach (User user in _users)
{
string userid = "[" + user.UserId.ToString() + "]";
<tr>
<td>@user.DisplayName</td>
@foreach (PermissionString permission in _permissions)
@foreach (var permissionname in _permissionnames)
{
var p = permission;
<td style="text-align: center; width: 1px;">
<TriStateCheckBox Value=@GetPermissionValue(p.Permissions, userid) Disabled="@GetPermissionDisabled(p.EntityName, p.PermissionName, "")" OnChange="@(e => PermissionChanged(e, p.EntityName, p.PermissionName, userid))" />
<TriStateCheckBox Value=@GetPermissionValue(permissionname, "", user.UserId) Disabled="@GetPermissionDisabled(permissionname, "")" OnChange="@(e => PermissionChanged(e, permissionname, "", user.UserId))" />
</td>
}
</tr>
@ -94,9 +91,9 @@
}
@code {
private string _permissionnames = string.Empty;
private List<string> _permissionnames;
private List<Permission> _permissions;
private List<Role> _roles;
private List<PermissionString> _permissions;
private List<User> _users = new List<User>();
private AutoComplete _user;
private string _message = string.Empty;
@ -108,28 +105,31 @@
public string PermissionNames { get; set; }
[Parameter]
public string Permissions { get; set; }
public List<Permission> Permissions { get; set; }
protected override async Task OnInitializedAsync()
{
if (string.IsNullOrEmpty(PermissionNames))
{
_permissionnames = Shared.PermissionNames.View + "," + Shared.PermissionNames.Edit;
}
else
{
_permissionnames = PermissionNames;
}
_roles = await RoleService.GetRolesAsync(ModuleState.SiteId, true);
if (!UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
_roles.RemoveAll(item => item.Name == RoleNames.Host);
}
_permissions = new List<PermissionString>();
// get permission names
if (string.IsNullOrEmpty(PermissionNames))
{
_permissionnames = new List<string>();
_permissionnames.Add(Shared.PermissionNames.View);
_permissionnames.Add(Shared.PermissionNames.Edit);
}
else
{
_permissionnames = PermissionNames.Split(',', StringSplitOptions.RemoveEmptyEntries).ToList();
}
foreach (string permissionname in _permissionnames.Split(',', StringSplitOptions.RemoveEmptyEntries))
// initialize permissions
_permissions = new List<Permission>();
foreach (string permissionname in _permissionnames)
{
// permission names can be in the form of "EntityName:PermissionName:Roles"
if (permissionname.Contains(":"))
@ -137,78 +137,83 @@
var segments = permissionname.Split(':');
if (segments.Length == 3)
{
if (!segments[2].Contains(RoleNames.Admin))
foreach (var role in segments[2].Split(';'))
{
segments[2] = RoleNames.Admin + ";" + segments[2]; // ensure admin access
_permissions.Add(new Permission(segments[0], segments[1], role, null, true));
}
// ensure admin access
if (!_permissions.Any(item => item.EntityName == segments[0] && item.PermissionName == segments[1] && item.Role.Name == RoleNames.Admin))
{
_permissions.Add(new Permission(segments[0], segments[1], RoleNames.Admin, null, true));
}
_permissions.Add(new PermissionString { EntityName = segments[0], PermissionName = segments[1], Permissions = segments[2] });
}
}
else
{
_permissions.Add(new PermissionString { EntityName = EntityName, PermissionName = permissionname, Permissions = RoleNames.Admin });
_permissions.Add(new Permission(EntityName, permissionname, RoleNames.Admin, null, true));
}
}
if (!string.IsNullOrEmpty(Permissions))
// populate permissions and users
if (Permissions.Any())
{
// populate permissions
foreach (PermissionString permissionstring in UserSecurity.GetPermissionStrings(Permissions))
foreach (var permission in Permissions)
{
int index = _permissions.FindIndex(item => item.EntityName == permissionstring.EntityName && item.PermissionName == permissionstring.PermissionName);
if (index != -1)
if (!_permissions.Any(item => item.EntityName == permission.EntityName && item.PermissionName == permission.PermissionName && item.Role.Name == permission.Role.Name))
{
_permissions[index].Permissions = permissionstring.Permissions;
_permissions.Add(permission);
}
if (permissionstring.Permissions.Contains("["))
if (permission.UserId != null)
{
foreach (string user in permissionstring.Permissions.Split('[', StringSplitOptions.RemoveEmptyEntries))
if (!_users.Any(item => item.UserId == permission.UserId.Value))
{
if (user.Contains("]"))
{
var userid = int.Parse(user.Substring(0, user.IndexOf("]")));
if (_users.Where(item => item.UserId == userid).FirstOrDefault() == null)
{
_users.Add(await UserService.GetUserAsync(userid, ModuleState.SiteId));
}
}
_users.Add(await UserService.GetUserAsync(permission.UserId.Value, ModuleState.SiteId));
}
}
}
}
}
private string GetPermissionName(PermissionString permission)
private string GetPermissionName(string permissionName)
{
var permissionname = Localizer[permission.PermissionName].ToString();
if (!string.IsNullOrEmpty(EntityName))
{
permissionname += " " + Localizer[permission.EntityName].ToString();
}
return permissionname;
return (permissionName.Contains(":")) ? permissionName.Split(':')[1] : permissionName;
}
private bool? GetPermissionValue(string permissions, string securityKey)
private string GetEntityName(string permissionName)
{
if ((";" + permissions + ";").Contains(";" + "!" + securityKey + ";"))
return (permissionName.Contains(":")) ? permissionName.Split(':')[0] : EntityName;
}
private string DisplayPermissionName(string permissionName)
{
var name = Localizer[GetPermissionName(permissionName)].ToString();
name += " " + Localizer[GetEntityName(permissionName)].ToString();
return name;
}
private bool? GetPermissionValue(string permissionName, string roleName, int userId)
{
bool? isauthorized = null;
if (roleName != "")
{
return false; // deny permission
var permission = _permissions.FirstOrDefault(item => item.EntityName == GetEntityName(permissionName) && item.PermissionName == GetPermissionName(permissionName) && item.Role.Name == roleName);
if (permission != null)
{
isauthorized = permission.IsAuthorized;
}
}
else
{
if ((";" + permissions + ";").Contains(";" + securityKey + ";"))
var permission = _permissions.FirstOrDefault(item => item.EntityName == GetEntityName(permissionName) && item.PermissionName == GetPermissionName(permissionName) && item.UserId == userId);
if (permission != null)
{
return true; // grant permission
}
else
{
return null; // not specified
}
isauthorized = permission.IsAuthorized;
}
}
return isauthorized;
}
private bool GetPermissionDisabled(string entityName, string permissionName, string roleName)
private bool GetPermissionDisabled(string permissionName, string roleName)
{
if (roleName == RoleNames.Admin && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
@ -216,7 +221,7 @@
}
else
{
if (entityName != EntityName && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
if (GetEntityName(permissionName) != EntityName && !UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
{
return true;
}
@ -227,6 +232,34 @@
}
}
private void PermissionChanged(bool? value, string permissionName, string roleName, int userId)
{
if (roleName != "")
{
var permission = _permissions.FirstOrDefault(item => item.EntityName == GetEntityName(permissionName) && item.PermissionName == GetPermissionName(permissionName) && item.Role.Name == roleName);
if (permission == null)
{
_permissions.Remove(permission);
}
if (value != null)
{
_permissions.Add(new Permission(GetEntityName(permissionName), GetPermissionName(permissionName), roleName, null, value.Value));
}
}
else
{
var permission = _permissions.FirstOrDefault(item => item.EntityName == GetEntityName(permissionName) && item.PermissionName == GetPermissionName(permissionName) && item.UserId == userId);
if (permission == null)
{
_permissions.Remove(permission);
}
if (value != null)
{
_permissions.Add(new Permission(GetEntityName(permissionName), GetPermissionName(permissionName), null, userId, value.Value));
}
}
}
private async Task<Dictionary<string, string>> GetUsers(string filter)
{
var users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered);
@ -251,62 +284,39 @@
_user.Clear();
}
private void PermissionChanged(bool? value, string entityName, string permissionName, string securityId)
{
var selected = value;
int index = _permissions.FindIndex(item => item.EntityName == entityName && item.PermissionName == permissionName);
if (index != -1)
{
var permission = _permissions[index];
var ids = permission.Permissions.Split(';').ToList();
ids.Remove(securityId); // remove grant permission
ids.Remove("!" + securityId); // remove deny permission
switch (selected)
{
case true:
ids.Add(securityId); // add grant permission
break;
case false:
ids.Add("!" + securityId); // add deny permission
break;
case null:
break; // permission not specified
}
_permissions[index].Permissions = string.Join(";", ids.ToArray());
}
}
public string GetPermissions()
public List<Permission> GetPermissions()
{
ValidatePermissions();
return UserSecurity.SetPermissionStrings(_permissions);
return _permissions;
}
private void ValidatePermissions()
{
PermissionString permission;
for (int index = 0; index < _permissions.Count; index++)
// remove deny all users, unauthenticated, and registered users
var permissions = _permissions.Where(item => !item.IsAuthorized &&
(item.Role.Name == RoleNames.Everyone || item.Role.Name == RoleNames.Unauthenticated || item.Role.Name == RoleNames.Registered));
foreach (var permission in permissions)
{
permission = _permissions[index];
List<string> ids = permission.Permissions.Split(';', StringSplitOptions.RemoveEmptyEntries).ToList();
ids.Remove("!" + RoleNames.Everyone); // remove deny all users
ids.Remove("!" + RoleNames.Unauthenticated); // remove deny unauthenticated
ids.Remove("!" + RoleNames.Registered); // remove deny registered users
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
_permissions.Remove(permission);
}
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
{
// remove deny administrators and host users
permissions = _permissions.Where(item => !item.IsAuthorized &&
(item.Role.Name == RoleNames.Admin || item.Role.Name == RoleNames.Host));
foreach (var permission in permissions)
{
ids.Remove("!" + RoleNames.Admin); // remove deny administrators
ids.Remove("!" + RoleNames.Host); // remove deny host users
if (!ids.Contains(RoleNames.Host) && !ids.Contains(RoleNames.Admin))
_permissions.Remove(permission);
}
foreach (var permissionname in _permissionnames)
{
// add administrators role if neither host or administrator is assigned
if (!_permissions.Any(item => item.EntityName == GetEntityName(permissionname) && item.PermissionName == GetPermissionName(permissionname) &&
(item.Role.Name == RoleNames.Admin || item.Role.Name == RoleNames.Host)))
{
// add administrators role if host user role is not assigned
ids.Add(RoleNames.Admin);
_permissions.Add(new Permission(GetEntityName(permissionname), GetPermissionName(permissionname), RoleNames.Admin, null, true));
}
}
permission.Permissions = string.Join(";", ids.ToArray());
_permissions[index] = permission;
}
}
}
}

View File

@ -136,36 +136,32 @@ namespace Oqtane.Themes.Controls
private async Task<string> Publish(string url, PageModule pagemodule)
{
var permissions = UserSecurity.GetPermissionStrings(pagemodule.Module.Permissions);
foreach (var permissionstring in permissions)
var permissions = pagemodule.Module.Permissions;
if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.Role.Name == RoleNames.Everyone))
{
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());
}
permissions.Add(new Permission(EntityNames.Page, pagemodule.PageId, PermissionNames.View, RoleNames.Everyone, null, true));
}
pagemodule.Module.Permissions = UserSecurity.SetPermissionStrings(permissions);
if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.Role.Name == RoleNames.Registered))
{
permissions.Add(new Permission(EntityNames.Page, pagemodule.PageId, PermissionNames.View, RoleNames.Registered, null, true));
}
pagemodule.Module.Permissions = permissions;
await ModuleService.UpdateModuleAsync(pagemodule.Module);
return url;
}
private async Task<string> Unpublish(string url, PageModule pagemodule)
{
var permissions = UserSecurity.GetPermissionStrings(pagemodule.Module.Permissions);
foreach (var permissionstring in permissions)
var permissions = pagemodule.Module.Permissions;
if (permissions.Any(item => item.PermissionName == PermissionNames.View && item.Role.Name == RoleNames.Everyone))
{
if (permissionstring.PermissionName == PermissionNames.View)
{
List<string> ids = permissionstring.Permissions.Split(';').ToList();
ids.Remove(RoleNames.Everyone);
ids.Remove(RoleNames.Registered);
permissionstring.Permissions = string.Join(";", ids.ToArray());
}
permissions.Remove(permissions.First(item => item.PermissionName == PermissionNames.View && item.Role.Name == RoleNames.Everyone));
}
pagemodule.Module.Permissions = UserSecurity.SetPermissionStrings(permissions);
if (permissions.Any(item => item.PermissionName == PermissionNames.View && item.Role.Name == RoleNames.Registered))
{
permissions.Remove(permissions.First(item => item.PermissionName == PermissionNames.View && item.Role.Name == RoleNames.Registered));
}
pagemodule.Module.Permissions = permissions;
await ModuleService.UpdateModuleAsync(pagemodule.Module);
return url;
}

View File

@ -392,20 +392,21 @@
module.ModuleDefinitionName = ModuleDefinitionName;
module.AllPages = false;
List<PermissionString> permissions = UserSecurity.GetPermissionStrings(PageState.Page.Permissions);
var permissions = new List<Permission>();
if (Visibility == "view")
{
// set module view permissions to page view permissions
permissions.Find(p => p.PermissionName == PermissionNames.View).Permissions = permissions.Find(p => p.PermissionName == PermissionNames.View).Permissions;
permissions = PageState.Page.Permissions.Where(item => item.PermissionName == PermissionNames.View).ToList();
}
else
{
// set module view permissions to page edit permissions
permissions.Find(p => p.PermissionName == PermissionNames.View).Permissions = permissions.Find(p => p.PermissionName == PermissionNames.Edit).Permissions;
permissions = PageState.Page.Permissions.Where(item => item.PermissionName == PermissionNames.Edit).ToList();
}
// set entityname
// set entity name and permission name
permissions.ForEach(item => item.EntityName = EntityNames.Module);
module.Permissions = UserSecurity.SetPermissionStrings(permissions);
permissions.ForEach(item => item.PermissionName = PermissionNames.View);
module.Permissions = permissions;
module = await ModuleService.AddModuleAsync(module);
ModuleId = module.ModuleId.ToString();
@ -527,32 +528,17 @@
{
if (UserSecurity.IsAuthorized(PageState.User, PermissionNames.Edit, PageState.Page.Permissions))
{
List<PermissionString> permissions;
// publish/unpublish page
var page = PageState.Page;
permissions = UserSecurity.GetPermissionStrings(page.Permissions);
foreach (var permissionstring in permissions)
{
if (permissionstring.PermissionName == PermissionNames.View)
{
List<string> ids = permissionstring.Permissions.Split(';').ToList();
switch (action)
{
case "publish":
if (!ids.Contains(RoleNames.Everyone)) ids.Add(RoleNames.Everyone);
if (!ids.Contains(RoleNames.Registered)) ids.Add(RoleNames.Registered);
break;
case "unpublish":
ids.Remove(RoleNames.Everyone);
ids.Remove(RoleNames.Registered);
break;
}
permissionstring.Permissions = string.Join(";", ids.ToArray());
}
}
page.Permissions = UserSecurity.SetPermissionStrings(permissions);
await PageService.UpdatePageAsync(page);
var permissions = PageState.Page.Permissions;
if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.Role.Name == RoleNames.Everyone))
{
permissions.Add(new Permission(EntityNames.Page, PageState.Page.PageId, PermissionNames.View, RoleNames.Everyone, null, true));
}
if (!permissions.Any(item => item.PermissionName == PermissionNames.View && item.Role.Name == RoleNames.Registered))
{
permissions.Add(new Permission(EntityNames.Page, PageState.Page.PageId, PermissionNames.View, RoleNames.Registered, null, true));
}
PageState.Page.Permissions = permissions;
await PageService.UpdatePageAsync(PageState.Page);
NavigationManager.NavigateTo(NavigateUrl(PageState.Page.Path, true));
}
}

View File

@ -104,7 +104,7 @@ namespace Oqtane.Controllers
{
if (ModelState.IsValid && folder.SiteId == _alias.SiteId)
{
string permissions;
List<Permission> permissions;
if (folder.ParentId != null)
{
permissions = _folders.GetFolder(folder.ParentId.Value).Permissions;

View File

@ -15,6 +15,7 @@ using System;
using Microsoft.Extensions.DependencyInjection;
using System.Text.Json;
using System.Net;
using Oqtane.Modules;
namespace Oqtane.Controllers
{
@ -22,6 +23,8 @@ namespace Oqtane.Controllers
public class ModuleDefinitionController : Controller
{
private readonly IModuleDefinitionRepository _moduleDefinitions;
private readonly IModuleRepository _modules;
private readonly IPageModuleRepository _pagemodules;
private readonly ITenantRepository _tenants;
private readonly ISqlRepository _sql;
private readonly IUserPermissions _userPermissions;
@ -33,9 +36,11 @@ namespace Oqtane.Controllers
private readonly ILogManager _logger;
private readonly Alias _alias;
public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, ITenantRepository tenants, ISqlRepository sql, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, IModuleRepository module,IPageModuleRepository pageModule, ITenantRepository tenants, ISqlRepository sql, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
{
_moduleDefinitions = moduleDefinitions;
_modules = module;
_pagemodules = pageModule;
_tenants = tenants;
_sql = sql;
_userPermissions = userPermissions;
@ -228,6 +233,24 @@ namespace Oqtane.Controllers
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Static Resources Folder Removed For {ModuleDefinitionName}", moduledefinition.ModuleDefinitionName);
}
// remove PageModule and Module
List<Models.Module> modulesToRemove = _modules.GetModules(moduledefinition.SiteId).Where(m => m.ModuleDefinitionName == moduledefinition.ModuleDefinitionName).ToList();
foreach (Models.Module moduleToRemove in modulesToRemove)
{
// Get the PageModule items associated with the Module item to be removed
List<PageModule> pageModulesToRemove = _pagemodules.GetPageModules(moduledefinition.SiteId).Where(pm => pm.ModuleId == moduleToRemove.ModuleId).ToList();
foreach(PageModule pageModule in pageModulesToRemove)
{
// Remove the PageModule item
_pagemodules.DeletePageModule(pageModule.PageModuleId);
}
// Remove the Module item
_modules.DeleteModule(moduleToRemove.ModuleId);
}
// remove module definition
_moduleDefinitions.DeleteModuleDefinition(id);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.ModuleDefinition, moduledefinition.ModuleDefinitionId, SyncEventActions.Delete);

View File

@ -128,7 +128,7 @@ namespace Oqtane.Controllers
{
if (ModelState.IsValid && page.SiteId == _alias.SiteId)
{
string permissions;
List<Permission> permissions;
if (page.ParentId != null)
{
permissions = _pages.GetPage(page.ParentId.Value).Permissions;
@ -274,9 +274,8 @@ namespace Oqtane.Controllers
}
// get differences between current and new page permissions
var newPermissions = _permissionRepository.DecodePermissions(page.Permissions, page.SiteId, EntityNames.Page, page.PageId).ToList();
var added = GetPermissionsDifferences(newPermissions, currentPermissions);
var removed = GetPermissionsDifferences(currentPermissions, newPermissions);
var added = GetPermissionsDifferences(page.Permissions, currentPermissions);
var removed = GetPermissionsDifferences(currentPermissions, page.Permissions);
// synchronize module permissions
if (added.Count > 0 || removed.Count > 0)

View File

@ -1,66 +1,14 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using Oqtane.Models;
namespace Oqtane.Extensions
{
public static class PermissionExtension
{
public static string EncodePermissions(this IEnumerable<Permission> permissionList)
public static List<Permission> EncodePermissions(this IEnumerable<Permission> permissionList)
{
List<PermissionString> permissionstrings = new List<PermissionString>();
string entityname = "";
string permissionname = "";
string permissions = "";
StringBuilder permissionsbuilder = new StringBuilder();
string securityid = "";
foreach (Permission permission in permissionList.OrderBy(item => item.EntityName).ThenBy(item => item.PermissionName))
{
// permission collections are grouped by entityname and permissionname
if (entityname != permission.EntityName || permissionname != permission.PermissionName)
{
permissions = permissionsbuilder.ToString();
if (permissions != "")
{
permissionstrings.Add(new PermissionString { EntityName = entityname, PermissionName = permissionname, Permissions = permissions.Substring(0, permissions.Length - 1) });
}
entityname = permission.EntityName;
permissionname = permission.PermissionName;
permissionsbuilder = new StringBuilder();
}
// deny permissions are prefixed with a "!"
string prefix = !permission.IsAuthorized ? "!" : "";
// encode permission
if (permission.UserId == null)
{
securityid = prefix + permission.Role.Name + ";";
}
else
{
securityid = prefix + "[" + permission.UserId + "];";
}
// insert deny permissions at the beginning and append grant permissions at the end
if (prefix == "!")
{
permissionsbuilder.Insert(0, securityid);
}
else
{
permissionsbuilder.Append(securityid);
}
}
permissions = permissionsbuilder.ToString();
if (permissions != "")
{
permissionstrings.Add(new PermissionString { EntityName = entityname, PermissionName = permissionname, Permissions = permissions.Substring(0, permissions.Length - 1) });
}
return JsonSerializer.Serialize(permissionstrings);
return permissionList.ToList();
}
}
}

View File

@ -60,4 +60,8 @@
<Copy SourceFiles="@(ModuleTemplateFiles)" DestinationFiles="@(ModuleTemplateFiles->'$(PublishDir)wwwroot\Modules\Templates\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="false" />
<Copy SourceFiles="@(ThemeTemplateFiles)" DestinationFiles="@(ThemeTemplateFiles->'$(PublishDir)wwwroot\Themes\Templates\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="false" />
</Target>
<ItemGroup>
<!-- extends watching group to include *.dll files and exclude the ones cause an infinite loop -->
<Watch Include="**\*.dll" Exclude="**\Microsoft.EntityFrameworkCore.*.dll;**\Oqtane.Database.*.dll;"/>
</ItemGroup>
</Project>

View File

@ -95,7 +95,7 @@ namespace Oqtane.Repository
// anti-pattern used to reference config service in base class without causing breaking change
_config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.AddJsonFile("appsettings.json", false, false)
.Build();
}
}

View File

@ -10,14 +10,12 @@ namespace Oqtane.Repository
IEnumerable<Permission> GetPermissions(int siteId, string entityName);
IEnumerable<Permission> GetPermissions(int siteId, string entityName, string permissionName);
IEnumerable<Permission> GetPermissions(int siteId, string entityName, int entityId);
IEnumerable<Permission> GetPermissions(int siteId, string entityName, int entityId, string permissionName);
IEnumerable<Permission> GetPermissions(int siteId, string entityName, int entityId, string permissionName);
Permission AddPermission(Permission permission);
Permission UpdatePermission(Permission permission);
void UpdatePermissions(int siteId, string entityName, int entityId, string permissionStrings);
void UpdatePermissions(int siteId, string entityName, int entityId, List<Permission> permissions);
Permission GetPermission(int permissionId);
void DeletePermission(int permissionId);
void DeletePermissions(int siteId, string entityName, int entityId);
IEnumerable<Permission> DecodePermissions(string permissions, int siteId, string entityName, int entityId);
}
}

View File

@ -7,6 +7,7 @@ using Microsoft.EntityFrameworkCore;
using Oqtane.Models;
using Microsoft.Extensions.Caching.Memory;
using Oqtane.Infrastructure;
using Oqtane.Modules.Admin.Users;
namespace Oqtane.Repository
{
@ -77,11 +78,28 @@ namespace Oqtane.Repository
return permission;
}
public void UpdatePermissions(int siteId, string entityName, int entityId, string permissionStrings)
public void UpdatePermissions(int siteId, string entityName, int entityId, List<Permission> permissions)
{
// ensure permissions are fully populated
List<Role> roles = _roles.GetRoles(siteId, true).ToList();
foreach (var permission in permissions)
{
permission.SiteId = siteId;
permission.EntityName = (string.IsNullOrEmpty(permission.EntityName)) ? entityName : permission.EntityName;
permission.EntityId = (permission.EntityName == entityName) ? entityId : -1;
if (permission.RoleId == null && permission.Role != null && !string.IsNullOrEmpty(permission.Role.Name))
{
var role = roles.FirstOrDefault(item => item.Name == permission.Role.Name);
if (role != null)
{
permission.RoleId = role.RoleId;
}
}
permission.Role = null;
}
// add or update permissions
bool modified = false;
var existing = new List<Permission>();
var permissions = DecodePermissions(permissionStrings, siteId, entityName, entityId);
foreach (var permission in permissions)
{
if (!existing.Any(item => item.EntityName == permission.EntityName && item.PermissionName == permission.PermissionName))
@ -108,6 +126,7 @@ namespace Oqtane.Repository
modified = true;
}
}
// delete permissions
foreach (var permission in existing)
{
if (!permissions.Any(item => item.EntityName == permission.EntityName && item.PermissionName == permission.PermissionName
@ -163,122 +182,5 @@ namespace Oqtane.Repository
_cache.Remove($"permissions:{alias.TenantId}:{siteId}:{entityName}");
}
}
// permissions are stored in the format "{permissionname:!rolename1;![userid1];rolename2;rolename3;[userid2];[userid3]}" where "!" designates Deny permissions
public string EncodePermissions(IEnumerable<Permission> permissionList)
{
List<PermissionString> permissionstrings = new List<PermissionString>();
string permissionname = "";
string permissions = "";
StringBuilder permissionsbuilder = new StringBuilder();
string securityid = "";
foreach (Permission permission in permissionList.OrderBy(item => item.PermissionName))
{
// permission collections are grouped by permissionname
if (permissionname != permission.PermissionName)
{
permissions = permissionsbuilder.ToString();
if (permissions != "")
{
permissionstrings.Add(new PermissionString { PermissionName = permissionname, Permissions = permissions.Substring(0, permissions.Length - 1) });
}
permissionname = permission.PermissionName;
permissionsbuilder = new StringBuilder();
}
// deny permissions are prefixed with a "!"
string prefix = !permission.IsAuthorized ? "!" : "";
// encode permission
if (permission.UserId == null)
{
securityid = prefix + permission.Role.Name + ";";
}
else
{
securityid = prefix + "[" + permission.UserId + "];";
}
// insert deny permissions at the beginning and append grant permissions at the end
if (prefix == "!")
{
permissionsbuilder.Insert(0, securityid);
}
else
{
permissionsbuilder.Append(securityid);
}
}
permissions = permissionsbuilder.ToString();
if (permissions != "")
{
permissionstrings.Add(new PermissionString { PermissionName = permissionname, Permissions = permissions.Substring(0, permissions.Length - 1) });
}
return JsonSerializer.Serialize(permissionstrings);
}
public IEnumerable<Permission> DecodePermissions(string permissionStrings, int siteId, string entityName, int entityId)
{
List<Permission> permissions = new List<Permission>();
List<Role> roles = _roles.GetRoles(siteId, true).ToList();
string securityid = "";
foreach (PermissionString permissionstring in JsonSerializer.Deserialize<List<PermissionString>>(permissionStrings))
{
foreach (string id in permissionstring.Permissions.Split(';', StringSplitOptions.RemoveEmptyEntries))
{
securityid = id;
Permission permission = new Permission();
permission.SiteId = siteId;
if (!string.IsNullOrEmpty(permissionstring.EntityName))
{
permission.EntityName = permissionstring.EntityName;
}
else
{
permission.EntityName = entityName;
}
if (permission.EntityName == entityName)
{
permission.EntityId = entityId;
}
else
{
permission.EntityId = -1;
}
permission.PermissionName = permissionstring.PermissionName;
permission.RoleId = null;
permission.UserId = null;
permission.IsAuthorized = true;
if (securityid.StartsWith("!"))
{
// deny permission
securityid = securityid.Replace("!", "");
permission.IsAuthorized = false;
}
if (securityid.StartsWith("[") && securityid.EndsWith("]"))
{
// user id
securityid = securityid.Replace("[", "").Replace("]", "");
permission.UserId = int.Parse(securityid);
}
else
{
// role name
Role role = roles.SingleOrDefault(item => item.Name == securityid);
if (role != null)
{
permission.RoleId = role.RoleId;
}
}
if (permission.UserId != null || permission.RoleId != null)
{
permissions.Add(permission);
}
}
}
return permissions;
}
}
}
}

View File

@ -5,6 +5,7 @@ using System.Security.Claims;
using Oqtane.Repository;
using Oqtane.Extensions;
using System;
using System.Collections.Generic;
namespace Oqtane.Security
{
@ -12,7 +13,7 @@ namespace Oqtane.Security
{
bool IsAuthorized(ClaimsPrincipal user, int siteId, string entityName, int entityId, string permissionName, string roles);
bool IsAuthorized(ClaimsPrincipal user, int siteId, string entityName, int entityId, string permissionName);
bool IsAuthorized(ClaimsPrincipal user, string permissionName, string permissions);
bool IsAuthorized(ClaimsPrincipal user, string permissionName, List<Permission> permissions);
User GetUser(ClaimsPrincipal user);
User GetUser();
@ -36,7 +37,7 @@ namespace Oqtane.Security
var permissions = _permissions.GetPermissions(siteId, entityName, entityId, permissionName).ToList();
if (permissions != null && permissions.Count != 0)
{
return IsAuthorized(principal, permissionName, permissions.EncodePermissions());
return IsAuthorized(principal, permissionName, permissions.ToList());
}
else
{
@ -46,10 +47,10 @@ namespace Oqtane.Security
public bool IsAuthorized(ClaimsPrincipal principal, int siteId, string entityName, int entityId, string permissionName)
{
return IsAuthorized(principal, permissionName, _permissions.GetPermissions(siteId, entityName, entityId, permissionName)?.EncodePermissions());
return IsAuthorized(principal, permissionName, _permissions.GetPermissions(siteId, entityName, entityId, permissionName).ToList());
}
public bool IsAuthorized(ClaimsPrincipal principal, string permissionName, string permissions)
public bool IsAuthorized(ClaimsPrincipal principal, string permissionName, List<Permission> permissions)
{
return UserSecurity.IsAuthorized(GetUser(principal), permissionName, permissions);
}
@ -96,7 +97,7 @@ namespace Oqtane.Security
// deprecated
public bool IsAuthorized(ClaimsPrincipal principal, string entityName, int entityId, string permissionName)
{
return IsAuthorized(principal, permissionName, _permissions.GetPermissions(_accessor.HttpContext.GetAlias().SiteId, entityName, entityId, permissionName)?.EncodePermissions());
return IsAuthorized(principal, permissionName, _permissions.GetPermissions(_accessor.HttpContext.GetAlias().SiteId, entityName, entityId, permissionName).ToList());
}
}
}

View File

@ -31,7 +31,7 @@ namespace Oqtane
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile("appsettings.json", false, true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, true);
Configuration = builder.Build();

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace Oqtane.Models
@ -68,7 +69,7 @@ namespace Oqtane.Models
/// TODO: todoc what would this contain?
/// </summary>
[NotMapped]
public string Permissions { get; set; }
public List<Permission> Permissions { get; set; }
/// <summary>
/// Folder Depth

View File

@ -42,7 +42,7 @@ namespace Oqtane.Models
#endregion
[NotMapped]
public string Permissions { get; set; }
public List<Permission> Permissions { get; set; }
[NotMapped]
public Dictionary<string, string> Settings { get; set; }

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using Oqtane.Documentation;
@ -99,7 +100,7 @@ namespace Oqtane.Models
[NotMapped]
public string AssemblyName { get; set; }
[NotMapped]
public string Permissions { get; set; }
public List<Permission> Permissions { get; set; }
[NotMapped]
public string Template { get; set; }
}

View File

@ -98,7 +98,7 @@ namespace Oqtane.Models
public List<Resource> Resources { get; set; }
[NotMapped]
public string Permissions { get; set; }
public List<Permission> Permissions { get; set; }
[NotMapped]
public Dictionary<string, string> Settings { get; set; }

View File

@ -1,4 +1,5 @@
using System;
using Oqtane.Shared;
namespace Oqtane.Models
{
@ -66,15 +67,41 @@ namespace Oqtane.Models
public Permission(string permissionName, string roleName, bool isAuthorized)
{
PermissionName = permissionName;
Role = new Role { Name = roleName };
IsAuthorized = isAuthorized;
Initialize("", -1, permissionName, roleName, null, isAuthorized);
}
public Permission(string permissionName, int userId, bool isAuthorized)
{
Initialize("", -1, permissionName, "", userId, isAuthorized);
}
public Permission(string entityName, string permissionName, string roleName, int? userId, bool isAuthorized)
{
Initialize(entityName, -1, permissionName, roleName, userId, isAuthorized);
}
public Permission(string entityName, int entityId, string permissionName, string roleName, int? userId, bool isAuthorized)
{
Initialize(entityName, entityId, permissionName, roleName, userId, isAuthorized);
}
private void Initialize(string entityName, int entityId, string permissionName, string roleName, int? userId, bool isAuthorized)
{
EntityName = entityName;
EntityId = entityId;
PermissionName = permissionName;
UserId = userId;
if (!string.IsNullOrEmpty(roleName))
{
Role = new Role { Name = roleName };
RoleId = null;
UserId = null;
}
else
{
Role = null;
RoleId = null;
UserId = userId;
}
IsAuthorized = isAuthorized;
}
}

View File

@ -1,23 +0,0 @@
namespace Oqtane.Models
{
/// <summary>
/// Use this to define a <see cref="PermissionName"/> which addresses a set of multiple permissions.
/// </summary>
public class PermissionString
{
/// <summary>
/// A term describing the entity
/// </summary>
public string EntityName { get; set; }
/// <summary>
/// A term describing a set of permissions
/// </summary>
public string PermissionName { get; set; }
/// <summary>
/// The permissions
/// </summary>
public string Permissions { get; set; }
}
}

View File

@ -18,7 +18,7 @@ namespace Oqtane.Models
public string Icon { get; set; }
public bool IsNavigation { get; set; }
public bool IsPersonalizable { get; set; }
public string PagePermissions { get; set; }
public List<Permission> PagePermissions { get; set; }
public List<PageTemplateModule> PageTemplateModules { get; set; }
[Obsolete("This property is obsolete", false)]
@ -30,7 +30,7 @@ namespace Oqtane.Models
public string ModuleDefinitionName { get; set; }
public string Title { get; set; }
public string Pane { get; set; }
public string ModulePermissions { get; set; }
public List<Permission> ModulePermissions { get; set; }
public string Content { get; set; }
}
}

View File

@ -2,7 +2,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text.Json;
using Oqtane.Models;
using Oqtane.Shared;
@ -10,38 +9,25 @@ namespace Oqtane.Security
{
public class UserSecurity
{
public static List<PermissionString> GetPermissionStrings(string permissionStrings)
public static bool IsAuthorized(User user, string roles)
{
return JsonSerializer.Deserialize<List<PermissionString>>(permissionStrings);
}
public static string SetPermissionStrings(List<PermissionString> permissionStrings)
{
return JsonSerializer.Serialize(permissionStrings);
}
public static string GetPermissions(string permissionName, string permissionStrings)
{
string permissions = "";
List<PermissionString> permissionstrings = JsonSerializer.Deserialize<List<PermissionString>>(permissionStrings);
PermissionString permissionstring = permissionstrings.FirstOrDefault(item => item.PermissionName == permissionName);
if (permissionstring != null)
var permissions = new List<Permission>();
foreach (var role in roles.Split(';', StringSplitOptions.RemoveEmptyEntries))
{
permissions = permissionstring.Permissions;
permissions.Add(new Permission("", role, true));
}
return permissions;
return IsAuthorized(user, permissions);
}
public static bool IsAuthorized(User user, string permissionName, string permissionStrings)
public static bool IsAuthorized(User user, string permissionName, List<Permission> permissions)
{
return IsAuthorized(user, GetPermissions(permissionName, permissionStrings));
return IsAuthorized(user, permissions.Where(item => item.PermissionName == permissionName).ToList());
}
// permissions are stored in the format "!rolename1;![userid1];rolename2;rolename3;[userid2];[userid3]" where "!" designates Deny permissions
public static bool IsAuthorized(User user, string permissions)
public static bool IsAuthorized(User user, List<Permission> permissions)
{
bool authorized = false;
if (permissions != "")
if (permissions != null && permissions.Any())
{
if (user == null)
{
@ -56,77 +42,43 @@ namespace Oqtane.Security
return authorized;
}
private static bool IsAuthorized(int userId, string roles, string permissions)
private static bool IsAuthorized(int userId, string roles, List<Permission> permissions)
{
bool isAuthorized = false;
if (permissions != null)
if (permissions != null && permissions.Any())
{
foreach (string permission in permissions.Split(';', StringSplitOptions.RemoveEmptyEntries))
// check if denied first
isAuthorized = !permissions.Where(item => !item.IsAuthorized && (
(item.Role != null && (
(item.Role.Name == RoleNames.Everyone) ||
(item.Role.Name == RoleNames.Unauthenticated && userId == -1) ||
roles.Split(';', StringSplitOptions.RemoveEmptyEntries).Contains(item.Role.Name))) ||
(item.UserId != null && item.UserId.Value == userId))).Any();
if (isAuthorized)
{
bool? allowed = VerifyPermission(userId, roles, permission);
if (allowed.HasValue)
{
isAuthorized = allowed.Value;
break;
}
// then check if authorized
isAuthorized = permissions.Where(item => item.IsAuthorized && (
(item.Role != null && (
(item.Role.Name == RoleNames.Everyone) ||
(item.Role.Name == RoleNames.Unauthenticated && userId == -1) ||
roles.Split(';', StringSplitOptions.RemoveEmptyEntries).Contains(item.Role.Name))) ||
(item.UserId != null && item.UserId.Value == userId))).Any();
}
}
return isAuthorized;
}
private static bool? VerifyPermission(int userId, string roles, string permission)
public static bool ContainsRole(List<Permission> permissions, string permissionName, string roleName)
{
bool? allowed = null;
//permissions strings are encoded with deny permissions at the beginning and grant permissions at the end for optimal performance
if (!String.IsNullOrEmpty(permission))
{
// deny permission
if (permission.StartsWith("!"))
{
string denyRole = permission.Replace("!", "");
if (denyRole == RoleNames.Everyone || IsAllowed(userId, roles, denyRole))
{
allowed = false;
}
}
else // grant permission
{
if (permission == RoleNames.Everyone || IsAllowed(userId, roles, permission))
{
allowed = true;
}
}
}
return allowed;
return permissions.Any(item => item.PermissionName == permissionName && item.Role.Name == roleName);
}
private static bool IsAllowed(int userId, string roles, string permission)
public static bool ContainsUser(List<Permission> permissions, string permissionName, int userId)
{
if (permission == RoleNames.Unauthenticated)
{
return userId == -1;
}
if ("[" + userId + "]" == permission)
{
return true;
}
if (roles != null)
{
return roles.IndexOf(";" + permission + ";") != -1;
}
return false;
}
public static bool ContainsRole(string permissionStrings, string permissionName, string roleName)
{
return GetPermissionStrings(permissionStrings).FirstOrDefault(item => item.PermissionName == permissionName).Permissions.Split(';').Contains(roleName);
}
public static bool ContainsUser(string permissionStrings, string permissionName, int userId)
{
return GetPermissionStrings(permissionStrings).FirstOrDefault(item => item.PermissionName == permissionName).Permissions.Split(';').Contains($"[{userId}]");
return permissions.Any(item => item.PermissionName == permissionName && item.UserId == userId);
}
public static ClaimsIdentity CreateClaimsIdentity(Alias alias, User user, List<UserRole> userroles)