diff --git a/Oqtane.Client/Modules/Admin/Files/Edit.razor b/Oqtane.Client/Modules/Admin/Files/Edit.razor index 4f53443f..e474f597 100644 --- a/Oqtane.Client/Modules/Admin/Files/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Files/Edit.razor @@ -99,7 +99,7 @@ private string _imagesizes = string.Empty; private string _capacity = "0"; private bool _isSystem; - private string _permissions = string.Empty; + private List _permissions; private string _createdBy; private DateTime _createdOn; private string _modifiedBy; diff --git a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor index a8a78168..5da79f52 100644 --- a/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor +++ b/Oqtane.Client/Modules/Admin/ModuleDefinitions/Edit.razor @@ -206,7 +206,7 @@ private string _contact = ""; private string _license = ""; private string _runtimes = ""; - private string _permissions; + private List _permissions; private string _createdby; private DateTime _createdon; private string _modifiedby; diff --git a/Oqtane.Client/Modules/Admin/Modules/Settings.razor b/Oqtane.Client/Modules/Admin/Modules/Settings.razor index ca84fd46..8195b54a 100644 --- a/Oqtane.Client/Modules/Admin/Modules/Settings.razor +++ b/Oqtane.Client/Modules/Admin/Modules/Settings.razor @@ -101,7 +101,7 @@ private string _containerType; private string _allPages = "false"; private string _permissionNames = ""; - private string _permissions = null; + private List _permissions; private string _pageId; private PermissionGrid _permissionGrid; private Type _moduleSettingsType; diff --git a/Oqtane.Client/Modules/Admin/Pages/Add.razor b/Oqtane.Client/Modules/Admin/Pages/Add.razor index bd0cd704..716677fd 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Add.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Add.razor @@ -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 _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) diff --git a/Oqtane.Client/Modules/Admin/Pages/Edit.razor b/Oqtane.Client/Modules/Admin/Pages/Edit.razor index 144a4dad..0cfc24b6 100644 --- a/Oqtane.Client/Modules/Admin/Pages/Edit.razor +++ b/Oqtane.Client/Modules/Admin/Pages/Edit.razor @@ -221,7 +221,7 @@ private string _themetype; private string _containertype = "-"; private string _icon; - private string _permissions = null; + private List _permissions = null; private string _createdby; private DateTime _createdon; private string _modifiedby; diff --git a/Oqtane.Client/Modules/Controls/ActionDialog.razor b/Oqtane.Client/Modules/Controls/ActionDialog.razor index 4e21b91a..13cdf176 100644 --- a/Oqtane.Client/Modules/Controls/ActionDialog.razor +++ b/Oqtane.Client/Modules/Controls/ActionDialog.razor @@ -40,7 +40,7 @@ @code { private bool _visible = false; - private string _permissions = string.Empty; + private List _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 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(); } diff --git a/Oqtane.Client/Modules/Controls/ActionLink.razor b/Oqtane.Client/Modules/Controls/ActionLink.razor index 6f3a29bf..2bc3bc52 100644 --- a/Oqtane.Client/Modules/Controls/ActionLink.razor +++ b/Oqtane.Client/Modules/Controls/ActionLink.razor @@ -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 _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 Permissions { get; set; } // optional - can be used to specify permissions [Parameter] public bool Disabled { get; set; } // optional @@ -119,7 +119,7 @@ _iconSpan = $"{(IconOnly ? "" : " ")}"; } - _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)) diff --git a/Oqtane.Client/Modules/Controls/PermissionGrid.razor b/Oqtane.Client/Modules/Controls/PermissionGrid.razor index 4fcdc5ac..b8d5443c 100644 --- a/Oqtane.Client/Modules/Controls/PermissionGrid.razor +++ b/Oqtane.Client/Modules/Controls/PermissionGrid.razor @@ -15,20 +15,19 @@ @Localizer["Role"] - @foreach (PermissionString permission in _permissions) + @foreach (var permissionname in _permissionnames) { - @((MarkupString)GetPermissionName(permission).Replace(" ", "
")) + @((MarkupString)DisplayPermissionName(permissionname).Replace(" ", "
")) } @foreach (Role role in _roles) { @role.Name - @foreach (PermissionString permission in _permissions) + @foreach (var permissionname in _permissionnames) { - var p = permission; - + } @@ -50,23 +49,21 @@ @Localizer["User"] - @foreach (PermissionString permission in _permissions) - { - @Localizer[permission.PermissionName] - } - + @foreach (var permissionname in _permissionnames) + { + @((MarkupString)DisplayPermissionName(permissionname).Replace(" ", "
")) + } + @foreach (User user in _users) { - string userid = "[" + user.UserId.ToString() + "]"; @user.DisplayName - @foreach (PermissionString permission in _permissions) + @foreach (var permissionname in _permissionnames) { - var p = permission; - + } @@ -94,9 +91,9 @@ } @code { - private string _permissionnames = string.Empty; + private List _permissionnames; + private List _permissions; private List _roles; - private List _permissions; private List _users = new List(); 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 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(); + // get permission names + if (string.IsNullOrEmpty(PermissionNames)) + { + _permissionnames = new List(); + _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(); + 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> 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 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 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; - } + } } } diff --git a/Oqtane.Client/Themes/Controls/Container/ModuleActionsBase.cs b/Oqtane.Client/Themes/Controls/Container/ModuleActionsBase.cs index 8714b938..d08d0a44 100644 --- a/Oqtane.Client/Themes/Controls/Container/ModuleActionsBase.cs +++ b/Oqtane.Client/Themes/Controls/Container/ModuleActionsBase.cs @@ -136,36 +136,32 @@ namespace Oqtane.Themes.Controls private async Task 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 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 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 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; } diff --git a/Oqtane.Client/Themes/Controls/Theme/ControlPanel.razor b/Oqtane.Client/Themes/Controls/Theme/ControlPanel.razor index e72a20d4..a49527c6 100644 --- a/Oqtane.Client/Themes/Controls/Theme/ControlPanel.razor +++ b/Oqtane.Client/Themes/Controls/Theme/ControlPanel.razor @@ -392,20 +392,21 @@ module.ModuleDefinitionName = ModuleDefinitionName; module.AllPages = false; - List permissions = UserSecurity.GetPermissionStrings(PageState.Page.Permissions); + var permissions = new List(); 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 permissions; - - // publish/unpublish page - var page = PageState.Page; - permissions = UserSecurity.GetPermissionStrings(page.Permissions); - foreach (var permissionstring in permissions) - { - if (permissionstring.PermissionName == PermissionNames.View) - { - List 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)); } } diff --git a/Oqtane.Server/Controllers/FolderController.cs b/Oqtane.Server/Controllers/FolderController.cs index 839c8e1f..445d41c5 100644 --- a/Oqtane.Server/Controllers/FolderController.cs +++ b/Oqtane.Server/Controllers/FolderController.cs @@ -104,7 +104,7 @@ namespace Oqtane.Controllers { if (ModelState.IsValid && folder.SiteId == _alias.SiteId) { - string permissions; + List permissions; if (folder.ParentId != null) { permissions = _folders.GetFolder(folder.ParentId.Value).Permissions; diff --git a/Oqtane.Server/Controllers/PageController.cs b/Oqtane.Server/Controllers/PageController.cs index 80d56d78..fd12234e 100644 --- a/Oqtane.Server/Controllers/PageController.cs +++ b/Oqtane.Server/Controllers/PageController.cs @@ -128,7 +128,7 @@ namespace Oqtane.Controllers { if (ModelState.IsValid && page.SiteId == _alias.SiteId) { - string permissions; + List 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) diff --git a/Oqtane.Server/Extensions/PermissionExtension.cs b/Oqtane.Server/Extensions/PermissionExtension.cs index ba02e294..bc2c4705 100644 --- a/Oqtane.Server/Extensions/PermissionExtension.cs +++ b/Oqtane.Server/Extensions/PermissionExtension.cs @@ -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 permissionList) + public static List EncodePermissions(this IEnumerable permissionList) { - List permissionstrings = new List(); - 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(); } } } diff --git a/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs b/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs index 8ba2c9b0..a3dc7358 100644 --- a/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs +++ b/Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs @@ -10,14 +10,12 @@ namespace Oqtane.Repository IEnumerable GetPermissions(int siteId, string entityName); IEnumerable GetPermissions(int siteId, string entityName, string permissionName); IEnumerable GetPermissions(int siteId, string entityName, int entityId); - IEnumerable GetPermissions(int siteId, string entityName, int entityId, string permissionName); - + IEnumerable 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 permissions); Permission GetPermission(int permissionId); void DeletePermission(int permissionId); void DeletePermissions(int siteId, string entityName, int entityId); - IEnumerable DecodePermissions(string permissions, int siteId, string entityName, int entityId); } } diff --git a/Oqtane.Server/Repository/PermissionRepository.cs b/Oqtane.Server/Repository/PermissionRepository.cs index d6e3d75f..c3f4acc6 100644 --- a/Oqtane.Server/Repository/PermissionRepository.cs +++ b/Oqtane.Server/Repository/PermissionRepository.cs @@ -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 permissions) { + // ensure permissions are fully populated + List 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(); - 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 permissionList) - { - List permissionstrings = new List(); - 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 DecodePermissions(string permissionStrings, int siteId, string entityName, int entityId) - { - List permissions = new List(); - List roles = _roles.GetRoles(siteId, true).ToList(); - string securityid = ""; - foreach (PermissionString permissionstring in JsonSerializer.Deserialize>(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; - } - } + } } diff --git a/Oqtane.Server/Security/UserPermissions.cs b/Oqtane.Server/Security/UserPermissions.cs index cf0217c4..91d2cc90 100644 --- a/Oqtane.Server/Security/UserPermissions.cs +++ b/Oqtane.Server/Security/UserPermissions.cs @@ -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 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 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()); } } } diff --git a/Oqtane.Shared/Models/Folder.cs b/Oqtane.Shared/Models/Folder.cs index 90bf87bd..46e5fa90 100644 --- a/Oqtane.Shared/Models/Folder.cs +++ b/Oqtane.Shared/Models/Folder.cs @@ -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? /// [NotMapped] - public string Permissions { get; set; } + public List Permissions { get; set; } /// /// Folder Depth diff --git a/Oqtane.Shared/Models/Module.cs b/Oqtane.Shared/Models/Module.cs index d6f219fd..0c8b5f97 100644 --- a/Oqtane.Shared/Models/Module.cs +++ b/Oqtane.Shared/Models/Module.cs @@ -42,7 +42,7 @@ namespace Oqtane.Models #endregion [NotMapped] - public string Permissions { get; set; } + public List Permissions { get; set; } [NotMapped] public Dictionary Settings { get; set; } diff --git a/Oqtane.Shared/Models/ModuleDefinition.cs b/Oqtane.Shared/Models/ModuleDefinition.cs index aec53d88..796611b1 100644 --- a/Oqtane.Shared/Models/ModuleDefinition.cs +++ b/Oqtane.Shared/Models/ModuleDefinition.cs @@ -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 Permissions { get; set; } [NotMapped] public string Template { get; set; } } diff --git a/Oqtane.Shared/Models/Page.cs b/Oqtane.Shared/Models/Page.cs index 94c1d8f0..e24a3268 100644 --- a/Oqtane.Shared/Models/Page.cs +++ b/Oqtane.Shared/Models/Page.cs @@ -98,7 +98,7 @@ namespace Oqtane.Models public List Resources { get; set; } [NotMapped] - public string Permissions { get; set; } + public List Permissions { get; set; } [NotMapped] public Dictionary Settings { get; set; } diff --git a/Oqtane.Shared/Models/Permission.cs b/Oqtane.Shared/Models/Permission.cs index 4c93d8c0..f53e699c 100644 --- a/Oqtane.Shared/Models/Permission.cs +++ b/Oqtane.Shared/Models/Permission.cs @@ -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; } } diff --git a/Oqtane.Shared/Models/PermissionString.cs b/Oqtane.Shared/Models/PermissionString.cs deleted file mode 100644 index 44bcfc36..00000000 --- a/Oqtane.Shared/Models/PermissionString.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Oqtane.Models -{ - /// - /// Use this to define a which addresses a set of multiple permissions. - /// - public class PermissionString - { - /// - /// A term describing the entity - /// - public string EntityName { get; set; } - - /// - /// A term describing a set of permissions - /// - public string PermissionName { get; set; } - - /// - /// The permissions - /// - public string Permissions { get; set; } - } -} diff --git a/Oqtane.Shared/Models/SiteTemplate.cs b/Oqtane.Shared/Models/SiteTemplate.cs index 7ef52696..dba0302f 100644 --- a/Oqtane.Shared/Models/SiteTemplate.cs +++ b/Oqtane.Shared/Models/SiteTemplate.cs @@ -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 PagePermissions { get; set; } public List 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 ModulePermissions { get; set; } public string Content { get; set; } } } diff --git a/Oqtane.Shared/Security/UserSecurity.cs b/Oqtane.Shared/Security/UserSecurity.cs index 36cb21c9..9c550f4a 100644 --- a/Oqtane.Shared/Security/UserSecurity.cs +++ b/Oqtane.Shared/Security/UserSecurity.cs @@ -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 GetPermissionStrings(string permissionStrings) + public static bool IsAuthorized(User user, string roles) { - return JsonSerializer.Deserialize>(permissionStrings); - } - - public static string SetPermissionStrings(List permissionStrings) - { - return JsonSerializer.Serialize(permissionStrings); - } - - public static string GetPermissions(string permissionName, string permissionStrings) - { - string permissions = ""; - List permissionstrings = JsonSerializer.Deserialize>(permissionStrings); - PermissionString permissionstring = permissionstrings.FirstOrDefault(item => item.PermissionName == permissionName); - if (permissionstring != null) + var permissions = new List(); + 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 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 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 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 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 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 userroles)