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.Abstractions;
|
||||||
using MoonCore.Extended.Helpers;
|
using MoonCore.Extended.Helpers;
|
||||||
using MoonCore.Models;
|
using MoonCore.Models;
|
||||||
|
using Moonlight.ApiServer.Attributes;
|
||||||
using Moonlight.ApiServer.Database.Entities;
|
using Moonlight.ApiServer.Database.Entities;
|
||||||
using Moonlight.Shared.Http.Requests.Admin.Users;
|
using Moonlight.Shared.Http.Requests.Admin.Users;
|
||||||
using Moonlight.Shared.Http.Responses.Admin.Users;
|
using Moonlight.Shared.Http.Responses.Admin.Users;
|
||||||
@@ -23,14 +24,17 @@ public class UsersController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
|
[RequirePermission("admin.users.read")]
|
||||||
public async Task<IPagedData<UserDetailResponse>> Get([FromQuery] int page, [FromQuery] int pageSize = 50)
|
public async Task<IPagedData<UserDetailResponse>> Get([FromQuery] int page, [FromQuery] int pageSize = 50)
|
||||||
=> await CrudHelper.Get(page, pageSize);
|
=> await CrudHelper.Get(page, pageSize);
|
||||||
|
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
|
[RequirePermission("admin.users.read")]
|
||||||
public async Task<UserDetailResponse> GetSingle(int id)
|
public async Task<UserDetailResponse> GetSingle(int id)
|
||||||
=> await CrudHelper.GetSingle(id);
|
=> await CrudHelper.GetSingle(id);
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
|
[RequirePermission("admin.users.create")]
|
||||||
public async Task<UserDetailResponse> Create([FromBody] CreateUserRequest request)
|
public async Task<UserDetailResponse> Create([FromBody] CreateUserRequest request)
|
||||||
{
|
{
|
||||||
// Reformat values
|
// Reformat values
|
||||||
@@ -50,6 +54,7 @@ public class UsersController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpPatch("{id}")]
|
[HttpPatch("{id}")]
|
||||||
|
[RequirePermission("admin.users.update")]
|
||||||
public async Task<UserDetailResponse> Update([FromRoute] int id, [FromBody] UpdateUserRequest request)
|
public async Task<UserDetailResponse> Update([FromRoute] int id, [FromBody] UpdateUserRequest request)
|
||||||
{
|
{
|
||||||
var user = await CrudHelper.GetSingleModel(id);
|
var user = await CrudHelper.GetSingleModel(id);
|
||||||
@@ -76,6 +81,7 @@ public class UsersController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
|
[RequirePermission("admin.users.delete")]
|
||||||
public async Task Delete([FromRoute] int id)
|
public async Task Delete([FromRoute] int id)
|
||||||
=> await CrudHelper.Delete(id);
|
=> await CrudHelper.Delete(id);
|
||||||
}
|
}
|
||||||
@@ -86,6 +86,8 @@ public class AuthorizationMiddleware
|
|||||||
detail: permission,
|
detail: permission,
|
||||||
statusCode: 403
|
statusCode: 403
|
||||||
).ExecuteAsync(context);
|
).ExecuteAsync(context);
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,11 +13,12 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="MoonCore" Version="1.5.8" />
|
<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="MoonCore.PluginFramework" Version="1.0.0" />
|
||||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
|
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0"/>
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0"/>
|
||||||
<PackageReference Include="Ben.Demystifier" Version="0.4.1" />
|
<PackageReference Include="Ben.Demystifier" Version="0.4.1" />
|
||||||
|
<PackageReference Include="MoonCore.Blazor.Tailwind" Version="1.0.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ public class DefaultSidebarItemProvider : ISidebarItemProvider
|
|||||||
Group = "Admin",
|
Group = "Admin",
|
||||||
Path = "/admin",
|
Path = "/admin",
|
||||||
Priority = 0,
|
Priority = 0,
|
||||||
RequiresExactMatch = true
|
RequiresExactMatch = true,
|
||||||
|
Permission = "admin.overview"
|
||||||
},
|
},
|
||||||
new SidebarItem()
|
new SidebarItem()
|
||||||
{
|
{
|
||||||
@@ -36,7 +37,8 @@ public class DefaultSidebarItemProvider : ISidebarItemProvider
|
|||||||
Group = "Admin",
|
Group = "Admin",
|
||||||
Path = "/admin/users",
|
Path = "/admin/users",
|
||||||
Priority = 1,
|
Priority = 1,
|
||||||
RequiresExactMatch = false
|
RequiresExactMatch = false,
|
||||||
|
Permission = "admin.users.read"
|
||||||
},
|
},
|
||||||
new SidebarItem()
|
new SidebarItem()
|
||||||
{
|
{
|
||||||
@@ -45,7 +47,8 @@ public class DefaultSidebarItemProvider : ISidebarItemProvider
|
|||||||
Group = "Admin",
|
Group = "Admin",
|
||||||
Path = "/admin/api",
|
Path = "/admin/api",
|
||||||
Priority = 2,
|
Priority = 2,
|
||||||
RequiresExactMatch = false
|
RequiresExactMatch = false,
|
||||||
|
Permission = "admin.api.read"
|
||||||
},
|
},
|
||||||
new SidebarItem()
|
new SidebarItem()
|
||||||
{
|
{
|
||||||
@@ -54,7 +57,8 @@ public class DefaultSidebarItemProvider : ISidebarItemProvider
|
|||||||
Group = "Admin",
|
Group = "Admin",
|
||||||
Path = "/admin/system",
|
Path = "/admin/system",
|
||||||
Priority = 3,
|
Priority = 3,
|
||||||
RequiresExactMatch = false
|
RequiresExactMatch = false,
|
||||||
|
Permission = "admin.system.info"
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,4 +8,5 @@ public class SidebarItem
|
|||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
public int Priority { get; set; }
|
public int Priority { get; set; }
|
||||||
public bool RequiresExactMatch { get; set; } = false;
|
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" Version="1.5.8" />
|
||||||
<PackageReference Include="MoonCore.Blazor" Version="1.2.1" />
|
<PackageReference Include="MoonCore.Blazor" Version="1.2.1" />
|
||||||
<PackageReference Include="MoonCore.PluginFramework" Version="1.0.0" />
|
<PackageReference Include="MoonCore.PluginFramework" Version="1.0.0" />
|
||||||
<PackageReference Include="MoonCore.Blazor.Tailwind" Version="1.0.2" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ public class IdentityService
|
|||||||
ApiClient = apiClient;
|
ApiClient = apiClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Login / Logout
|
||||||
|
|
||||||
public async Task Check()
|
public async Task Check()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -56,4 +58,42 @@ public class IdentityService
|
|||||||
await CookieService.SetValue("token", "", 30);
|
await CookieService.SetValue("token", "", 30);
|
||||||
await Check();
|
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">
|
<Router AppAssembly="@typeof(App).Assembly">
|
||||||
<Found Context="routeData">
|
<Found Context="routeData">
|
||||||
|
<CascadingValue Name="TargetPageType" Value="routeData.PageType">
|
||||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
|
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
|
||||||
|
</CascadingValue>
|
||||||
</Found>
|
</Found>
|
||||||
<NotFound>
|
<NotFound>
|
||||||
<PageTitle>Not found</PageTitle>
|
<PageTitle>Not found</PageTitle>
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
@using MoonCore.Helpers
|
@using MoonCore.Exceptions
|
||||||
|
@using MoonCore.Helpers
|
||||||
@using MoonCore.PluginFramework.Services
|
@using MoonCore.PluginFramework.Services
|
||||||
@using Moonlight.Client.Interfaces
|
@using Moonlight.Client.Interfaces
|
||||||
|
@using Moonlight.Client.Services
|
||||||
@using Moonlight.Client.UI.Partials
|
@using Moonlight.Client.UI.Partials
|
||||||
|
|
||||||
@inherits LayoutComponentBase
|
@inherits LayoutComponentBase
|
||||||
|
|
||||||
@inject ImplementationService ImplementationService
|
@inject ImplementationService ImplementationService
|
||||||
|
@inject IdentityService IdentityService
|
||||||
@inject IServiceProvider ServiceProvider
|
@inject IServiceProvider ServiceProvider
|
||||||
@inject ILogger<MainLayout> Logger
|
@inject ILogger<MainLayout> Logger
|
||||||
|
|
||||||
@@ -29,15 +32,21 @@ else
|
|||||||
<div class="lg:pl-72">
|
<div class="lg:pl-72">
|
||||||
<AppHeader Layout="this"/>
|
<AppHeader Layout="this"/>
|
||||||
|
|
||||||
<ErrorHandler>
|
|
||||||
<main class="py-10">
|
<main class="py-10">
|
||||||
<div class="px-4 sm:px-6 lg:px-8">
|
<div class="px-4 sm:px-6 lg:px-8">
|
||||||
|
<ErrorHandler CustomHandler="HandleException">
|
||||||
|
|
||||||
|
<PermissionHandler CheckFunction="CheckPermission">
|
||||||
<CascadingValue Value="this" IsFixed="true">
|
<CascadingValue Value="this" IsFixed="true">
|
||||||
@Body
|
@Body
|
||||||
</CascadingValue>
|
</CascadingValue>
|
||||||
|
</PermissionHandler>
|
||||||
|
|
||||||
|
</ErrorHandler>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</ErrorHandler>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -127,4 +136,17 @@ else
|
|||||||
|
|
||||||
await InvokeAsync(StateHasChanged);
|
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 MoonCore.PluginFramework.Services
|
||||||
@using Moonlight.Client.Interfaces
|
@using Moonlight.Client.Interfaces
|
||||||
@using Moonlight.Client.Models
|
@using Moonlight.Client.Models
|
||||||
|
@using Moonlight.Client.Services
|
||||||
@using Moonlight.Client.UI.Layouts
|
@using Moonlight.Client.UI.Layouts
|
||||||
|
|
||||||
@inject NavigationManager Navigation
|
@inject NavigationManager Navigation
|
||||||
|
@inject IdentityService IdentityService
|
||||||
@inject ImplementationService ImplementationService
|
@inject ImplementationService ImplementationService
|
||||||
|
|
||||||
@{
|
@{
|
||||||
@@ -111,7 +113,7 @@
|
|||||||
<li>
|
<li>
|
||||||
@if (!string.IsNullOrEmpty(group.Key))
|
@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
|
@group.Key
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@@ -168,7 +170,7 @@
|
|||||||
{
|
{
|
||||||
Items = ImplementationService.Get<ISidebarItemProvider>()
|
Items = ImplementationService.Get<ISidebarItemProvider>()
|
||||||
.SelectMany(x => x.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 ?? "")
|
.GroupBy(x => x.Group ?? "")
|
||||||
.OrderByDescending(x => string.IsNullOrEmpty(x.Key))
|
.OrderByDescending(x => string.IsNullOrEmpty(x.Key))
|
||||||
.ToDictionary(x => x.Key, x => x.OrderBy(y => y.Priority).ToArray());
|
.ToDictionary(x => x.Key, x => x.OrderBy(y => y.Priority).ToArray());
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
@page "/admin/users"
|
@page "/admin/users"
|
||||||
|
|
||||||
|
@using MoonCore.Blazor.Tailwind.Attributes
|
||||||
@using MoonCore.Blazor.Tailwind.Forms.Components
|
@using MoonCore.Blazor.Tailwind.Forms.Components
|
||||||
@using MoonCore.Helpers
|
@using MoonCore.Helpers
|
||||||
@using MoonCore.Models
|
@using MoonCore.Models
|
||||||
@using Moonlight.Shared.Http.Requests.Admin.Users
|
@using Moonlight.Shared.Http.Requests.Admin.Users
|
||||||
@using Moonlight.Shared.Http.Responses.Admin.Users
|
@using Moonlight.Shared.Http.Responses.Admin.Users
|
||||||
|
|
||||||
|
@attribute [RequirePermission("admin.users.read")]
|
||||||
|
|
||||||
@inject HttpApiClient HttpApiClient
|
@inject HttpApiClient HttpApiClient
|
||||||
|
|
||||||
<Crud TItem="UserDetailResponse"
|
<Crud TItem="UserDetailResponse"
|
||||||
|
|||||||
Reference in New Issue
Block a user