From 3e06f0203fa9ec519ceeab0372e44792feebb7eb Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 22 Jun 2023 01:30:03 +0200 Subject: [PATCH] Add basic role and route for server hub admins. --- SS14.Auth.Shared/AuthConstants.cs | 14 ++++++ SS14.Auth.Shared/Data/SpaceUser.cs | 5 ++ SS14.Auth.Shared/Data/SpaceUserManager.cs | 24 ++++++--- SS14.Auth/Services/EnsureRolesService.cs | 7 +-- SS14.Web/Areas/Admin/Pages/Index.cshtml | 16 +++++- .../Areas/Admin/Pages/Servers/Index.cshtml | 14 ++++++ .../Areas/Admin/Pages/Servers/Index.cshtml.cs | 11 +++++ .../Areas/Admin/Pages/Users/ViewUser.cshtml | 10 ++++ .../Admin/Pages/Users/ViewUser.cshtml.cs | 49 ++++++++++++------- SS14.Web/Startup.cs | 21 ++++++-- SS14.Web/Views/Shared/_Layout.cshtml | 4 +- 11 files changed, 141 insertions(+), 34 deletions(-) create mode 100644 SS14.Web/Areas/Admin/Pages/Servers/Index.cshtml create mode 100644 SS14.Web/Areas/Admin/Pages/Servers/Index.cshtml.cs diff --git a/SS14.Auth.Shared/AuthConstants.cs b/SS14.Auth.Shared/AuthConstants.cs index 45254ad..2a20dd2 100644 --- a/SS14.Auth.Shared/AuthConstants.cs +++ b/SS14.Auth.Shared/AuthConstants.cs @@ -2,6 +2,20 @@ public static class AuthConstants { + /// + /// User has any admin role. + /// + public const string PolicyAnyHubAdmin = "AnyHubAdmin"; + + /// + /// User has the ability to mess with the accounts list and OAuth clients. + /// public const string PolicySysAdmin = "SysAdmin"; public const string RoleSysAdmin = "SysAdmin"; + + /// + /// User has the ability to mess with the game server hub. + /// + public const string PolicyServerHubAdmin = "ServerHubAdmin"; + public const string RoleServerHubAdmin = "ServerHubAdmin"; } \ No newline at end of file diff --git a/SS14.Auth.Shared/Data/SpaceUser.cs b/SS14.Auth.Shared/Data/SpaceUser.cs index 4542757..951e6d4 100644 --- a/SS14.Auth.Shared/Data/SpaceUser.cs +++ b/SS14.Auth.Shared/Data/SpaceUser.cs @@ -99,6 +99,9 @@ public sealed record AccountLogRecoveryCodesGenerated(Guid Actor) : AccountLogEn public sealed record AccountLogAdminNotesChanged(string NewNotes, Guid Actor) : AccountLogEntry; public sealed record AccountLogAdminLockedChanged(bool NewLocked, Guid Actor) : AccountLogEntry; +public sealed record AccountLogAuthRoleAdded(Guid Role, Guid Actor) : AccountLogEntry; +public sealed record AccountLogAuthRoleRemoved(Guid Role, Guid Actor) : AccountLogEntry; + public enum AccountLogType { Created = 0, @@ -115,4 +118,6 @@ public enum AccountLogType RecoveryCodesGenerated = 11, AdminNotesChanged = 12, AdminLockedChanged = 13, + AuthRoleAdded = 14, + AuthRoleRemoved = 15 } \ No newline at end of file diff --git a/SS14.Auth.Shared/Data/SpaceUserManager.cs b/SS14.Auth.Shared/Data/SpaceUserManager.cs index 157679b..281b8f4 100644 --- a/SS14.Auth.Shared/Data/SpaceUserManager.cs +++ b/SS14.Auth.Shared/Data/SpaceUserManager.cs @@ -91,14 +91,6 @@ public void LogPasswordChanged(SpaceUser user, SpaceUser actor) new AccountLogPasswordChanged(actor.Id)); } - public void LogHubAdminChanged(SpaceUser user, bool newHubAdmin, SpaceUser actor) - { - AccountLog( - user, - AccountLogType.HubAdminChanged, - new AccountLogHubAdminChanged(newHubAdmin, actor.Id)); - } - public void LogEmailConfirmedChanged(SpaceUser user, bool newEmailConfirmed, SpaceUser actor) { AccountLog( @@ -139,6 +131,22 @@ public void LogAdminLockedChanged(SpaceUser user, bool newLocked, SpaceUser acto new AccountLogAdminLockedChanged(newLocked, actor.Id)); } + public void LogAuthRoleAdded(SpaceUser user, Guid role, SpaceUser actor) + { + AccountLog( + user, + AccountLogType.AuthRoleAdded, + new AccountLogAuthRoleAdded(role, actor.Id)); + } + + public void LogAuthRoleRemoved(SpaceUser user, Guid role, SpaceUser actor) + { + AccountLog( + user, + AccountLogType.AuthRoleRemoved, + new AccountLogAuthRoleRemoved(role, actor.Id)); + } + public void AccountLog(SpaceUser user, AccountLogType type, AccountLogEntry entry) { _dbContext.AccountLogs.Add(new AccountLog diff --git a/SS14.Auth/Services/EnsureRolesService.cs b/SS14.Auth/Services/EnsureRolesService.cs index cb1348c..0431f81 100644 --- a/SS14.Auth/Services/EnsureRolesService.cs +++ b/SS14.Auth/Services/EnsureRolesService.cs @@ -17,7 +17,8 @@ namespace SS14.Auth.Services; public sealed class EnsureRolesService : IHostedService { private static readonly string[] RolesToEnsure = { - AuthConstants.RoleSysAdmin + AuthConstants.RoleSysAdmin, + AuthConstants.RoleServerHubAdmin }; private readonly IServiceProvider _serviceProvider; @@ -40,14 +41,14 @@ public async Task StartAsync(CancellationToken cancellationToken) foreach (var roleName in RolesToEnsure) { - if (await roleManager.FindByNameAsync(AuthConstants.RoleSysAdmin) != null) + if (await roleManager.FindByNameAsync(roleName) != null) continue; _logger.LogInformation("Creating role {Role} because it does not exist in the database yet", roleName); await roleManager.CreateAsync(new SpaceRole { - Name = AuthConstants.RoleSysAdmin + Name = roleName }); } diff --git a/SS14.Web/Areas/Admin/Pages/Index.cshtml b/SS14.Web/Areas/Admin/Pages/Index.cshtml index 15bad14..79dc445 100644 --- a/SS14.Web/Areas/Admin/Pages/Index.cshtml +++ b/SS14.Web/Areas/Admin/Pages/Index.cshtml @@ -1,5 +1,9 @@ @page +@using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Mvc.TagHelpers +@using SS14.Auth.Shared @model SS14.Web.Areas.Admin.Pages.Index +@inject IAuthorizationService AuthorizationService @{ ViewData["Title"] = "Hub Admin"; @@ -11,5 +15,13 @@ -Users -OAuth Clients \ No newline at end of file +@if ((await AuthorizationService.AuthorizeAsync(User, AuthConstants.PolicySysAdmin)).Succeeded) +{ + Users + OAuth Clients +} + +@if ((await AuthorizationService.AuthorizeAsync(User, AuthConstants.PolicyServerHubAdmin)).Succeeded) +{ + Servers +} \ No newline at end of file diff --git a/SS14.Web/Areas/Admin/Pages/Servers/Index.cshtml b/SS14.Web/Areas/Admin/Pages/Servers/Index.cshtml new file mode 100644 index 0000000..7604719 --- /dev/null +++ b/SS14.Web/Areas/Admin/Pages/Servers/Index.cshtml @@ -0,0 +1,14 @@ +@page +@model SS14.Web.Areas.Admin.Pages.Servers.Index + +@{ + ViewData["Title"] = "Servers home"; +} + + + diff --git a/SS14.Web/Areas/Admin/Pages/Servers/Index.cshtml.cs b/SS14.Web/Areas/Admin/Pages/Servers/Index.cshtml.cs new file mode 100644 index 0000000..018739e --- /dev/null +++ b/SS14.Web/Areas/Admin/Pages/Servers/Index.cshtml.cs @@ -0,0 +1,11 @@ +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace SS14.Web.Areas.Admin.Pages.Servers; + +public class Index : PageModel +{ + public void OnGet() + { + + } +} \ No newline at end of file diff --git a/SS14.Web/Areas/Admin/Pages/Users/ViewUser.cshtml b/SS14.Web/Areas/Admin/Pages/Users/ViewUser.cshtml index 1421df0..771f39b 100644 --- a/SS14.Web/Areas/Admin/Pages/Users/ViewUser.cshtml +++ b/SS14.Web/Areas/Admin/Pages/Users/ViewUser.cshtml @@ -60,6 +60,16 @@ +
+ Permissions +
+
+ + +
+
+
+
diff --git a/SS14.Web/Areas/Admin/Pages/Users/ViewUser.cshtml.cs b/SS14.Web/Areas/Admin/Pages/Users/ViewUser.cshtml.cs index d6c1174..cb00de4 100644 --- a/SS14.Web/Areas/Admin/Pages/Users/ViewUser.cshtml.cs +++ b/SS14.Web/Areas/Admin/Pages/Users/ViewUser.cshtml.cs @@ -2,6 +2,7 @@ using System.ComponentModel.DataAnnotations; using System.Text; using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.WebUtilities; @@ -19,6 +20,7 @@ public class ViewUser : PageModel private readonly SessionManager _sessionManager; private readonly PatreonDataManager _patreonDataManager; private readonly ApplicationDbContext _dbContext; + private readonly RoleManager _roleManager; public SpaceUser SpaceUser { get; set; } @@ -38,7 +40,8 @@ public class InputModel [Display(Name = "Email Confirmed?")] public bool EmailConfirmed { get; set; } - [Display(Name = "Is Hub Admin?")] public bool HubAdmin { get; set; } + [Display(Name = "Is Auth Hub Admin?")] public bool HubAdmin { get; set; } + [Display(Name = "Is Server Hub Admin?")] public bool ServerHubAdmin { get; set; } [Display(Name = "2FA enabled?")] public bool TfaEnabled { get; set; } @@ -54,13 +57,15 @@ public ViewUser( IEmailSender emailSender, SessionManager sessionManager, PatreonDataManager patreonDataManager, - ApplicationDbContext dbContext) + ApplicationDbContext dbContext, + RoleManager roleManager) { _userManager = userManager; _emailSender = emailSender; _sessionManager = sessionManager; _patreonDataManager = patreonDataManager; _dbContext = dbContext; + _roleManager = roleManager; } public async Task OnGetAsync(Guid id) @@ -128,21 +133,8 @@ public async Task OnPostSaveAsync(Guid id) SpaceUser.AdminLocked = Input.AdminLocked; } - if (Input.HubAdmin != await _userManager.IsInRoleAsync(SpaceUser, AuthConstants.RoleSysAdmin)) - { - _userManager.LogHubAdminChanged(SpaceUser, Input.HubAdmin, actor); - - if (Input.HubAdmin) - { - await _userManager.AddToRoleAsync(SpaceUser, AuthConstants.RoleSysAdmin); - } - else - { - await _userManager.RemoveFromRoleAsync(SpaceUser, AuthConstants.RoleSysAdmin); - } - - await _userManager.UpdateSecurityStampAsync(SpaceUser); - } + await CheckRole(Input.HubAdmin, AuthConstants.RoleSysAdmin); + await CheckRole(Input.ServerHubAdmin, AuthConstants.RoleServerHubAdmin); await _userManager.UpdateAsync(SpaceUser); @@ -151,6 +143,28 @@ public async Task OnPostSaveAsync(Guid id) StatusMessage = "Changes saved"; return RedirectToPage(new {id}); + + async Task CheckRole(bool set, string roleName) + { + if (set != await _userManager.IsInRoleAsync(SpaceUser, roleName)) + { + var role = await _roleManager.FindByNameAsync(roleName); + var roleGuid = Guid.Parse(await _roleManager.GetRoleIdAsync(role)); + + if (set) + { + await _userManager.AddToRoleAsync(SpaceUser, roleName); + _userManager.LogAuthRoleAdded(SpaceUser, roleGuid, actor); + } + else + { + await _userManager.RemoveFromRoleAsync(SpaceUser, roleName); + _userManager.LogAuthRoleRemoved(SpaceUser, roleGuid, actor); + } + + await _userManager.UpdateSecurityStampAsync(SpaceUser); + } + } } public async Task OnPostResendConfirmationAsync(Guid id) @@ -209,6 +223,7 @@ private async Task LoadAsync() EmailConfirmed = SpaceUser.EmailConfirmed, Username = SpaceUser.UserName, HubAdmin = await _userManager.IsInRoleAsync(SpaceUser, AuthConstants.RoleSysAdmin), + ServerHubAdmin = await _userManager.IsInRoleAsync(SpaceUser, AuthConstants.RoleServerHubAdmin), TfaEnabled = SpaceUser.TwoFactorEnabled, AdminLocked = SpaceUser.AdminLocked, AdminNotes = SpaceUser.AdminNotes diff --git a/SS14.Web/Startup.cs b/SS14.Web/Startup.cs index 990e28f..f4aaaa4 100644 --- a/SS14.Web/Startup.cs +++ b/SS14.Web/Startup.cs @@ -42,8 +42,20 @@ public void ConfigureServices(IServiceCollection services) services.AddAuthorization(options => { - options.AddPolicy(AuthConstants.PolicySysAdmin, - policy => policy.RequireRole(AuthConstants.RoleSysAdmin)); + options.AddPolicy( + AuthConstants.PolicyAnyHubAdmin, + policy => policy.RequireRole(AuthConstants.RoleSysAdmin, AuthConstants.RoleServerHubAdmin) + ); + + options.AddPolicy( + AuthConstants.PolicySysAdmin, + policy => policy.RequireRole(AuthConstants.RoleSysAdmin) + ); + + options.AddPolicy( + AuthConstants.PolicyServerHubAdmin, + policy => policy.RequireRole(AuthConstants.RoleServerHubAdmin) + ); }); services.AddMvc() @@ -51,7 +63,10 @@ public void ConfigureServices(IServiceCollection services) { options.Conventions.AuthorizeAreaFolder("Identity", "/Account/Manage"); options.Conventions.AuthorizeAreaPage("Identity", "/Account/Logout"); - options.Conventions.AuthorizeAreaFolder("Admin", "/", AuthConstants.PolicySysAdmin); + options.Conventions.AuthorizeAreaFolder("Admin", "/", AuthConstants.PolicyAnyHubAdmin); + options.Conventions.AuthorizeAreaFolder("Admin", "/Clients", AuthConstants.PolicySysAdmin); + options.Conventions.AuthorizeAreaFolder("Admin", "/Users", AuthConstants.PolicySysAdmin); + options.Conventions.AuthorizeAreaFolder("Admin", "/Servers", AuthConstants.PolicyServerHubAdmin); }); services.ConfigureApplicationCookie(options => diff --git a/SS14.Web/Views/Shared/_Layout.cshtml b/SS14.Web/Views/Shared/_Layout.cshtml index 2a464f8..df6bcf6 100644 --- a/SS14.Web/Views/Shared/_Layout.cshtml +++ b/SS14.Web/Views/Shared/_Layout.cshtml @@ -1,4 +1,6 @@ @using SS14.Auth.Shared +@using Microsoft.AspNetCore.Authorization +@inject IAuthorizationService AuthorizationService @@ -25,7 +27,7 @@ - @if (User.IsInRole(AuthConstants.RoleSysAdmin)) + @if ((await AuthorizationService.AuthorizeAsync(User, AuthConstants.PolicyAnyHubAdmin)).Succeeded) {