add support for API permissions at the UI layer - including ability to delegate user, role, profile management
This commit is contained in:
@ -1,172 +0,0 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using System.Collections.Generic;
|
||||
using Oqtane.Shared;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Enums;
|
||||
using System.Net;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Extensions;
|
||||
using System.Reflection;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Oqtane.Controllers
|
||||
{
|
||||
[Route(ControllerRoutes.ApiRoute)]
|
||||
public class ApiController : Controller
|
||||
{
|
||||
private readonly IPermissionRepository _permissions;
|
||||
private readonly IRoleRepository _roles;
|
||||
private readonly ILogManager _logger;
|
||||
private readonly Alias _alias;
|
||||
|
||||
public ApiController(IPermissionRepository permissions, IRoleRepository roles, ILogManager logger, ITenantManager tenantManager)
|
||||
{
|
||||
_permissions = permissions;
|
||||
_roles = roles;
|
||||
_logger = logger;
|
||||
_alias = tenantManager.GetAlias();
|
||||
}
|
||||
|
||||
// GET: api/<controller>?siteid=x
|
||||
[HttpGet]
|
||||
[Authorize(Roles = RoleNames.Admin)]
|
||||
public List<Api> Get(string siteid)
|
||||
{
|
||||
int SiteId;
|
||||
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
|
||||
{
|
||||
var apis = new List<Api>();
|
||||
|
||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
// iterate controllers
|
||||
foreach (var type in assembly.GetTypes().Where(type => typeof(Controller).IsAssignableFrom(type)))
|
||||
{
|
||||
// iterate controller methods with authorize attribute
|
||||
var actions = type.GetMethods(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(m => m.GetCustomAttributes<AuthorizeAttribute>().Any());
|
||||
foreach(var action in actions)
|
||||
{
|
||||
// get policy
|
||||
var policy = action.GetCustomAttribute<AuthorizeAttribute>().Policy;
|
||||
if (!string.IsNullOrEmpty(policy) && policy.Contains(":") && !policy.Contains(Constants.RequireEntityId))
|
||||
{
|
||||
// parse policy
|
||||
var segments = policy.Split(':');
|
||||
if (!apis.Any(item => item.EntityName == segments[0]))
|
||||
{
|
||||
apis.Add(new Api { SiteId = SiteId, EntityName = segments[0], Permissions = segments[1] });
|
||||
}
|
||||
else
|
||||
{
|
||||
// concatenate permissions
|
||||
var permissions = apis.SingleOrDefault(item => item.EntityName == segments[0]).Permissions;
|
||||
if (!permissions.Split(',').Contains(segments[1]))
|
||||
{
|
||||
apis.SingleOrDefault(item => item.EntityName == segments[0]).Permissions += "," + segments[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return apis;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Api Get Attempt {SiteId}", siteid);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// GET: api/<controller>/1/user
|
||||
[HttpGet("{siteid}/{entityname}")]
|
||||
[Authorize(Roles = RoleNames.Admin)]
|
||||
public Api Get(int siteid, string entityname)
|
||||
{
|
||||
if (siteid == _alias.SiteId)
|
||||
{
|
||||
var permissions = _permissions.GetPermissions(siteid, entityname);
|
||||
if (permissions == null || permissions.ToList().Count == 0)
|
||||
{
|
||||
permissions = GetPermissions(siteid, entityname);
|
||||
}
|
||||
return new Api { SiteId = siteid, EntityName = entityname, Permissions = permissions.EncodePermissions() };
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Api Get Attempt {SiteId} {EntityName}", siteid, entityname);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// POST: api/<controller>
|
||||
[HttpPost]
|
||||
[Authorize(Roles = RoleNames.Admin)]
|
||||
public void Post([FromBody] Api api)
|
||||
{
|
||||
if (ModelState.IsValid && api.SiteId == _alias.SiteId)
|
||||
{
|
||||
_permissions.UpdatePermissions(api.SiteId, api.EntityName, -1, api.Permissions);
|
||||
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Api Updated {Api}", api);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Api Post Attempt {Api}", api);
|
||||
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Permission> GetPermissions(int siteid, string entityname)
|
||||
{
|
||||
var permissions = new List<Permission>();
|
||||
|
||||
var assemblies = AppDomain.CurrentDomain.GetOqtaneAssemblies();
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
// iterate controllers
|
||||
foreach (var type in assembly.GetTypes().Where(type => typeof(Controller).IsAssignableFrom(type)))
|
||||
{
|
||||
// iterate controller methods with authorize attribute
|
||||
var actions = type.GetMethods(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(m => m.GetCustomAttributes<AuthorizeAttribute>().Any());
|
||||
foreach (var action in actions)
|
||||
{
|
||||
// get policy
|
||||
var policy = action.GetCustomAttribute<AuthorizeAttribute>().Policy;
|
||||
if (!string.IsNullOrEmpty(policy) && policy.Contains(":") && !policy.Contains(Constants.RequireEntityId))
|
||||
{
|
||||
// parse policy
|
||||
var segments = policy.Split(':');
|
||||
// entity match
|
||||
if (segments[0] == entityname && segments.Length > 2)
|
||||
{
|
||||
var roles = _roles.GetRoles(siteid);
|
||||
foreach (var rolename in (segments[2]).Split(','))
|
||||
{
|
||||
var role = roles.FirstOrDefault(item => item.Name == rolename);
|
||||
if (role != null)
|
||||
{
|
||||
if (!permissions.Any(item => item.EntityName == entityname && item.PermissionName == segments[1] && item.RoleId == role.RoleId))
|
||||
{
|
||||
permissions.Add(new Permission { SiteId = siteid, EntityName = entityname, EntityId = -1, PermissionName = segments[1], RoleId = role.RoleId, Role = role, UserId = null, IsAuthorized = true });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return permissions;
|
||||
}
|
||||
}
|
||||
}
|
@ -47,7 +47,6 @@ namespace Oqtane.Controllers
|
||||
int SiteId;
|
||||
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
|
||||
{
|
||||
List<ModuleDefinition> moduledefinitions = _moduleDefinitions.GetModuleDefinitions(SiteId).ToList();
|
||||
List<Setting> settings = _settings.GetSettings(EntityNames.Module).ToList();
|
||||
|
||||
foreach (PageModule pagemodule in _pageModules.GetPageModules(SiteId))
|
||||
@ -75,7 +74,6 @@ namespace Oqtane.Controllers
|
||||
module.Order = pagemodule.Order;
|
||||
module.ContainerType = pagemodule.ContainerType;
|
||||
|
||||
module.ModuleDefinition = moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName);
|
||||
module.Settings = settings.Where(item => item.EntityId == pagemodule.ModuleId)
|
||||
.Where(item => !item.IsPrivate || _userPermissions.IsAuthorized(User, PermissionNames.Edit, pagemodule.Module.Permissions))
|
||||
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
|
||||
|
@ -281,7 +281,7 @@ namespace Oqtane.Controllers
|
||||
// synchronize module permissions
|
||||
if (added.Count > 0 || removed.Count > 0)
|
||||
{
|
||||
foreach (PageModule pageModule in _pageModules.GetPageModules(page.PageId, "").ToList())
|
||||
foreach (PageModule pageModule in _pageModules.GetPageModules(page.SiteId).Where(item => item.PageId == page.PageId).ToList())
|
||||
{
|
||||
var modulePermissions = _permissionRepository.GetPermissions(pageModule.Module.SiteId, EntityNames.Module, pageModule.Module.ModuleId).ToList();
|
||||
// permissions added
|
||||
|
@ -120,7 +120,8 @@ namespace Oqtane.Controllers
|
||||
if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, page.SiteId, EntityNames.Page, pageid, PermissionNames.Edit))
|
||||
{
|
||||
int order = 1;
|
||||
List<PageModule> pagemodules = _pageModules.GetPageModules(pageid, pane).OrderBy(item => item.Order).ToList();
|
||||
List<PageModule> pagemodules = _pageModules.GetPageModules(page.SiteId)
|
||||
.Where(item => item.PageId == pageid && item.Pane == pane).OrderBy(item => item.Order).ToList();
|
||||
foreach (PageModule pagemodule in pagemodules)
|
||||
{
|
||||
if (pagemodule.Order != order)
|
||||
|
@ -28,7 +28,7 @@ namespace Oqtane.Controllers
|
||||
|
||||
// GET: api/<controller>?siteid=x
|
||||
[HttpGet]
|
||||
[Authorize(Policy = $"{EntityNames.Profile}:{PermissionNames.Read}:{RoleNames.Registered}")]
|
||||
[Authorize(Roles = RoleNames.Registered)]
|
||||
public IEnumerable<Profile> Get(string siteid)
|
||||
{
|
||||
int SiteId;
|
||||
@ -46,7 +46,7 @@ namespace Oqtane.Controllers
|
||||
|
||||
// GET api/<controller>/5
|
||||
[HttpGet("{id}")]
|
||||
[Authorize(Policy = $"{EntityNames.Profile}:{PermissionNames.Read}:{RoleNames.Registered}")]
|
||||
[Authorize(Roles = RoleNames.Registered)]
|
||||
public Profile Get(int id)
|
||||
{
|
||||
var profile = _profiles.GetProfile(id);
|
||||
|
@ -28,7 +28,7 @@ namespace Oqtane.Controllers
|
||||
|
||||
// GET: api/<controller>?siteid=x&global=true/false
|
||||
[HttpGet]
|
||||
[Authorize(Policy = $"{EntityNames.Role}:{PermissionNames.Read}:{RoleNames.Registered}")]
|
||||
[Authorize(Roles = RoleNames.Registered)]
|
||||
public IEnumerable<Role> Get(string siteid, string global)
|
||||
{
|
||||
int SiteId;
|
||||
@ -50,7 +50,7 @@ namespace Oqtane.Controllers
|
||||
|
||||
// GET api/<controller>/5
|
||||
[HttpGet("{id}")]
|
||||
[Authorize(Policy = $"{EntityNames.Role}:{PermissionNames.Read}:{RoleNames.Registered}")]
|
||||
[Authorize(Roles = RoleNames.Registered)]
|
||||
public Role Get(int id)
|
||||
{
|
||||
var role = _roles.GetRole(id);
|
||||
|
@ -212,7 +212,7 @@ namespace Oqtane.Controllers
|
||||
authorized = true;
|
||||
if (permissionName == PermissionNames.Edit)
|
||||
{
|
||||
authorized = User.IsInRole(RoleNames.Admin) || (_userPermissions.GetUser(User).UserId == entityId);
|
||||
authorized = _userPermissions.IsAuthorized(User, _alias.SiteId, entityName, -1, PermissionNames.Write, RoleNames.Admin) || (_userPermissions.GetUser(User).UserId == entityId);
|
||||
}
|
||||
break;
|
||||
case EntityNames.Visitor:
|
||||
@ -226,14 +226,11 @@ namespace Oqtane.Controllers
|
||||
}
|
||||
break;
|
||||
default: // custom entity
|
||||
authorized = true;
|
||||
if (permissionName == PermissionNames.Edit)
|
||||
{
|
||||
authorized = User.IsInRole(RoleNames.Admin) || _userPermissions.IsAuthorized(User, _alias.SiteId, entityName, entityId, permissionName);
|
||||
}
|
||||
else
|
||||
{
|
||||
authorized = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return authorized;
|
||||
|
@ -29,23 +29,25 @@ namespace Oqtane.Controllers
|
||||
private readonly ITenantManager _tenantManager;
|
||||
private readonly INotificationRepository _notifications;
|
||||
private readonly IFolderRepository _folders;
|
||||
private readonly ISyncManager _syncManager;
|
||||
private readonly ISiteRepository _sites;
|
||||
private readonly IUserPermissions _userPermissions;
|
||||
private readonly IJwtManager _jwtManager;
|
||||
private readonly ISyncManager _syncManager;
|
||||
private readonly ILogManager _logger;
|
||||
|
||||
public UserController(IUserRepository users, IUserRoleRepository userRoles, UserManager<IdentityUser> identityUserManager, SignInManager<IdentityUser> identitySignInManager, ITenantManager tenantManager, INotificationRepository notifications, IFolderRepository folders, ISyncManager syncManager, ISiteRepository sites, IJwtManager jwtManager, ILogManager logger)
|
||||
public UserController(IUserRepository users, IUserRoleRepository userRoles, UserManager<IdentityUser> identityUserManager, SignInManager<IdentityUser> identitySignInManager, ITenantManager tenantManager, INotificationRepository notifications, IFolderRepository folders, ISiteRepository sites, IUserPermissions userPermissions, IJwtManager jwtManager, ISyncManager syncManager, ILogManager logger)
|
||||
{
|
||||
_users = users;
|
||||
_userRoles = userRoles;
|
||||
_identityUserManager = identityUserManager;
|
||||
_identitySignInManager = identitySignInManager;
|
||||
_tenantManager = tenantManager;
|
||||
_folders = folders;
|
||||
_notifications = notifications;
|
||||
_syncManager = syncManager;
|
||||
_folders = folders;
|
||||
_sites = sites;
|
||||
_userPermissions = userPermissions;
|
||||
_jwtManager = jwtManager;
|
||||
_syncManager = syncManager;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@ -105,7 +107,7 @@ namespace Oqtane.Controllers
|
||||
user.TwoFactorCode = "";
|
||||
user.TwoFactorExpiry = null;
|
||||
|
||||
if (!User.IsInRole(RoleNames.Admin) && User.Identity.Name?.ToLower() != user.Username.ToLower())
|
||||
if (!_userPermissions.IsAuthorized(User, user.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin) && User.Identity.Name?.ToLower() != user.Username.ToLower())
|
||||
{
|
||||
user.Email = "";
|
||||
user.PhotoFileId = null;
|
||||
@ -148,8 +150,8 @@ namespace Oqtane.Controllers
|
||||
User newUser = null;
|
||||
|
||||
bool verified;
|
||||
bool allowregistration;
|
||||
if (User.IsInRole(RoleNames.Admin))
|
||||
bool allowregistration;
|
||||
if (_userPermissions.IsAuthorized(User, user.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin))
|
||||
{
|
||||
verified = true;
|
||||
allowregistration = true;
|
||||
@ -241,7 +243,8 @@ namespace Oqtane.Controllers
|
||||
[Authorize]
|
||||
public async Task<User> Put(int id, [FromBody] User user)
|
||||
{
|
||||
if (ModelState.IsValid && user.SiteId == _tenantManager.GetAlias().SiteId && _users.GetUser(user.UserId, false) != null && (User.IsInRole(RoleNames.Admin) || User.Identity.Name == user.Username))
|
||||
if (ModelState.IsValid && user.SiteId == _tenantManager.GetAlias().SiteId && _users.GetUser(user.UserId, false) != null
|
||||
&& (_userPermissions.IsAuthorized(User, user.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin) || User.Identity.Name == user.Username))
|
||||
{
|
||||
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
|
||||
if (identityuser != null)
|
||||
@ -287,7 +290,7 @@ namespace Oqtane.Controllers
|
||||
|
||||
// DELETE api/<controller>/5?siteid=x
|
||||
[HttpDelete("{id}")]
|
||||
[Authorize(Roles = RoleNames.Admin)]
|
||||
[Authorize(Policy = $"{EntityNames.User}:{PermissionNames.Write}:{RoleNames.Admin}")]
|
||||
public async Task Delete(int id, string siteid)
|
||||
{
|
||||
int SiteId;
|
||||
|
@ -10,6 +10,7 @@ using System.Linq;
|
||||
using System.Net;
|
||||
using Oqtane.Security;
|
||||
using System;
|
||||
using Oqtane.Modules.Admin.Roles;
|
||||
|
||||
namespace Oqtane.Controllers
|
||||
{
|
||||
@ -93,7 +94,7 @@ namespace Oqtane.Controllers
|
||||
userrole.User.TwoFactorCode = "";
|
||||
userrole.User.TwoFactorExpiry = null;
|
||||
|
||||
if (!User.IsInRole(RoleNames.Admin) && userid != userrole.User.UserId)
|
||||
if (!_userPermissions.IsAuthorized(User, userrole.User.SiteId, EntityNames.User, -1, PermissionNames.Write, RoleNames.Admin) && userid != userrole.User.UserId)
|
||||
{
|
||||
userrole.User.Email = "";
|
||||
userrole.User.PhotoFileId = null;
|
||||
@ -115,7 +116,7 @@ namespace Oqtane.Controllers
|
||||
|
||||
// POST api/<controller>
|
||||
[HttpPost]
|
||||
[Authorize(Roles = RoleNames.Admin)]
|
||||
[Authorize(Policy = $"{EntityNames.UserRole}:{PermissionNames.Write}:{RoleNames.Admin}")]
|
||||
public UserRole Post([FromBody] UserRole userRole)
|
||||
{
|
||||
var role = _roles.GetRole(userRole.RoleId);
|
||||
@ -138,7 +139,7 @@ namespace Oqtane.Controllers
|
||||
|
||||
// PUT api/<controller>/5
|
||||
[HttpPut("{id}")]
|
||||
[Authorize(Roles = RoleNames.Admin)]
|
||||
[Authorize(Policy = $"{EntityNames.UserRole}:{PermissionNames.Write}:{RoleNames.Admin}")]
|
||||
public UserRole Put(int id, [FromBody] UserRole userRole)
|
||||
{
|
||||
var role = _roles.GetRole(userRole.RoleId);
|
||||
@ -160,7 +161,7 @@ namespace Oqtane.Controllers
|
||||
|
||||
// DELETE api/<controller>/5
|
||||
[HttpDelete("{id}")]
|
||||
[Authorize(Roles = RoleNames.Admin)]
|
||||
[Authorize(Policy = $"{EntityNames.UserRole}:{PermissionNames.Write}:{RoleNames.Admin}")]
|
||||
public void Delete(int id)
|
||||
{
|
||||
UserRole userrole = _userRoles.GetUserRole(id);
|
||||
|
Reference in New Issue
Block a user