Added permissions to users controller and the client.
This commit is contained in:
@@ -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<IPagedData<UserDetailResponse>> Get([FromQuery] int page, [FromQuery] int pageSize = 50)
|
||||
=> await CrudHelper.Get(page, pageSize);
|
||||
|
||||
[HttpGet("{id}")]
|
||||
[RequirePermission("admin.users.read")]
|
||||
public async Task<UserDetailResponse> GetSingle(int id)
|
||||
=> await CrudHelper.GetSingle(id);
|
||||
|
||||
[HttpPost]
|
||||
[RequirePermission("admin.users.create")]
|
||||
public async Task<UserDetailResponse> Create([FromBody] CreateUserRequest request)
|
||||
{
|
||||
// Reformat values
|
||||
@@ -50,6 +54,7 @@ public class UsersController : Controller
|
||||
}
|
||||
|
||||
[HttpPatch("{id}")]
|
||||
[RequirePermission("admin.users.update")]
|
||||
public async Task<UserDetailResponse> 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);
|
||||
}
|
||||
@@ -86,6 +86,8 @@ public class AuthorizationMiddleware
|
||||
detail: permission,
|
||||
statusCode: 403
|
||||
).ExecuteAsync(context);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,11 +13,12 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MoonCore" Version="1.5.8" />
|
||||
<PackageReference Include="MoonCore.Extended" Version="1.0.6" />
|
||||
<PackageReference Include="MoonCore.Extended" Version="1.0.7" />
|
||||
<PackageReference Include="MoonCore.PluginFramework" Version="1.0.0" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0"/>
|
||||
<PackageReference Include="Ben.Demystifier" Version="0.4.1" />
|
||||
<PackageReference Include="MoonCore.Blazor.Tailwind" Version="1.0.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -13,7 +13,6 @@
|
||||
<PackageReference Include="MoonCore" Version="1.5.8" />
|
||||
<PackageReference Include="MoonCore.Blazor" Version="1.2.1" />
|
||||
<PackageReference Include="MoonCore.PluginFramework" Version="1.0.0" />
|
||||
<PackageReference Include="MoonCore.Blazor.Tailwind" Version="1.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
<Router AppAssembly="@typeof(App).Assembly">
|
||||
<Found Context="routeData">
|
||||
<CascadingValue Name="TargetPageType" Value="routeData.PageType">
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
|
||||
</CascadingValue>
|
||||
</Found>
|
||||
<NotFound>
|
||||
<PageTitle>Not found</PageTitle>
|
||||
|
||||
@@ -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<MainLayout> Logger
|
||||
|
||||
@@ -29,15 +32,21 @@ else
|
||||
<div class="lg:pl-72">
|
||||
<AppHeader Layout="this"/>
|
||||
|
||||
<ErrorHandler>
|
||||
<main class="py-10">
|
||||
<div class="px-4 sm:px-6 lg:px-8">
|
||||
<ErrorHandler CustomHandler="HandleException">
|
||||
|
||||
<PermissionHandler CheckFunction="CheckPermission">
|
||||
<CascadingValue Value="this" IsFixed="true">
|
||||
@Body
|
||||
</CascadingValue>
|
||||
</PermissionHandler>
|
||||
|
||||
</ErrorHandler>
|
||||
</div>
|
||||
</main>
|
||||
</ErrorHandler>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@@ -127,4 +136,17 @@ else
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private bool CheckPermission(string permission) => IdentityService.HasPermission(permission);
|
||||
|
||||
private Task<bool> HandleException(Exception exception, ErrorHandler handler)
|
||||
{
|
||||
if (exception is HttpApiException httpApiException && httpApiException.Status == 401)
|
||||
{
|
||||
Task.Run(Load);
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
}
|
||||
@@ -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 @@
|
||||
<li>
|
||||
@if (!string.IsNullOrEmpty(group.Key))
|
||||
{
|
||||
<div class="text-xs font-semibold leading-6 text-gray-400">
|
||||
<div class="text-xs font-semibold leading-6 text-gray-400 my-2">
|
||||
@group.Key
|
||||
</div>
|
||||
}
|
||||
@@ -168,7 +170,7 @@
|
||||
{
|
||||
Items = ImplementationService.Get<ISidebarItemProvider>()
|
||||
.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());
|
||||
|
||||
@@ -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
|
||||
|
||||
<Crud TItem="UserDetailResponse"
|
||||
|
||||
Reference in New Issue
Block a user