diff --git a/Oqtane.Client/Modules/Admin/Roles/Users.razor b/Oqtane.Client/Modules/Admin/Roles/Users.razor index c96221e3..359bc113 100644 --- a/Oqtane.Client/Modules/Admin/Roles/Users.razor +++ b/Oqtane.Client/Modules/Admin/Roles/Users.razor @@ -95,11 +95,7 @@ else roleid = Int32.Parse(PageState.QueryString["id"]); Role role = await RoleService.GetRoleAsync(roleid); name = role.Name; - users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId); - users = users - .Where(u => u.Role.Name == RoleNames.Registered) - .OrderBy(u => u.User.DisplayName) - .ToList(); + users = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered); await GetUserRoles(); } catch (Exception ex) @@ -113,8 +109,7 @@ else { try { - userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId); - userroles = userroles.Where(item => item.RoleId == roleid).ToList(); + userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, name); } catch (Exception ex) { diff --git a/Oqtane.Client/Modules/Admin/Users/Index.razor b/Oqtane.Client/Modules/Admin/Users/Index.razor index d2c59065..c39888b1 100644 --- a/Oqtane.Client/Modules/Admin/Users/Index.razor +++ b/Oqtane.Client/Modules/Admin/Users/Index.razor @@ -8,7 +8,7 @@ @inject IStringLocalizer SharedLocalizer @inject SiteState SiteState -@if (userroles == null) +@if (users == null) {

@SharedLocalizer["Loading"] @@ -31,7 +31,7 @@ else - +

    @@ -350,9 +350,9 @@ else } @code { - private List allroles; - private List userroles; - private string _search; + private List allusers; + private List users; + private string _search = ""; private string _allowregistration; private string _allowsitelogin; @@ -399,9 +399,8 @@ else protected override async Task OnInitializedAsync() { - allroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId); - await LoadSettingsAsync(); - userroles = Search(_search); + await LoadUserSettingsAsync(); + await LoadUsersAsync(true); var settings = await SettingService.GetSiteSettingsAsync(PageState.Site.SiteId); _allowregistration = PageState.Site.AllowRegistration.ToString(); @@ -447,27 +446,36 @@ else _lifetime = SettingService.GetSetting(settings, "JwtOptions:Lifetime", "20"); } } - private List Search(string search) + private async Task LoadUsersAsync(bool load) { - var results = allroles.Where(item => item.Role.Name == RoleNames.Registered || (item.Role.Name == RoleNames.Host && UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))); + if (load) + { + allusers = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Registered); + if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host)) + { + var hosts = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, RoleNames.Host); + allusers.AddRange(hosts); + allusers = allusers.OrderBy(u => u.User.DisplayName).ToList(); + } + } + users = allusers; if (!string.IsNullOrEmpty(_search)) { - results = results.Where(item => + users = users.Where(item => ( - item.User.Username.Contains(search, StringComparison.OrdinalIgnoreCase) || - item.User.Email.Contains(search, StringComparison.OrdinalIgnoreCase) || - item.User.DisplayName.Contains(search, StringComparison.OrdinalIgnoreCase) + item.User.Username.Contains(_search, StringComparison.OrdinalIgnoreCase) || + item.User.Email.Contains(_search, StringComparison.OrdinalIgnoreCase) || + item.User.DisplayName.Contains(_search, StringComparison.OrdinalIgnoreCase) ) - ); + ).ToList(); } - return results.ToList(); } private async Task OnSearch() { - userroles = Search(_search); - await UpdateSettingsAsync(); + await UpdateUserSettingsAsync(); + await LoadUsersAsync(false); } private async Task DeleteUser(UserRole UserRole) @@ -479,8 +487,7 @@ else { await UserService.DeleteUserAsync(user.UserId, PageState.Site.SiteId); await logger.LogInformation("User Deleted {User}", UserRole.User); - allroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId); - userroles = Search(_search); + await LoadUsersAsync(true); StateHasChanged(); } } @@ -493,13 +500,13 @@ else private string settingSearch = "AU-search"; - private async Task LoadSettingsAsync() + private async Task LoadUserSettingsAsync() { Dictionary settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId); _search = SettingService.GetSetting(settings, settingSearch, ""); } - private async Task UpdateSettingsAsync() + private async Task UpdateUserSettingsAsync() { Dictionary settings = await SettingService.GetUserSettingsAsync(PageState.User.UserId); SettingService.SetSetting(settings, settingSearch, _search); diff --git a/Oqtane.Client/Modules/Admin/Users/Roles.razor b/Oqtane.Client/Modules/Admin/Users/Roles.razor index 59e4d00c..4b90b902 100644 --- a/Oqtane.Client/Modules/Admin/Users/Roles.razor +++ b/Oqtane.Client/Modules/Admin/Users/Roles.razor @@ -110,8 +110,7 @@ else { try { - userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId); - userroles = userroles.Where(item => item.UserId == userid).ToList(); + userroles = await UserRoleService.GetUserRolesAsync(PageState.Site.SiteId, userid); } catch (Exception ex) { diff --git a/Oqtane.Client/Services/Interfaces/IUserRoleService.cs b/Oqtane.Client/Services/Interfaces/IUserRoleService.cs index b0897554..802a0d0e 100644 --- a/Oqtane.Client/Services/Interfaces/IUserRoleService.cs +++ b/Oqtane.Client/Services/Interfaces/IUserRoleService.cs @@ -16,6 +16,31 @@ namespace Oqtane.Services /// Task> GetUserRolesAsync(int siteId); + /// + /// Get all s on a + /// + /// ID-reference to a + /// ID-reference to a + /// + Task> GetUserRolesAsync(int siteId, int userId); + + /// + /// Get all s on a + /// + /// ID-reference to a + /// Name reference a + /// + Task> GetUserRolesAsync(int siteId, string roleName); + + /// + /// Get all s on a + /// + /// ID-reference to a + /// ID-reference to a + /// Name reference a + /// + Task> GetUserRolesAsync(int siteId, int userId, string roleName); + /// /// Get one specific /// diff --git a/Oqtane.Client/Services/UserRoleService.cs b/Oqtane.Client/Services/UserRoleService.cs index fad8205b..ef596854 100644 --- a/Oqtane.Client/Services/UserRoleService.cs +++ b/Oqtane.Client/Services/UserRoleService.cs @@ -16,7 +16,31 @@ namespace Oqtane.Services public async Task> GetUserRolesAsync(int siteId) { - return await GetJsonAsync>($"{Apiurl}?siteid={siteId}"); + return await GetUserRolesAsync(siteId, -1, ""); + } + + public async Task> GetUserRolesAsync(int siteId, int userId) + { + return await GetUserRolesAsync(siteId, userId, ""); + } + + public async Task> GetUserRolesAsync(int siteId, string roleName) + { + return await GetUserRolesAsync(siteId, -1, roleName); + } + + public async Task> GetUserRolesAsync(int siteId, int userId, string roleName) + { + var url = $"{Apiurl}?siteid={siteId}"; + if (userId != -1) + { + url += $"&userid={userId}"; + } + if (roleName != "") + { + url += $"&rolename={roleName}"; + } + return await GetJsonAsync>(url); } public async Task GetUserRoleAsync(int userRoleId) diff --git a/Oqtane.Client/UI/SiteRouter.razor b/Oqtane.Client/UI/SiteRouter.razor index 10f18942..f66983e0 100644 --- a/Oqtane.Client/UI/SiteRouter.razor +++ b/Oqtane.Client/UI/SiteRouter.razor @@ -80,7 +80,7 @@ var runtime = (Shared.Runtime)Enum.Parse(typeof(Shared.Runtime), Runtime); Route route = new Route(_absoluteUri, SiteState.Alias.Path); - var moduleid = (int.TryParse(route.ModuleId, out int mid)) ? mid : -1; + int moduleid = (int.TryParse(route.ModuleId, out moduleid)) ? moduleid : -1; var action = (!string.IsNullOrEmpty(route.Action)) ? route.Action : Constants.DefaultAction; var querystring = ParseQueryString(route.Query); diff --git a/Oqtane.Server/Controllers/UserController.cs b/Oqtane.Server/Controllers/UserController.cs index 37f837f3..d73ea1e6 100644 --- a/Oqtane.Server/Controllers/UserController.cs +++ b/Oqtane.Server/Controllers/UserController.cs @@ -109,7 +109,6 @@ namespace Oqtane.Controllers if (!User.IsInRole(RoleNames.Admin) && User.Identity.Name?.ToLower() != user.Username.ToLower()) { - user.DisplayName = ""; user.Email = ""; user.PhotoFileId = null; user.LastLoginOn = DateTime.MinValue; diff --git a/Oqtane.Server/Controllers/UserRoleController.cs b/Oqtane.Server/Controllers/UserRoleController.cs index 0250fd7c..1d179a3d 100644 --- a/Oqtane.Server/Controllers/UserRoleController.cs +++ b/Oqtane.Server/Controllers/UserRoleController.cs @@ -8,6 +8,8 @@ using Oqtane.Infrastructure; using Oqtane.Repository; using System.Linq; using System.Net; +using Oqtane.Security; +using System; namespace Oqtane.Controllers { @@ -16,32 +18,57 @@ namespace Oqtane.Controllers { private readonly IUserRoleRepository _userRoles; private readonly IRoleRepository _roles; + private readonly IUserPermissions _userPermissions; private readonly ISyncManager _syncManager; private readonly ILogManager _logger; private readonly Alias _alias; - public UserRoleController(IUserRoleRepository userRoles, IRoleRepository roles, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger) + public UserRoleController(IUserRoleRepository userRoles, IRoleRepository roles, IUserPermissions userPermissions, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger) { _userRoles = userRoles; _roles = roles; + _userPermissions = userPermissions; _syncManager = syncManager; _logger = logger; _alias = tenantManager.GetAlias(); } - // GET: api/?siteid=x + // GET: api/?siteid=x&userid=y&rolename=z [HttpGet] - [Authorize(Roles = RoleNames.Admin)] - public IEnumerable Get(string siteid) + [Authorize(Roles = RoleNames.Registered)] + public IEnumerable Get(string siteid, string userid = null, string rolename = null) { int SiteId; if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId) { - return _userRoles.GetUserRoles(SiteId); + int UserId = (int.TryParse(userid, out UserId)) ? UserId : -1; + if (User.IsInRole(RoleNames.Admin) || ((userid == null || _userPermissions.GetUser().UserId == UserId) && (rolename == null || (User.IsInRole(rolename) && rolename != RoleNames.Registered)))) + { + var userroles = _userRoles.GetUserRoles(SiteId).ToList(); + if (userid != null) + { + userroles = userroles.Where(item => item.UserId == UserId).ToList(); + } + if (rolename != null) + { + userroles = userroles.Where(item => item.Role.Name == rolename).ToList(); + } + for (int i = 0; i < userroles.Count(); i++) + { + userroles[i] = Filter(userroles[i]); + } + return userroles.OrderBy(u => u.User.DisplayName); + } + else + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized UserRole Get Attempt For Site {SiteId} User {UserId} Role {RoleName}", siteid, userid, rolename); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + return null; + } } else { - _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized UserRole Get Attempt {SiteId}", siteid); + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized UserRole Get Attempt For Site {SiteId} User {UserId} Role {RoleName}", siteid, userid, rolename); HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; return null; } @@ -49,13 +76,22 @@ namespace Oqtane.Controllers // GET api//5 [HttpGet("{id}")] - [Authorize(Roles = RoleNames.Admin)] + [Authorize(Roles = RoleNames.Registered)] public UserRole Get(int id) { var userrole = _userRoles.GetUserRole(id); if (userrole != null && SiteValid(userrole.Role.SiteId)) { - return userrole; + if (User.IsInRole(RoleNames.Admin) || User.Identity.Name?.ToLower() != userrole.User.Username.ToLower() || User.IsInRole(userrole.Role.Name)) + { + return Filter(userrole); + } + else + { + _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Role Get Attempt {UserRoleId}", id); + HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; + return null; + } } else { @@ -65,6 +101,35 @@ namespace Oqtane.Controllers } } + private UserRole Filter(UserRole userrole) + { + if (userrole != null) + { + userrole.User.Password = ""; + userrole.User.IsAuthenticated = false; + userrole.User.TwoFactorCode = ""; + userrole.User.TwoFactorExpiry = null; + + if (!User.IsInRole(RoleNames.Admin) && User.Identity.Name?.ToLower() != userrole.User.Username.ToLower()) + { + userrole.User.Email = ""; + userrole.User.PhotoFileId = null; + userrole.User.LastLoginOn = DateTime.MinValue; + userrole.User.LastIPAddress = ""; + userrole.User.Roles = ""; + userrole.User.CreatedBy = ""; + userrole.User.CreatedOn = DateTime.MinValue; + userrole.User.ModifiedBy = ""; + userrole.User.ModifiedOn = DateTime.MinValue; + userrole.User.DeletedBy = ""; + userrole.User.DeletedOn = DateTime.MinValue; + userrole.User.IsDeleted = false; + userrole.User.TwoFactorRequired = false; + } + } + return userrole; + } + // POST api/ [HttpPost] [Authorize(Roles = RoleNames.Admin)] diff --git a/Oqtane.Server/Infrastructure/DatabaseManager.cs b/Oqtane.Server/Infrastructure/DatabaseManager.cs index 40b56608..e3da4de5 100644 --- a/Oqtane.Server/Infrastructure/DatabaseManager.cs +++ b/Oqtane.Server/Infrastructure/DatabaseManager.cs @@ -267,13 +267,13 @@ namespace Oqtane.Infrastructure var databaseType = install.DatabaseType; - //Get database Type + // get database type var type = Type.GetType(databaseType); - //Create database object from Type + // create database object from type var database = Activator.CreateInstance(type) as IDatabase; - //create data directory if does not exist + // create data directory if does not exist var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString(); if (!Directory.Exists(dataDirectory)) Directory.CreateDirectory(dataDirectory ?? String.Empty); @@ -287,7 +287,7 @@ namespace Oqtane.Infrastructure } catch (Exception ex) { - result.Message = ex.Message; + result.Message = "An Error Occurred Creating The Database. This Is Usually Related To Your User Not Having Sufficient Rights To Perform This Operation. Please Note That You Can Also Create The Database Manually Prior To Initiating The Install Wizard. " + ex.Message; _filelogger.LogError(Utilities.LogMessage(this, result.Message)); } } @@ -321,14 +321,14 @@ namespace Oqtane.Infrastructure { UpgradeSqlServer(sql, install.ConnectionString, install.DatabaseType, true); } - // Push latest model into database + // push latest model into database masterDbContext.Database.Migrate(); result.Success = true; } } catch (Exception ex) { - result.Message = ex.Message; + result.Message = "An Error Occurred Provisioning The Master Database. This Is Usually Related To The Master Database Not Being In A Supported State. " + ex.Message; _filelogger.LogError(Utilities.LogMessage(this, result.Message)); } } @@ -429,14 +429,14 @@ namespace Oqtane.Infrastructure UpgradeSqlServer(sql, tenant.DBConnectionString, tenant.DBType, false); } - // Push latest model into database + // push latest model into database tenantDbContext.Database.Migrate(); result.Success = true; } } catch (Exception ex) { - result.Message = ex.Message; + result.Message = "An Error Occurred Migrating A Tenant Database. This Is Usually Related To A Tenant Database Not Being In A Supported State. " + ex.Message; _filelogger.LogError(Utilities.LogMessage(this, result.Message)); } @@ -445,13 +445,21 @@ namespace Oqtane.Infrastructure var index = Array.FindIndex(versions, item => item == version); if (index != (versions.Length - 1)) { - for (var i = (index + 1); i < versions.Length; i++) + try { - upgrades.Upgrade(tenant, versions[i]); + for (var i = (index + 1); i < versions.Length; i++) + { + upgrades.Upgrade(tenant, versions[i]); + } + tenant.Version = versions[versions.Length - 1]; + db.Entry(tenant).State = EntityState.Modified; + db.SaveChanges(); + } + catch (Exception ex) + { + result.Message = "An Error Occurred Executing Upgrade Logic. " + ex.Message; + _filelogger.LogError(Utilities.LogMessage(this, result.Message)); } - tenant.Version = versions[versions.Length - 1]; - db.Entry(tenant).State = EntityState.Modified; - db.SaveChanges(); } } } @@ -653,7 +661,7 @@ namespace Oqtane.Infrastructure } catch (Exception ex) { - result.Message = "An Error Occurred Creating Site - " + ex.Message; + result.Message = "An Error Occurred Creating Site. " + ex.Message; } } diff --git a/Oqtane.Server/Startup.cs b/Oqtane.Server/Startup.cs index 54b16ebd..6eeafb3e 100644 --- a/Oqtane.Server/Startup.cs +++ b/Oqtane.Server/Startup.cs @@ -164,7 +164,7 @@ namespace Oqtane // execute any IServerStartup logic app.ConfigureOqtaneAssemblies(env); - // Allow oqtane localization middleware + // allow oqtane localization middleware app.UseOqtaneLocalization(); app.UseHttpsRedirection();