From 19afc5d055354d26ec396f9fc88b04b968d359c7 Mon Sep 17 00:00:00 2001 From: Masu-Baumgartner <68913099+Masu-Baumgartner@users.noreply.github.com> Date: Mon, 7 Oct 2024 16:37:18 +0200 Subject: [PATCH] Added permissions to users controller and the client. --- .../Admin/Users/UsersController.cs | 6 +++ .../Middleware/AuthorizationMiddleware.cs | 2 + .../Moonlight.ApiServer.csproj | 3 +- .../DefaultSidebarItemProvider.cs | 12 ++++-- Moonlight.Client/Models/SidebarItem.cs | 1 + Moonlight.Client/Moonlight.Client.csproj | 1 - Moonlight.Client/Services/IdentityService.cs | 40 ++++++++++++++++++ Moonlight.Client/UI/App.razor | 4 +- Moonlight.Client/UI/Layouts/MainLayout.razor | 42 ++++++++++++++----- Moonlight.Client/UI/Partials/AppSidebar.razor | 6 ++- .../UI/Views/Admin/Users/Index.razor | 3 ++ 11 files changed, 101 insertions(+), 19 deletions(-) diff --git a/Moonlight.ApiServer/Http/Controllers/Admin/Users/UsersController.cs b/Moonlight.ApiServer/Http/Controllers/Admin/Users/UsersController.cs index 2e72b3af..507241d0 100644 --- a/Moonlight.ApiServer/Http/Controllers/Admin/Users/UsersController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Admin/Users/UsersController.cs @@ -3,6 +3,7 @@ using MoonCore.Exceptions; using MoonCore.Extended.Abstractions; using MoonCore.Extended.Helpers; using MoonCore.Models; +using Moonlight.ApiServer.Attributes; using Moonlight.ApiServer.Database.Entities; using Moonlight.Shared.Http.Requests.Admin.Users; using Moonlight.Shared.Http.Responses.Admin.Users; @@ -23,14 +24,17 @@ public class UsersController : Controller } [HttpGet] + [RequirePermission("admin.users.read")] public async Task> Get([FromQuery] int page, [FromQuery] int pageSize = 50) => await CrudHelper.Get(page, pageSize); [HttpGet("{id}")] + [RequirePermission("admin.users.read")] public async Task GetSingle(int id) => await CrudHelper.GetSingle(id); [HttpPost] + [RequirePermission("admin.users.create")] public async Task Create([FromBody] CreateUserRequest request) { // Reformat values @@ -50,6 +54,7 @@ public class UsersController : Controller } [HttpPatch("{id}")] + [RequirePermission("admin.users.update")] public async Task Update([FromRoute] int id, [FromBody] UpdateUserRequest request) { var user = await CrudHelper.GetSingleModel(id); @@ -76,6 +81,7 @@ public class UsersController : Controller } [HttpDelete("{id}")] + [RequirePermission("admin.users.delete")] public async Task Delete([FromRoute] int id) => await CrudHelper.Delete(id); } \ No newline at end of file diff --git a/Moonlight.ApiServer/Http/Middleware/AuthorizationMiddleware.cs b/Moonlight.ApiServer/Http/Middleware/AuthorizationMiddleware.cs index 8ca662f7..4159bdcb 100644 --- a/Moonlight.ApiServer/Http/Middleware/AuthorizationMiddleware.cs +++ b/Moonlight.ApiServer/Http/Middleware/AuthorizationMiddleware.cs @@ -86,6 +86,8 @@ public class AuthorizationMiddleware detail: permission, statusCode: 403 ).ExecuteAsync(context); + + return false; } } diff --git a/Moonlight.ApiServer/Moonlight.ApiServer.csproj b/Moonlight.ApiServer/Moonlight.ApiServer.csproj index 58691412..250d4104 100644 --- a/Moonlight.ApiServer/Moonlight.ApiServer.csproj +++ b/Moonlight.ApiServer/Moonlight.ApiServer.csproj @@ -13,11 +13,12 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + + diff --git a/Moonlight.Client/Implementations/DefaultSidebarItemProvider.cs b/Moonlight.Client/Implementations/DefaultSidebarItemProvider.cs index 738c6df8..f199639f 100644 --- a/Moonlight.Client/Implementations/DefaultSidebarItemProvider.cs +++ b/Moonlight.Client/Implementations/DefaultSidebarItemProvider.cs @@ -27,7 +27,8 @@ public class DefaultSidebarItemProvider : ISidebarItemProvider Group = "Admin", Path = "/admin", Priority = 0, - RequiresExactMatch = true + RequiresExactMatch = true, + Permission = "admin.overview" }, new SidebarItem() { @@ -36,7 +37,8 @@ public class DefaultSidebarItemProvider : ISidebarItemProvider Group = "Admin", Path = "/admin/users", Priority = 1, - RequiresExactMatch = false + RequiresExactMatch = false, + Permission = "admin.users.read" }, new SidebarItem() { @@ -45,7 +47,8 @@ public class DefaultSidebarItemProvider : ISidebarItemProvider Group = "Admin", Path = "/admin/api", Priority = 2, - RequiresExactMatch = false + RequiresExactMatch = false, + Permission = "admin.api.read" }, new SidebarItem() { @@ -54,7 +57,8 @@ public class DefaultSidebarItemProvider : ISidebarItemProvider Group = "Admin", Path = "/admin/system", Priority = 3, - RequiresExactMatch = false + RequiresExactMatch = false, + Permission = "admin.system.info" }, ]; } diff --git a/Moonlight.Client/Models/SidebarItem.cs b/Moonlight.Client/Models/SidebarItem.cs index b56d991a..c3f7f926 100644 --- a/Moonlight.Client/Models/SidebarItem.cs +++ b/Moonlight.Client/Models/SidebarItem.cs @@ -8,4 +8,5 @@ public class SidebarItem public string Path { get; set; } public int Priority { get; set; } public bool RequiresExactMatch { get; set; } = false; + public string? Permission { get; set; } } \ No newline at end of file diff --git a/Moonlight.Client/Moonlight.Client.csproj b/Moonlight.Client/Moonlight.Client.csproj index dd97f0d9..bc029be0 100644 --- a/Moonlight.Client/Moonlight.Client.csproj +++ b/Moonlight.Client/Moonlight.Client.csproj @@ -13,7 +13,6 @@ - diff --git a/Moonlight.Client/Services/IdentityService.cs b/Moonlight.Client/Services/IdentityService.cs index a5491d8c..fef45c27 100644 --- a/Moonlight.Client/Services/IdentityService.cs +++ b/Moonlight.Client/Services/IdentityService.cs @@ -25,6 +25,8 @@ public class IdentityService ApiClient = apiClient; } + #region Login / Logout + public async Task Check() { try @@ -56,4 +58,42 @@ public class IdentityService await CookieService.SetValue("token", "", 30); await Check(); } + + #endregion + + public bool HasPermission(string requiredPermission) + { + // Check for wildcard permission + if (Permissions.Contains("*")) + return true; + + var requiredSegments = requiredPermission.Split('.'); + + // Check if the user has the exact permission or a wildcard match + foreach (var permission in Permissions) + { + var permissionSegments = permission.Split('.'); + + // Iterate over the segments of the required permission + for (var i = 0; i < requiredSegments.Length; i++) + { + // If the current segment matches or is a wildcard, continue to the next segment + if (i < permissionSegments.Length && requiredSegments[i] == permissionSegments[i] || + permissionSegments[i] == "*") + { + // If we've reached the end of the permissionSegments array, it means we've found a match + if (i == permissionSegments.Length - 1) + return true; // Found an exact match or a wildcard match + } + else + { + // If we reach here, it means the segments don't match and we break out of the loop + break; + } + } + } + + // No matching permission found + return false; + } } \ No newline at end of file diff --git a/Moonlight.Client/UI/App.razor b/Moonlight.Client/UI/App.razor index db02c5e5..8b6ed3d4 100644 --- a/Moonlight.Client/UI/App.razor +++ b/Moonlight.Client/UI/App.razor @@ -2,7 +2,9 @@ - + + + Not found diff --git a/Moonlight.Client/UI/Layouts/MainLayout.razor b/Moonlight.Client/UI/Layouts/MainLayout.razor index 63224017..423dbcbc 100644 --- a/Moonlight.Client/UI/Layouts/MainLayout.razor +++ b/Moonlight.Client/UI/Layouts/MainLayout.razor @@ -1,11 +1,14 @@ -@using MoonCore.Helpers +@using MoonCore.Exceptions +@using MoonCore.Helpers @using MoonCore.PluginFramework.Services @using Moonlight.Client.Interfaces +@using Moonlight.Client.Services @using Moonlight.Client.UI.Partials @inherits LayoutComponentBase @inject ImplementationService ImplementationService +@inject IdentityService IdentityService @inject IServiceProvider ServiceProvider @inject ILogger Logger @@ -29,15 +32,21 @@ else
- -
-
- - @Body - -
-
-
+
+
+ + + + + @Body + + + + +
+
+ +
} @@ -127,4 +136,17 @@ else await InvokeAsync(StateHasChanged); } + + private bool CheckPermission(string permission) => IdentityService.HasPermission(permission); + + private Task HandleException(Exception exception, ErrorHandler handler) + { + if (exception is HttpApiException httpApiException && httpApiException.Status == 401) + { + Task.Run(Load); + return Task.FromResult(true); + } + + return Task.FromResult(false); + } } \ No newline at end of file diff --git a/Moonlight.Client/UI/Partials/AppSidebar.razor b/Moonlight.Client/UI/Partials/AppSidebar.razor index 3691bf03..a9ad45e5 100644 --- a/Moonlight.Client/UI/Partials/AppSidebar.razor +++ b/Moonlight.Client/UI/Partials/AppSidebar.razor @@ -1,9 +1,11 @@ @using MoonCore.PluginFramework.Services @using Moonlight.Client.Interfaces @using Moonlight.Client.Models +@using Moonlight.Client.Services @using Moonlight.Client.UI.Layouts @inject NavigationManager Navigation +@inject IdentityService IdentityService @inject ImplementationService ImplementationService @{ @@ -111,7 +113,7 @@
  • @if (!string.IsNullOrEmpty(group.Key)) { -
    +
    @group.Key
    } @@ -168,7 +170,7 @@ { Items = ImplementationService.Get() .SelectMany(x => x.Get()) - //.Where(x => x.Permission == null || (x.Permission != null && IdentityService.HasPermission(x.Permission))) + .Where(x => x.Permission == null || (x.Permission != null && IdentityService.HasPermission(x.Permission))) .GroupBy(x => x.Group ?? "") .OrderByDescending(x => string.IsNullOrEmpty(x.Key)) .ToDictionary(x => x.Key, x => x.OrderBy(y => y.Priority).ToArray()); diff --git a/Moonlight.Client/UI/Views/Admin/Users/Index.razor b/Moonlight.Client/UI/Views/Admin/Users/Index.razor index 401b8294..2141dc0c 100644 --- a/Moonlight.Client/UI/Views/Admin/Users/Index.razor +++ b/Moonlight.Client/UI/Views/Admin/Users/Index.razor @@ -1,11 +1,14 @@ @page "/admin/users" +@using MoonCore.Blazor.Tailwind.Attributes @using MoonCore.Blazor.Tailwind.Forms.Components @using MoonCore.Helpers @using MoonCore.Models @using Moonlight.Shared.Http.Requests.Admin.Users @using Moonlight.Shared.Http.Responses.Admin.Users +@attribute [RequirePermission("admin.users.read")] + @inject HttpApiClient HttpApiClient