Implemented permissions. Added user overview and session overview. Added lazy loader.. Did some ui

This commit is contained in:
Marcel Baumgartner 2023-10-16 21:15:04 +02:00
parent 0cde0fe302
commit 96bd131807
16 changed files with 388 additions and 19 deletions

View file

@ -0,0 +1,20 @@
using Moonlight.App.Models.Enums;
namespace Moonlight.App.Extensions.Attributes;
public class RequirePermissionAttribute : Attribute
{
public int PermissionInteger = 0;
public RequirePermissionAttribute(){}
public RequirePermissionAttribute(int perms)
{
PermissionInteger = perms;
}
public RequirePermissionAttribute(Permission permission)
{
PermissionInteger = (int)permission;
}
}

View file

@ -19,10 +19,12 @@
<Folder Include="App\Http\Middleware\" />
<Folder Include="App\Http\Requests\" />
<Folder Include="App\Http\Resources\" />
<Folder Include="wwwroot\img\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Ben.Demystifier" Version="0.4.1" />
<PackageReference Include="BlazorTable" Version="1.17.0" />
<PackageReference Include="JWT" Version="10.1.1" />
<PackageReference Include="MailKit" Version="4.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.0">

View file

@ -33,6 +33,7 @@
<script src="/js/bootstrap.bundle.min.js"></script>
<script src="/js/toaster.js"></script>
<script src="/js/moonlight.js"></script>
<script src="/_content/BlazorTable/BlazorTable.min.js"></script>
<script src="/_framework/blazor.server.js"></script>
</body>
</html>

View file

@ -1,3 +1,4 @@
using BlazorTable;
using Moonlight.App.Database;
using Moonlight.App.Extensions;
using Moonlight.App.Helpers;
@ -55,6 +56,7 @@ builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddHttpContextAccessor();
builder.Services.AddControllers();
builder.Services.AddBlazorTable();
builder.Logging.ClearProviders();
builder.Logging.AddProvider(new LogMigrateProvider());

View file

@ -0,0 +1,22 @@
<div class="card mb-5 mb-xl-10">
<div class="card-body pt-0 pb-0">
<ul class="nav nav-stretch nav-line-tabs nav-line-tabs-2x border-transparent fs-5 fw-bold">
<li class="nav-item mt-2">
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 0 ? "active" : "")" href="/admin/users">
Overview
</a>
</li>
<li class="nav-item mt-2">
<a class="nav-link text-active-primary ms-0 me-10 py-5 @(Index == 1 ? "active" : "")" href="/admin/users/sessions">
Sessions
</a>
</li>
</ul>
</div>
</div>
@code
{
[Parameter]
public int Index { get; set; }
}

View file

@ -0,0 +1,50 @@
@if (loaded)
{
@ChildContent
}
else
{
<div class="d-flex justify-content-center py-4">
<span class="fs-1 spinner-border spinner-border-lg align-middle me-2"></span>
<span class="mt-3 fs-5">@(Text)</span>
</div>
}
@code
{
[Parameter]
public RenderFragment ChildContent { get; set; }
[Parameter]
public Func<LazyLoader, Task> Load { get; set; }
[Parameter]
public string Text { get; set; } = "";
private bool loaded = false;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await Load.Invoke(this);
loaded = true;
await InvokeAsync(StateHasChanged);
}
}
public async Task SetText(string text)
{
Text = text;
await InvokeAsync(StateHasChanged);
}
public async Task Reload()
{
loaded = false;
await InvokeAsync(StateHasChanged);
await Load.Invoke(this);
loaded = true;
await InvokeAsync(StateHasChanged);
}
}

View file

@ -0,0 +1,64 @@
@using Moonlight.App.Extensions.Attributes
@using Moonlight.App.Models.Abstractions
@using Moonlight.App.Services
@inject IdentityService IdentityService
@if (Allowed)
{
@ChildContent
}
else
{
<h1>Ha, nein ;)</h1>
}
@code
{
[CascadingParameter(Name = "TargetPageType")]
public Type? TargetPageType { get; set; }
[Parameter]
public RenderFragment ChildContent { get; set; }
private bool Allowed = false;
protected override Task OnParametersSetAsync()
{
if(TargetPageType == null)
return Task.CompletedTask;
var attributes = TargetPageType.GetCustomAttributes(true);
var permAttrs = attributes
.Where(x => x.GetType() == typeof(RequirePermissionAttribute))
.Select(x => x as RequirePermissionAttribute)
.ToArray();
Allowed = true;
if (permAttrs.Any())
{
Allowed = false;
foreach (var permissionRequired in permAttrs)
{
if(permissionRequired == null)
continue;
var permission = PermissionStorage.GetFromInteger(permissionRequired.PermissionInteger);
if (IdentityService.Permissions[permission])
{
Allowed = true;
}
}
}
if (!Allowed)
{
//Logger.Warn($"{IdentityService.Ip} has tried to access {NavigationManager.Uri} without permission", "security");
}
return Task.CompletedTask;
}
}

View file

@ -1,4 +1,8 @@
@using Moonlight.Shared.Layouts
@using Moonlight.App.Services
@using Moonlight.App.Models.Enums
@inject IdentityService IdentityService
<div class="app-sidebar flex-column @(Layout.ShowMobileSidebar ? "drawer drawer-start drawer-on" : "")">
<div class="app-sidebar-header d-flex flex-stack d-none d-lg-flex pt-8 pb-2">
@ -20,6 +24,67 @@
</span>
</a>
</div>
<div class="menu-item">
<a class="menu-link " href="/store">
<span class="menu-icon">
<i class="bx bx-sm bx-store"></i>
</span>
<span class="menu-title">
Store
</span>
</a>
</div>
<div class="menu-item">
<a class="menu-link " href="/services">
<span class="menu-icon">
<i class="bx bx-sm bxs-component"></i>
</span>
<span class="menu-title">
Services
</span>
</a>
</div>
<div class="menu-item">
<a class="menu-link " href="/community">
<span class="menu-icon">
<i class="bx bx-sm bx-group"></i>
</span>
<span class="menu-title">
Community
</span>
</a>
</div>
@if (IdentityService.Permissions[Permission.AdminMenu])
{
<div class="menu-item mt-5">
<div class="menu-heading text-uppercase fs-7 fw-bold">
Admin
</div>
<div class="app-sidebar-separator separator"></div>
</div>
<div class="menu-item">
<a class="menu-link " href="/admin">
<span class="menu-icon">
<i class="bx bx-sm bxs-dashboard"></i>
</span>
<span class="menu-title">
Dashboard
</span>
</a>
</div>
<div class="menu-item">
<a class="menu-link " href="/admin/users">
<span class="menu-icon">
<i class="bx bx-sm bx-group"></i>
</span>
<span class="menu-title">
Users
</span>
</a>
</div>
}
</div>
</div>
</div>

View file

@ -1,11 +1,14 @@
@using System.Diagnostics
@using Moonlight.App.Exceptions
@using Moonlight.App.Models.Enums
@using Moonlight.App.Services
@inherits ErrorBoundaryBase
@inject IdentityService IdentityService
@if (Crashed)
{
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development") // TODO: Add check for admin perms to show exceptions to admins
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development" || IdentityService.Permissions[Permission.AdminViewExceptions]) // TODO: Add check for admin perms to show exceptions to admins
{
if (Exception != null)
{

View file

@ -36,7 +36,9 @@
{
<DefaultLayout>
<SoftErrorHandler>
<PermissionChecker>
@Body
</PermissionChecker>
</SoftErrorHandler>
</DefaultLayout>
}

View file

@ -0,0 +1,6 @@
@page "/admin"
@using Moonlight.App.Extensions.Attributes
@using Moonlight.App.Models.Enums
@attribute [RequirePermission(Permission.AdminOverview)]

View file

@ -0,0 +1,52 @@
@page "/admin/users"
@using Moonlight.App.Extensions.Attributes
@using Moonlight.App.Models.Enums
@using Moonlight.App.Repositories
@using BlazorTable
@attribute [RequirePermission(Permission.AdminUsers)]
@inject Repository<User> UserRepository
<AdminUsersNavigation Index="0"/>
<div class="card">
<div class="card-body">
<LazyLoader Load="Load">
<Table TableItem="User"
Items="AllUsers"
PageSize="50"
TableClass="table table-row-bordered table-row-gray-100 align-middle gs-0 gy-3 fs-6"
TableHeadClass="fw-bold text-muted">
<Column TableItem="User" Title="Id" Field="@(x => x.Id)" Sortable="true" Filterable="true"/>
<Column TableItem="User" Title="Email" Field="@(x => x.Email)" Sortable="true" Filterable="true">
<Template>
<a href="/admin/users/view/@(context.Id)">@(context.Email)</a>
</Template>
</Column>
<Column TableItem="User" Title="Username" Field="@(x => x.Username)" Sortable="true" Filterable="true"/>
<Column TableItem="User" Title="Created at" Field="@(x => x.CreatedAt)" Sortable="true" Filterable="true">
<Template>
<span>@(Formatter.FormatDate(context.CreatedAt))</span>
</Template>
</Column>
<Pager ShowPageNumber="true" ShowTotalCount="true" AlwaysShow="true"/>
</Table>
</LazyLoader>
</div>
</div>
@code
{
private User[] AllUsers;
private Task Load(LazyLoader _)
{
AllUsers = UserRepository
.Get()
.ToArray();
return Task.CompletedTask;
}
}

View file

@ -0,0 +1,61 @@
@page "/admin/users/sessions"
@using Moonlight.App.Extensions.Attributes
@using Moonlight.App.Models.Enums
@using BlazorTable
@using Moonlight.App.Models.Abstractions
@using Moonlight.App.Services
@attribute [RequirePermission(Permission.AdminSessions)]
@inject SessionService SessionService
<AdminUsersNavigation Index="1"/>
<div class="card">
<div class="card-body">
<LazyLoader Load="Load">
<Table TableItem="Session"
Items="SessionService.Sessions"
PageSize="50"
TableClass="table table-row-bordered table-row-gray-100 align-middle gs-0 gy-3 fs-6"
TableHeadClass="fw-bold text-muted">
<Column TableItem="Session" Title="IP" Field="@(x => x.Ip)" Sortable="true" Filterable="true"/>
<Column TableItem="Session" Title="URL" Field="@(x => x.Url)" Sortable="true" Filterable="true"/>
<Column TableItem="Session" Title="User" Field="@(x => x.User)" Sortable="false" Filterable="false">
<Template>
<span>@(context.User?.Username ?? "Guest")</span>
</Template>
</Column>
<Column TableItem="Session" Title="Last activity" Field="@(x => x.UpdatedAt)" Sortable="true" Filterable="true">
<Template>
<span>@(Formatter.FormatUptime(DateTime.UtcNow - context.UpdatedAt))</span>
</Template>
</Column>
<Column TableItem="Session" Title="Connected since" Field="@(x => x.UpdatedAt)" Sortable="true" Filterable="true">
<Template>
<span>@(Formatter.FormatUptime(DateTime.UtcNow - context.CreatedAt))</span>
</Template>
</Column>
<Pager ShowPageNumber="true" ShowTotalCount="true" AlwaysShow="true"/>
</Table>
</LazyLoader>
</div>
</div>
@code
{
private Task Load(LazyLoader _)
{
Task.Run(async () =>
{
while (true)
{
await InvokeAsync(StateHasChanged);
await Task.Delay(TimeSpan.FromSeconds(1));
}
});
return Task.CompletedTask;
}
}

View file

@ -1,20 +1,38 @@
@page "/"
@using Moonlight.App.Exceptions
<h1>Hello, world!</h1>
@using Moonlight.App.Services
<ConfirmButton Text="Crash" WorkingText="Crashing" CssClasses="btn-danger" OnClick="Do" />
<ConfirmButton Text="Crash" WorkingText="Crashing" CssClasses="btn-danger" OnClick="Do2" />
@inject IdentityService IdentityService
@code
{
private async Task Do()
{
throw new DisplayException("LOL");
}
<div class="card border-0" style="background-image: url('img/covers/minecraft.png'); background-repeat: no-repeat; background-position: bottom; background-size: cover">
<div class="card-body">
<span class="h2">Welcome back, <span class="text-info">@(IdentityService.CurrentUser.Username)</span></span>
</div>
</div>
private async Task Do2()
{
throw new FormatException("LOL");
}
}
<div class="mt-5 row">
<div class="col-md-3 col-12">
<div href="#" class="card card-body text-center fs-6">
<div class="mb-5">
Do you want to order new services? Are you searching for our service specifications and prices?
</div>
<a href="/store" class="btn btn-primary">Go to store</a>
</div>
</div>
<div class="col-md-3 col-12">
<div href="#" class="card card-body text-center fs-6">
<div class="mb-5">
Do you want to order new services? Are you searching for our service specifications and prices?
</div>
<a href="/store" class="btn btn-primary">Go to store</a>
</div>
</div>
<div class="col-md-3 col-12">
<div href="#" class="card card-body text-center fs-6">
<div class="mb-5">
Do you want to order new services? Are you searching for our service specifications and prices?
</div>
<a href="/store" class="btn btn-primary">Go to store</a>
</div>
</div>
</div>

View file

@ -8,3 +8,4 @@
@using Moonlight.Shared.Components.Navigations
@using Moonlight.App.Services.Interop
@using Moonlight.App.Exceptions
@using Moonlight.App.Database.Entities

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB