allow themes to define usage permissions similar to modules
This commit is contained in:
@ -252,7 +252,7 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized ModuleDefinition Delete Attempt {ModuleDefinitionId}", id);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized ModuleDefinition Delete Attempt {ModuleDefinitionId} {SiteId}", id, siteid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,9 @@ using System.Text.Json;
|
||||
using System.Net;
|
||||
using System;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System.Reflection.Metadata;
|
||||
using Oqtane.Security;
|
||||
using System.Security.Policy;
|
||||
|
||||
// ReSharper disable StringIndexOfIsCultureSpecific.1
|
||||
|
||||
@ -26,30 +29,50 @@ namespace Oqtane.Controllers
|
||||
private readonly IInstallationManager _installationManager;
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
private readonly ITenantManager _tenantManager;
|
||||
private readonly IUserPermissions _userPermissions;
|
||||
private readonly ISyncManager _syncManager;
|
||||
private readonly ILogManager _logger;
|
||||
private readonly Alias _alias;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public ThemeController(IThemeRepository themes, IInstallationManager installationManager, IWebHostEnvironment environment, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger, IServiceProvider serviceProvider)
|
||||
public ThemeController(IThemeRepository themes, IInstallationManager installationManager, IWebHostEnvironment environment, ITenantManager tenantManager, IUserPermissions userPermissions, ISyncManager syncManager, ILogManager logger, IServiceProvider serviceProvider)
|
||||
{
|
||||
_themes = themes;
|
||||
_installationManager = installationManager;
|
||||
_environment = environment;
|
||||
_tenantManager = tenantManager;
|
||||
_userPermissions = userPermissions;
|
||||
_syncManager = syncManager;
|
||||
_logger = logger;
|
||||
_alias = tenantManager.GetAlias();
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
// GET: api/<controller>
|
||||
// GET: api/<controller>?siteid=x
|
||||
[HttpGet]
|
||||
[Authorize(Roles = RoleNames.Registered)]
|
||||
public IEnumerable<Theme> Get()
|
||||
public IEnumerable<Theme> Get(string siteid)
|
||||
{
|
||||
return _themes.GetThemes();
|
||||
}
|
||||
int SiteId;
|
||||
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
|
||||
{
|
||||
List<Theme> themes = new List<Theme>();
|
||||
foreach (Theme theme in _themes.GetThemes(SiteId))
|
||||
{
|
||||
if (_userPermissions.IsAuthorized(User, PermissionNames.Utilize, theme.PermissionList))
|
||||
{
|
||||
themes.Add(theme);
|
||||
}
|
||||
}
|
||||
return themes;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Theme Get Attempt {SiteId}", siteid);
|
||||
HttpContext.Response.StatusCode = (int) HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// GET api/<controller>/5?siteid=x
|
||||
[HttpGet("{id}")]
|
||||
@ -58,7 +81,24 @@ namespace Oqtane.Controllers
|
||||
int SiteId;
|
||||
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
|
||||
{
|
||||
return _themes.GetTheme(id, SiteId);
|
||||
Theme theme = _themes.GetTheme(id, SiteId);
|
||||
if (theme != null && _userPermissions.IsAuthorized(User, PermissionNames.Utilize, theme.PermissionList))
|
||||
{
|
||||
return theme;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (theme != null)
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Theme Get Attempt {ThemeId} {SiteId}", id, siteid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -86,14 +126,13 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE api/<controller>/xxx
|
||||
// DELETE api/<controller>/5?siteid=x
|
||||
[HttpDelete("{themename}")]
|
||||
[Authorize(Roles = RoleNames.Host)]
|
||||
public void Delete(string themename)
|
||||
public void Delete(int id, int siteid)
|
||||
{
|
||||
List<Theme> themes = _themes.GetThemes().ToList();
|
||||
Theme theme = themes.Where(item => item.ThemeName == themename).FirstOrDefault();
|
||||
if (theme != null && Utilities.GetAssemblyName(theme.ThemeName) != Constants.ClientId)
|
||||
Theme theme = _themes.GetTheme(id, siteid);
|
||||
if (theme != null && theme.SiteId == _alias.SiteId && Utilities.GetAssemblyName(theme.ThemeName) != Constants.ClientId)
|
||||
{
|
||||
// remove theme assets
|
||||
if (_installationManager.UninstallPackage(theme.PackageName))
|
||||
@ -126,7 +165,7 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Theme Delete Attempt {Themename}", themename);
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Theme Delete Attempt {ThemeId} {SiteId}", id, siteid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
}
|
||||
|
||||
@ -386,6 +386,7 @@ namespace Oqtane.Repository
|
||||
moduledefinition.Categories = "Common";
|
||||
}
|
||||
|
||||
// default permissions
|
||||
if (moduledefinition.Categories == "Admin")
|
||||
{
|
||||
var shortName = moduledefinition.ModuleDefinitionName.Replace("Oqtane.Modules.Admin.", "").Replace(", Oqtane.Client", "");
|
||||
@ -455,18 +456,21 @@ namespace Oqtane.Repository
|
||||
private List<Permission> ClonePermissions(int siteId, List<Permission> permissionList)
|
||||
{
|
||||
var permissions = new List<Permission>();
|
||||
foreach (var p in permissionList)
|
||||
if (permissionList != null)
|
||||
{
|
||||
var permission = new Permission();
|
||||
permission.SiteId = siteId;
|
||||
permission.EntityName = p.EntityName;
|
||||
permission.EntityId = p.EntityId;
|
||||
permission.PermissionName = p.PermissionName;
|
||||
permission.RoleId = null;
|
||||
permission.RoleName = p.RoleName;
|
||||
permission.UserId = p.UserId;
|
||||
permission.IsAuthorized = p.IsAuthorized;
|
||||
permissions.Add(permission);
|
||||
foreach (var p in permissionList)
|
||||
{
|
||||
var permission = new Permission();
|
||||
permission.SiteId = siteId;
|
||||
permission.EntityName = p.EntityName;
|
||||
permission.EntityId = p.EntityId;
|
||||
permission.PermissionName = p.PermissionName;
|
||||
permission.RoleId = null;
|
||||
permission.RoleName = p.RoleName;
|
||||
permission.UserId = p.UserId;
|
||||
permission.IsAuthorized = p.IsAuthorized;
|
||||
permissions.Add(permission);
|
||||
}
|
||||
}
|
||||
return permissions;
|
||||
}
|
||||
|
||||
@ -135,7 +135,7 @@ namespace Oqtane.Repository
|
||||
if (site != null)
|
||||
{
|
||||
// initialize theme Assemblies
|
||||
site.Themes = _themeRepository.GetThemes().ToList();
|
||||
site.Themes = _themeRepository.GetThemes(site.SiteId).ToList();
|
||||
|
||||
// initialize module Assemblies
|
||||
var moduleDefinitions = _moduleDefinitionRepository.GetModuleDefinitions(alias.SiteId);
|
||||
|
||||
@ -15,7 +15,7 @@ namespace Oqtane.Repository
|
||||
{
|
||||
public interface IThemeRepository
|
||||
{
|
||||
IEnumerable<Theme> GetThemes();
|
||||
IEnumerable<Theme> GetThemes(int siteId);
|
||||
Theme GetTheme(int themeId, int siteId);
|
||||
void UpdateTheme(Theme theme);
|
||||
void DeleteTheme(int themeId);
|
||||
@ -26,24 +26,25 @@ namespace Oqtane.Repository
|
||||
{
|
||||
private MasterDBContext _db;
|
||||
private readonly IMemoryCache _cache;
|
||||
private readonly IPermissionRepository _permissions;
|
||||
private readonly ITenantManager _tenants;
|
||||
private readonly ISettingRepository _settings;
|
||||
private readonly IServerStateManager _serverState;
|
||||
private readonly string settingprefix = "SiteEnabled:";
|
||||
|
||||
public ThemeRepository(MasterDBContext context, IMemoryCache cache, ITenantManager tenants, ISettingRepository settings, IServerStateManager serverState)
|
||||
public ThemeRepository(MasterDBContext context, IMemoryCache cache, IPermissionRepository permissions, ITenantManager tenants, ISettingRepository settings, IServerStateManager serverState)
|
||||
{
|
||||
_db = context;
|
||||
_cache = cache;
|
||||
_permissions = permissions;
|
||||
_tenants = tenants;
|
||||
_settings = settings;
|
||||
_serverState = serverState;
|
||||
}
|
||||
|
||||
public IEnumerable<Theme> GetThemes()
|
||||
public IEnumerable<Theme> GetThemes(int siteId)
|
||||
{
|
||||
// for consistency siteid should be passed in as parameter, but this would require breaking change
|
||||
return LoadThemes(_tenants.GetAlias().SiteId);
|
||||
return LoadThemes(siteId);
|
||||
}
|
||||
|
||||
public Theme GetTheme(int themeId, int siteId)
|
||||
@ -56,6 +57,7 @@ namespace Oqtane.Repository
|
||||
{
|
||||
_db.Entry(theme).State = EntityState.Modified;
|
||||
_db.SaveChanges();
|
||||
_permissions.UpdatePermissions(theme.SiteId, EntityNames.Theme, theme.ThemeId, theme.PermissionList);
|
||||
|
||||
var settingname = $"{settingprefix}{_tenants.GetAlias().SiteKey}";
|
||||
var setting = _settings.GetSetting(EntityNames.Theme, theme.ThemeId, settingname);
|
||||
@ -96,6 +98,7 @@ namespace Oqtane.Repository
|
||||
Theme.ThemeSettingsType = theme.ThemeSettingsType;
|
||||
Theme.ContainerSettingsType = theme.ContainerSettingsType;
|
||||
Theme.PackageName = theme.PackageName;
|
||||
Theme.PermissionList = theme.PermissionList;
|
||||
Theme.Fingerprint = Utilities.GenerateSimpleHash(theme.ModifiedOn.ToString("yyyyMMddHHmm"));
|
||||
Themes.Add(Theme);
|
||||
}
|
||||
@ -176,6 +179,9 @@ namespace Oqtane.Repository
|
||||
var siteKey = _tenants.GetAlias().SiteKey;
|
||||
var assemblies = new List<string>();
|
||||
|
||||
// get all module definition permissions for site
|
||||
List<Permission> permissions = _permissions.GetPermissions(siteId, EntityNames.Theme).ToList();
|
||||
|
||||
// get settings for site
|
||||
var settings = _settings.GetSettings(EntityNames.Theme).ToList();
|
||||
|
||||
@ -212,6 +218,26 @@ namespace Oqtane.Repository
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (permissions.Count == 0)
|
||||
{
|
||||
// no module definition permissions exist for this site
|
||||
theme.PermissionList = ClonePermissions(siteId, theme.PermissionList);
|
||||
_permissions.UpdatePermissions(siteId, EntityNames.Theme, theme.ThemeId, theme.PermissionList);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (permissions.Any(item => item.EntityId == theme.ThemeId))
|
||||
{
|
||||
theme.PermissionList = permissions.Where(item => item.EntityId == theme.ThemeId).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
// permissions for theme do not exist for this site
|
||||
theme.PermissionList = ClonePermissions(siteId, theme.PermissionList);
|
||||
_permissions.UpdatePermissions(siteId, EntityNames.Theme, theme.ThemeId, theme.PermissionList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cache site assemblies
|
||||
@ -220,6 +246,20 @@ namespace Oqtane.Repository
|
||||
{
|
||||
if (!serverState.Assemblies.Contains(assembly)) serverState.Assemblies.Add(assembly);
|
||||
}
|
||||
|
||||
// clean up any orphaned permissions
|
||||
var ids = new HashSet<int>(Themes.Select(item => item.ThemeId));
|
||||
foreach (var permission in permissions.Where(item => !ids.Contains(item.EntityId)))
|
||||
{
|
||||
try
|
||||
{
|
||||
_permissions.DeletePermission(permission.PermissionId);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// multi-threading can cause a race condition to occur
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Themes;
|
||||
@ -295,6 +335,14 @@ namespace Oqtane.Repository
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// default permissions
|
||||
theme.PermissionList = new List<Permission>
|
||||
{
|
||||
new Permission(PermissionNames.Utilize, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Utilize, RoleNames.Registered, true)
|
||||
};
|
||||
|
||||
Debug.WriteLine($"Oqtane Info: Registering Theme {theme.ThemeName}");
|
||||
themes.Add(theme);
|
||||
index = themes.FindIndex(item => item.ThemeName == qualifiedThemeType);
|
||||
@ -335,5 +383,27 @@ namespace Oqtane.Repository
|
||||
}
|
||||
return themes;
|
||||
}
|
||||
|
||||
private List<Permission> ClonePermissions(int siteId, List<Permission> permissionList)
|
||||
{
|
||||
var permissions = new List<Permission>();
|
||||
if (permissionList != null)
|
||||
{
|
||||
foreach (var p in permissionList)
|
||||
{
|
||||
var permission = new Permission();
|
||||
permission.SiteId = siteId;
|
||||
permission.EntityName = p.EntityName;
|
||||
permission.EntityId = p.EntityId;
|
||||
permission.PermissionName = p.PermissionName;
|
||||
permission.RoleId = null;
|
||||
permission.RoleName = p.RoleName;
|
||||
permission.UserId = p.UserId;
|
||||
permission.IsAuthorized = p.IsAuthorized;
|
||||
permissions.Add(permission);
|
||||
}
|
||||
}
|
||||
return permissions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,7 +144,7 @@ namespace Oqtane.Services
|
||||
}
|
||||
|
||||
// themes
|
||||
site.Themes = _themes.FilterThemes(_themes.GetThemes().ToList());
|
||||
site.Themes = _themes.FilterThemes(_themes.GetThemes(site.SiteId).ToList());
|
||||
|
||||
// installation date used for fingerprinting static assets
|
||||
site.Fingerprint = Utilities.GenerateSimpleHash(_configManager.GetSetting("InstallationDate", DateTime.UtcNow.ToString("yyyyMMddHHmm")));
|
||||
|
||||
Reference in New Issue
Block a user