Permission-based authorization utilizing Policies
This commit is contained in:
@ -14,6 +14,7 @@ namespace Oqtane.Repository
|
||||
public virtual DbSet<SiteUser> SiteUser { get; set; }
|
||||
public virtual DbSet<Role> Role { get; set; }
|
||||
public virtual DbSet<UserRole> UserRole { get; set; }
|
||||
public virtual DbSet<Permission> Permission { get; set; }
|
||||
public virtual DbSet<Setting> Setting { get; set; }
|
||||
|
||||
public TenantDBContext(ITenantResolver TenantResolver, IHttpContextAccessor accessor) : base(TenantResolver, accessor)
|
||||
|
19
Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs
Normal file
19
Oqtane.Server/Repository/Interfaces/IPermissionRepository.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
using Oqtane.Models;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
{
|
||||
public interface IPermissionRepository
|
||||
{
|
||||
IEnumerable<Permission> GetPermissions(int SiteId, string EntityName);
|
||||
IEnumerable<Permission> GetPermissions(string EntityName, int EntityId);
|
||||
IEnumerable<Permission> GetPermissions(string EntityName, int EntityId, string PermissionName);
|
||||
Permission AddPermission(Permission Permission);
|
||||
Permission UpdatePermission(Permission Permission);
|
||||
void UpdatePermissions(int SiteId, string EntityName, int EntityId, string Permissions);
|
||||
Permission GetPermission(int PermissionId);
|
||||
void DeletePermission(int PermissionId);
|
||||
string EncodePermissions(int EntityId, List<Permission> Permissions);
|
||||
List<Permission> DecodePermissions(string Permissions, int SiteId, string EntityName, int EntityId);
|
||||
}
|
||||
}
|
@ -8,10 +8,12 @@ namespace Oqtane.Repository
|
||||
public class ModuleRepository : IModuleRepository
|
||||
{
|
||||
private TenantDBContext db;
|
||||
private readonly IPermissionRepository Permissions;
|
||||
|
||||
public ModuleRepository(TenantDBContext context)
|
||||
public ModuleRepository(TenantDBContext context, IPermissionRepository Permissions)
|
||||
{
|
||||
db = context;
|
||||
this.Permissions = Permissions;
|
||||
}
|
||||
|
||||
public IEnumerable<Module> GetModules()
|
||||
@ -30,10 +32,16 @@ namespace Oqtane.Repository
|
||||
{
|
||||
try
|
||||
{
|
||||
return db.Module
|
||||
List<Permission> permissions = Permissions.GetPermissions(SiteId, "Module").ToList();
|
||||
List<Module> modules = db.Module
|
||||
.Where(item => item.SiteId == SiteId)
|
||||
.Where(item => item.ModuleDefinitionName == ModuleDefinitionName)
|
||||
.ToList();
|
||||
foreach (Module module in modules)
|
||||
{
|
||||
module.Permissions = Permissions.EncodePermissions(module.ModuleId, permissions);
|
||||
}
|
||||
return modules;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -47,6 +55,7 @@ namespace Oqtane.Repository
|
||||
{
|
||||
db.Module.Add(Module);
|
||||
db.SaveChanges();
|
||||
Permissions.UpdatePermissions(Module.SiteId, "Module", Module.ModuleId, Module.Permissions);
|
||||
return Module;
|
||||
}
|
||||
catch
|
||||
@ -61,6 +70,7 @@ namespace Oqtane.Repository
|
||||
{
|
||||
db.Entry(Module).State = EntityState.Modified;
|
||||
db.SaveChanges();
|
||||
Permissions.UpdatePermissions(Module.SiteId, "Module", Module.ModuleId, Module.Permissions);
|
||||
return Module;
|
||||
}
|
||||
catch
|
||||
@ -73,7 +83,13 @@ namespace Oqtane.Repository
|
||||
{
|
||||
try
|
||||
{
|
||||
return db.Module.Find(ModuleId);
|
||||
Module module = db.Module.Find(ModuleId);
|
||||
if (module != null)
|
||||
{
|
||||
List<Permission> permissions = Permissions.GetPermissions("Module", module.ModuleId).ToList();
|
||||
module.Permissions = Permissions.EncodePermissions(module.ModuleId, permissions);
|
||||
}
|
||||
return module;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -86,6 +102,7 @@ namespace Oqtane.Repository
|
||||
try
|
||||
{
|
||||
Module Module = db.Module.Find(ModuleId);
|
||||
Permissions.UpdatePermissions(Module.SiteId, "Module", ModuleId, "");
|
||||
db.Module.Remove(Module);
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
@ -8,10 +8,12 @@ namespace Oqtane.Repository
|
||||
public class PageModuleRepository : IPageModuleRepository
|
||||
{
|
||||
private TenantDBContext db;
|
||||
private readonly IPermissionRepository Permissions;
|
||||
|
||||
public PageModuleRepository(TenantDBContext context)
|
||||
public PageModuleRepository(TenantDBContext context, IPermissionRepository Permissions)
|
||||
{
|
||||
db = context;
|
||||
this.Permissions = Permissions;
|
||||
}
|
||||
|
||||
public IEnumerable<PageModule> GetPageModules()
|
||||
@ -29,9 +31,18 @@ namespace Oqtane.Repository
|
||||
{
|
||||
try
|
||||
{
|
||||
return db.PageModule.Where(item => item.PageId == PageId)
|
||||
List<PageModule> pagemodules = db.PageModule.Where(item => item.PageId == PageId)
|
||||
.Include(item => item.Module) // eager load modules
|
||||
.ToList();
|
||||
if (pagemodules != null && pagemodules.Any())
|
||||
{
|
||||
List<Permission> permissions = Permissions.GetPermissions(pagemodules.FirstOrDefault().Module.SiteId, "Module").ToList();
|
||||
foreach (PageModule pagemodule in pagemodules)
|
||||
{
|
||||
pagemodule.Module.Permissions = Permissions.EncodePermissions(pagemodule.ModuleId, permissions);
|
||||
}
|
||||
}
|
||||
return pagemodules;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -71,8 +82,14 @@ namespace Oqtane.Repository
|
||||
{
|
||||
try
|
||||
{
|
||||
return db.PageModule.Include(item => item.Module) // eager load modules
|
||||
.SingleOrDefault(item => item.PageModuleId == PageModuleId);
|
||||
PageModule pagemodule = db.PageModule.Include(item => item.Module) // eager load modules
|
||||
.SingleOrDefault(item => item.PageModuleId == PageModuleId);
|
||||
if (pagemodule != null)
|
||||
{
|
||||
List<Permission> permissions = Permissions.GetPermissions("Module", pagemodule.ModuleId).ToList();
|
||||
pagemodule.Module.Permissions = Permissions.EncodePermissions(pagemodule.ModuleId, permissions);
|
||||
}
|
||||
return pagemodule;
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -8,10 +8,12 @@ namespace Oqtane.Repository
|
||||
public class PageRepository : IPageRepository
|
||||
{
|
||||
private TenantDBContext db;
|
||||
private readonly IPermissionRepository Permissions;
|
||||
|
||||
public PageRepository(TenantDBContext context)
|
||||
public PageRepository(TenantDBContext context, IPermissionRepository Permissions)
|
||||
{
|
||||
db = context;
|
||||
this.Permissions = Permissions;
|
||||
}
|
||||
|
||||
public IEnumerable<Page> GetPages()
|
||||
@ -30,7 +32,13 @@ namespace Oqtane.Repository
|
||||
{
|
||||
try
|
||||
{
|
||||
return db.Page.Where(item => item.SiteId == SiteId).ToList();
|
||||
List<Permission> permissions = Permissions.GetPermissions(SiteId, "Page").ToList();
|
||||
List<Page> pages = db.Page.Where(item => item.SiteId == SiteId).ToList();
|
||||
foreach(Page page in pages)
|
||||
{
|
||||
page.Permissions = Permissions.EncodePermissions(page.PageId, permissions);
|
||||
}
|
||||
return pages;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -44,6 +52,7 @@ namespace Oqtane.Repository
|
||||
{
|
||||
db.Page.Add(Page);
|
||||
db.SaveChanges();
|
||||
Permissions.UpdatePermissions(Page.SiteId, "Page", Page.PageId, Page.Permissions);
|
||||
return Page;
|
||||
}
|
||||
catch
|
||||
@ -58,6 +67,7 @@ namespace Oqtane.Repository
|
||||
{
|
||||
db.Entry(Page).State = EntityState.Modified;
|
||||
db.SaveChanges();
|
||||
Permissions.UpdatePermissions(Page.SiteId, "Page", Page.PageId, Page.Permissions);
|
||||
return Page;
|
||||
}
|
||||
catch
|
||||
@ -70,7 +80,13 @@ namespace Oqtane.Repository
|
||||
{
|
||||
try
|
||||
{
|
||||
return db.Page.Find(PageId);
|
||||
Page page = db.Page.Find(PageId);
|
||||
if (page != null)
|
||||
{
|
||||
List<Permission> permissions = Permissions.GetPermissions("Page", page.PageId).ToList();
|
||||
page.Permissions = Permissions.EncodePermissions(page.PageId, permissions);
|
||||
}
|
||||
return page;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -83,6 +99,7 @@ namespace Oqtane.Repository
|
||||
try
|
||||
{
|
||||
Page Page = db.Page.Find(PageId);
|
||||
Permissions.UpdatePermissions(Page.SiteId, "Page", PageId, "");
|
||||
db.Page.Remove(Page);
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
236
Oqtane.Server/Repository/PermissionRepository.cs
Normal file
236
Oqtane.Server/Repository/PermissionRepository.cs
Normal file
@ -0,0 +1,236 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Oqtane.Models;
|
||||
using System.Text;
|
||||
using System;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
{
|
||||
public class PermissionRepository : IPermissionRepository
|
||||
{
|
||||
private TenantDBContext db;
|
||||
private readonly IRoleRepository Roles;
|
||||
|
||||
public PermissionRepository(TenantDBContext context, IRoleRepository Roles)
|
||||
{
|
||||
db = context;
|
||||
this.Roles = Roles;
|
||||
}
|
||||
|
||||
public IEnumerable<Permission> GetPermissions(int SiteId, string EntityName)
|
||||
{
|
||||
try
|
||||
{
|
||||
return db.Permission.Where(item => item.SiteId == SiteId)
|
||||
.Where(item => item.EntityName == EntityName)
|
||||
.Include(item => item.Role); // eager load roles
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Permission> GetPermissions(string EntityName, int EntityId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return db.Permission.Where(item => item.EntityName == EntityName)
|
||||
.Where(item => item.EntityId == EntityId)
|
||||
.Include(item => item.Role); // eager load roles
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Permission> GetPermissions(string EntityName, int EntityId, string PermissionName)
|
||||
{
|
||||
try
|
||||
{
|
||||
return db.Permission.Where(item => item.EntityName == EntityName)
|
||||
.Where(item => item.EntityId == EntityId)
|
||||
.Where(item => item.PermissionName == PermissionName)
|
||||
.Include(item => item.Role); // eager load roles
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public Permission AddPermission(Permission Permission)
|
||||
{
|
||||
try
|
||||
{
|
||||
db.Permission.Add(Permission);
|
||||
db.SaveChanges();
|
||||
return Permission;
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public Permission UpdatePermission(Permission Permission)
|
||||
{
|
||||
try
|
||||
{
|
||||
db.Entry(Permission).State = EntityState.Modified;
|
||||
db.SaveChanges();
|
||||
return Permission;
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdatePermissions(int SiteId, string EntityName, int EntityId, string Permissions)
|
||||
{
|
||||
// get current permissions and delete
|
||||
List<Permission> permissions = db.Permission.Where(item => item.EntityName == EntityName)
|
||||
.Where(item => item.EntityId == EntityId).ToList();
|
||||
foreach(Permission permission in permissions)
|
||||
{
|
||||
db.Permission.Remove(permission);
|
||||
}
|
||||
// add permissions
|
||||
permissions = DecodePermissions(Permissions, SiteId, EntityName, EntityId);
|
||||
foreach (Permission permission in permissions)
|
||||
{
|
||||
db.Permission.Add(permission);
|
||||
}
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
public Permission GetPermission(int PermissionId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return db.Permission.Find(PermissionId);
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public void DeletePermission(int PermissionId)
|
||||
{
|
||||
try
|
||||
{
|
||||
Permission Permission = db.Permission.Find(PermissionId);
|
||||
db.Permission.Remove(Permission);
|
||||
db.SaveChanges();
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// permissions are stored in the format "{permissionname:!rolename1;![userid1];rolename2;rolename3;[userid2];[userid3]}" where "!" designates Deny permissions
|
||||
public string EncodePermissions(int EntityId, List<Permission> Permissions)
|
||||
{
|
||||
string permissions = "";
|
||||
string permissionname = "";
|
||||
StringBuilder permissionsbuilder = new StringBuilder();
|
||||
string perm = "";
|
||||
foreach (Permission permission in Permissions.Where(item => item.EntityId == EntityId).OrderBy(item => item.PermissionName))
|
||||
{
|
||||
// permission collections are grouped by permissionname
|
||||
if (permissionname != permission.PermissionName)
|
||||
{
|
||||
permissionname = permission.PermissionName;
|
||||
permissions += permissionsbuilder.ToString();
|
||||
permissions += ((permissions != "") ? "}" : "") + "{" + permissionname + ":";
|
||||
permissionsbuilder = new StringBuilder();
|
||||
}
|
||||
|
||||
// deny permissions are prefixed with a "!"
|
||||
string prefix = !permission.IsAuthorized ? "!" : "";
|
||||
|
||||
// encode permission
|
||||
if (permission.UserId == null)
|
||||
{
|
||||
perm = prefix + permission.Role.Name + ";";
|
||||
}
|
||||
else
|
||||
{
|
||||
perm = prefix + "[" + permission.UserId.ToString() + "];";
|
||||
}
|
||||
|
||||
// insert Deny permissions at the beginning and append Grant permissions at the end
|
||||
if (prefix == "!")
|
||||
{
|
||||
permissionsbuilder.Insert(0, perm);
|
||||
}
|
||||
else
|
||||
{
|
||||
permissionsbuilder.Append(perm);
|
||||
}
|
||||
}
|
||||
|
||||
if (permissionsbuilder.ToString() != "")
|
||||
{
|
||||
permissions += permissionsbuilder.ToString() + "}";
|
||||
}
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
public List<Permission> DecodePermissions(string Permissions, int SiteId, string EntityName, int EntityId)
|
||||
{
|
||||
List<Role> roles = Roles.GetRoles(SiteId).ToList();
|
||||
List<Permission> permissions = new List<Permission>();
|
||||
string perm = "";
|
||||
string permissionname;
|
||||
string permissionstring;
|
||||
foreach (string PermissionString in Permissions.Split(new char[] { '{' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
permissionname = PermissionString.Substring(0, PermissionString.IndexOf(":"));
|
||||
permissionstring = PermissionString.Replace(permissionname + ":", "").Replace("}", "");
|
||||
foreach (string Perm in permissionstring.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
perm = Perm;
|
||||
Permission permission = new Permission();
|
||||
permission.SiteId = SiteId;
|
||||
permission.EntityName = EntityName;
|
||||
permission.EntityId = EntityId;
|
||||
permission.PermissionName = permissionname;
|
||||
permission.RoleId = null;
|
||||
permission.UserId = null;
|
||||
permission.IsAuthorized = true;
|
||||
|
||||
if (perm.StartsWith("!"))
|
||||
{
|
||||
// deny permission
|
||||
perm.Replace("!", "");
|
||||
permission.IsAuthorized = false;
|
||||
}
|
||||
if (perm.StartsWith("[") && perm.EndsWith("]"))
|
||||
{
|
||||
// user id
|
||||
perm = perm.Replace("[", "").Replace("]", "");
|
||||
permission.UserId = int.Parse(perm);
|
||||
}
|
||||
else
|
||||
{
|
||||
// role name
|
||||
Role role = roles.Where(item => item.Name == perm).SingleOrDefault();
|
||||
if (role != null)
|
||||
{
|
||||
permission.RoleId = role.RoleId;
|
||||
}
|
||||
}
|
||||
permissions.Add(permission);
|
||||
}
|
||||
}
|
||||
return permissions;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
using System.Linq;
|
||||
using Oqtane.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System;
|
||||
|
||||
namespace Oqtane.Repository
|
||||
{
|
||||
@ -21,8 +22,8 @@ namespace Oqtane.Repository
|
||||
// get alias based on request context
|
||||
aliasname = accessor.HttpContext.Request.Host.Value;
|
||||
string path = accessor.HttpContext.Request.Path.Value;
|
||||
string[] segments = path.Split('/');
|
||||
if (segments[0] == "api" && segments[1] != "~")
|
||||
string[] segments = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (segments.Length > 0 && segments[0] == "api" && segments[1] != "~")
|
||||
{
|
||||
aliasname += "/" + segments[1];
|
||||
}
|
||||
|
Reference in New Issue
Block a user