Updated MoonCore dependencies. Switched to asp.net core native authentication scheme abstractions. Updated claim usage in frontend
This commit is contained in:
19
Moonlight.Client/Implementations/LogErrorFilter.cs
Normal file
19
Moonlight.Client/Implementations/LogErrorFilter.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using MoonCore.Blazor.FlyonUi.Exceptions;
|
||||
|
||||
namespace Moonlight.Client.Implementations;
|
||||
|
||||
public class LogErrorFilter : IGlobalErrorFilter
|
||||
{
|
||||
private readonly ILogger<LogErrorFilter> Logger;
|
||||
|
||||
public LogErrorFilter(ILogger<LogErrorFilter> logger)
|
||||
{
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
public Task<bool> HandleException(Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "Global error processed");
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MoonCore.Blazor.FlyonUi.Exceptions;
|
||||
using MoonCore.Exceptions;
|
||||
|
||||
namespace Moonlight.Client.Implementations;
|
||||
|
||||
public class UnauthenticatedErrorFilter : IGlobalErrorFilter
|
||||
{
|
||||
private readonly NavigationManager Navigation;
|
||||
|
||||
public UnauthenticatedErrorFilter(NavigationManager navigation)
|
||||
{
|
||||
Navigation = navigation;
|
||||
}
|
||||
|
||||
public Task<bool> HandleException(Exception ex)
|
||||
{
|
||||
if (ex is not HttpApiException { Status: 401 })
|
||||
return Task.FromResult(false);
|
||||
|
||||
Navigation.NavigateTo("/api/auth/logout", true);
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
@@ -22,9 +22,11 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Blazor-ApexCharts" Version="6.0.0" />
|
||||
<PackageReference Include="MoonCore" Version="1.9.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.8" />
|
||||
<PackageReference Include="MoonCore" Version="1.9.6" />
|
||||
<PackageReference Include="MoonCore.Blazor" Version="1.3.1" />
|
||||
<PackageReference Include="MoonCore.Blazor.FlyonUi" Version="1.0.9" />
|
||||
<PackageReference Include="MoonCore.Blazor.FlyonUi" Version="1.1.5" />
|
||||
<PackageReference Include="System.Security.Claims" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="storage\**\*" />
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
using System.Security.Claims;
|
||||
using System.Web;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using MoonCore.Blazor.FlyonUi.Auth;
|
||||
using MoonCore.Blazor.Services;
|
||||
using MoonCore.Exceptions;
|
||||
using MoonCore.Helpers;
|
||||
using Moonlight.Shared.Http.Requests.Auth;
|
||||
using Moonlight.Shared.Http.Responses.Auth;
|
||||
|
||||
namespace Moonlight.Client.Services;
|
||||
|
||||
public class RemoteAuthStateManager : AuthenticationStateManager
|
||||
{
|
||||
private readonly NavigationManager NavigationManager;
|
||||
private readonly HttpApiClient HttpApiClient;
|
||||
private readonly LocalStorageService LocalStorageService;
|
||||
private readonly ILogger<RemoteAuthStateManager> Logger;
|
||||
|
||||
public RemoteAuthStateManager(
|
||||
HttpApiClient httpApiClient,
|
||||
LocalStorageService localStorageService,
|
||||
NavigationManager navigationManager,
|
||||
ILogger<RemoteAuthStateManager> logger
|
||||
)
|
||||
{
|
||||
HttpApiClient = httpApiClient;
|
||||
LocalStorageService = localStorageService;
|
||||
NavigationManager = navigationManager;
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||
=> await LoadAuthState();
|
||||
|
||||
public override async Task HandleLogin()
|
||||
{
|
||||
var uri = new Uri(NavigationManager.Uri);
|
||||
var codeParam = HttpUtility.ParseQueryString(uri.Query).Get("code");
|
||||
|
||||
if (string.IsNullOrEmpty(codeParam)) // If this is true, we need to log in the user
|
||||
{
|
||||
await StartLogin();
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
var loginCompleteData = await HttpApiClient.PostJson<LoginCompleteResponse>(
|
||||
"api/auth/complete",
|
||||
new LoginCompleteRequest()
|
||||
{
|
||||
Code = codeParam
|
||||
}
|
||||
);
|
||||
|
||||
await LocalStorageService.SetString("AccessToken", loginCompleteData.AccessToken);
|
||||
|
||||
NavigationManager.NavigateTo("/");
|
||||
NotifyAuthenticationStateChanged(LoadAuthState());
|
||||
}
|
||||
catch (HttpApiException e)
|
||||
{
|
||||
Logger.LogError("Unable to complete login: {e}", e);
|
||||
|
||||
await StartLogin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task Logout()
|
||||
{
|
||||
if (await LocalStorageService.ContainsKey("AccessToken"))
|
||||
await LocalStorageService.SetString("AccessToken", "");
|
||||
|
||||
NotifyAuthenticationStateChanged(LoadAuthState());
|
||||
}
|
||||
|
||||
#region Utilities
|
||||
|
||||
private async Task StartLogin()
|
||||
{
|
||||
var loginStartData = await HttpApiClient.GetJson<LoginStartResponse>("api/auth/start");
|
||||
|
||||
NavigationManager.NavigateTo(loginStartData.Url, true);
|
||||
}
|
||||
|
||||
private async Task<AuthenticationState> LoadAuthState()
|
||||
{
|
||||
AuthenticationState newState;
|
||||
|
||||
try
|
||||
{
|
||||
var checkData = await HttpApiClient.GetJson<CheckResponse>("api/auth/check");
|
||||
|
||||
newState = new(new ClaimsPrincipal(
|
||||
new ClaimsIdentity(
|
||||
[
|
||||
new Claim("username", checkData.Username),
|
||||
new Claim("email", checkData.Email),
|
||||
new Claim("permissions", string.Join(";", checkData.Permissions))
|
||||
],
|
||||
"RemoteAuthStateManager"
|
||||
)
|
||||
));
|
||||
}
|
||||
catch (HttpApiException)
|
||||
{
|
||||
newState = new(new ClaimsPrincipal(
|
||||
new ClaimsIdentity()
|
||||
));
|
||||
}
|
||||
|
||||
return newState;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
45
Moonlight.Client/Services/RemoteAuthStateProvider.cs
Normal file
45
Moonlight.Client/Services/RemoteAuthStateProvider.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using MoonCore.Exceptions;
|
||||
using MoonCore.Helpers;
|
||||
using Moonlight.Shared.Http.Responses.Auth;
|
||||
|
||||
namespace Moonlight.Client.Services;
|
||||
|
||||
public class RemoteAuthStateProvider : AuthenticationStateProvider
|
||||
{
|
||||
private readonly HttpApiClient ApiClient;
|
||||
|
||||
public RemoteAuthStateProvider(HttpApiClient apiClient)
|
||||
{
|
||||
ApiClient = apiClient;
|
||||
}
|
||||
|
||||
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||
{
|
||||
ClaimsPrincipal principal;
|
||||
|
||||
try
|
||||
{
|
||||
var claims = await ApiClient.GetJson<AuthClaimResponse[]>(
|
||||
"api/auth/check"
|
||||
);
|
||||
|
||||
principal = new ClaimsPrincipal(
|
||||
new ClaimsIdentity(
|
||||
claims.Select(x => new Claim(x.Type, x.Value)),
|
||||
"RemoteAuthentication"
|
||||
)
|
||||
);
|
||||
}
|
||||
catch (HttpApiException e)
|
||||
{
|
||||
if (e.Status != 401 && e.Status != 403)
|
||||
throw;
|
||||
|
||||
principal = new ClaimsPrincipal();
|
||||
}
|
||||
|
||||
return new AuthenticationState(principal);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MoonCore.Blazor.FlyonUi.Auth;
|
||||
using MoonCore.Blazor.FlyonUi.Exceptions;
|
||||
using MoonCore.Permissions;
|
||||
using Moonlight.Client.Implementations;
|
||||
using Moonlight.Client.Services;
|
||||
|
||||
namespace Moonlight.Client.Startup;
|
||||
@@ -12,11 +14,12 @@ public partial class Startup
|
||||
WebAssemblyHostBuilder.Services.AddAuthorizationCore();
|
||||
WebAssemblyHostBuilder.Services.AddCascadingAuthenticationState();
|
||||
|
||||
WebAssemblyHostBuilder.Services.AddAuthenticationStateManager<RemoteAuthStateManager>();
|
||||
WebAssemblyHostBuilder.Services.AddScoped<AuthenticationStateProvider, RemoteAuthStateProvider>();
|
||||
WebAssemblyHostBuilder.Services.AddScoped<IGlobalErrorFilter, UnauthenticatedErrorFilter>();
|
||||
|
||||
WebAssemblyHostBuilder.Services.AddAuthorizationPermissions(options =>
|
||||
{
|
||||
options.ClaimName = "permissions";
|
||||
options.ClaimName = "Permissions";
|
||||
options.Prefix = "permissions:";
|
||||
});
|
||||
|
||||
|
||||
@@ -25,27 +25,11 @@ public partial class Startup
|
||||
WebAssemblyHostBuilder.Services.AddScoped(sp =>
|
||||
{
|
||||
var httpClient = sp.GetRequiredService<HttpClient>();
|
||||
var httpApiClient = new HttpApiClient(httpClient);
|
||||
|
||||
var localStorageService = sp.GetRequiredService<LocalStorageService>();
|
||||
|
||||
httpApiClient.OnConfigureRequest += async request =>
|
||||
{
|
||||
var accessToken = await localStorageService.GetString("AccessToken");
|
||||
|
||||
if (string.IsNullOrEmpty(accessToken))
|
||||
return;
|
||||
|
||||
request.Headers.Add("Authorization", $"Bearer {accessToken}");
|
||||
};
|
||||
|
||||
return httpApiClient;
|
||||
return new HttpApiClient(httpClient);
|
||||
});
|
||||
|
||||
WebAssemblyHostBuilder.Services.AddScoped<WindowService>();
|
||||
WebAssemblyHostBuilder.Services.AddFileManagerOperations();
|
||||
WebAssemblyHostBuilder.Services.AddFlyonUiServices();
|
||||
WebAssemblyHostBuilder.Services.AddScoped<LocalStorageService>();
|
||||
|
||||
WebAssemblyHostBuilder.Services.AddScoped<ThemeService>();
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MoonCore.Blazor.FlyonUi.Exceptions;
|
||||
using MoonCore.Logging;
|
||||
using Moonlight.Client.Implementations;
|
||||
|
||||
namespace Moonlight.Client.Startup;
|
||||
|
||||
@@ -7,6 +10,7 @@ public partial class Startup
|
||||
private Task SetupLogging()
|
||||
{
|
||||
var loggerFactory = new LoggerFactory();
|
||||
|
||||
loggerFactory.AddAnsiConsole();
|
||||
|
||||
Logger = loggerFactory.CreateLogger<Startup>();
|
||||
@@ -19,6 +23,8 @@ public partial class Startup
|
||||
WebAssemblyHostBuilder.Logging.ClearProviders();
|
||||
WebAssemblyHostBuilder.Logging.AddAnsiConsole();
|
||||
|
||||
WebAssemblyHostBuilder.Services.AddScoped<IGlobalErrorFilter, LogErrorFilter>();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -28,8 +28,8 @@ public partial class Startup
|
||||
WebAssemblyHostBuilder = builder;
|
||||
|
||||
await PrintVersion();
|
||||
await SetupLogging();
|
||||
|
||||
await SetupLogging();
|
||||
await LoadConfiguration();
|
||||
await InitializePlugins();
|
||||
|
||||
|
||||
534
Moonlight.Client/Styles/MoonCore.Blazor.FlyonUi/mooncore.map
Normal file
534
Moonlight.Client/Styles/MoonCore.Blazor.FlyonUi/mooncore.map
Normal file
@@ -0,0 +1,534 @@
|
||||
!bg-base-100
|
||||
!border-base-content/40
|
||||
!border-none
|
||||
!flex
|
||||
!font-medium
|
||||
!font-semibold
|
||||
!h-2.5
|
||||
!justify-between
|
||||
!me-1.5
|
||||
!ms-auto
|
||||
!px-2.5
|
||||
!py-0.5
|
||||
!rounded-full
|
||||
!rounded-xs
|
||||
!text-sm
|
||||
!w-2.5
|
||||
*:[grid-area:1/1]
|
||||
*:first:rounded-tl-lg
|
||||
*:last:rounded-tr-lg
|
||||
-left-4
|
||||
-ml-4
|
||||
-translate-x-full
|
||||
-translate-y-1/2
|
||||
absolute
|
||||
accordion
|
||||
accordion-bordered
|
||||
accordion-content
|
||||
accordion-toggle
|
||||
active
|
||||
active-tab:bg-primary
|
||||
active-tab:hover:text-primary-content
|
||||
active-tab:text-primary-content
|
||||
advance-select-menu
|
||||
advance-select-option
|
||||
advance-select-tag
|
||||
advance-select-toggle
|
||||
alert
|
||||
alert-error
|
||||
alert-outline
|
||||
alert-soft
|
||||
align-bottom
|
||||
align-middle
|
||||
animate-bounce
|
||||
animate-ping
|
||||
aria-[current='page']:text-bg-soft-primary
|
||||
avatar
|
||||
badge
|
||||
badge-error
|
||||
badge-info
|
||||
badge-outline
|
||||
badge-primary
|
||||
badge-soft
|
||||
badge-success
|
||||
bg-background
|
||||
bg-background/60
|
||||
bg-base-100
|
||||
bg-base-150
|
||||
bg-base-200
|
||||
bg-base-200!
|
||||
bg-base-200/50
|
||||
bg-base-300
|
||||
bg-base-300/45
|
||||
bg-base-300/50
|
||||
bg-base-300/60
|
||||
bg-error
|
||||
bg-info
|
||||
bg-primary
|
||||
bg-primary/5
|
||||
bg-success
|
||||
bg-transparent
|
||||
bg-warning
|
||||
block
|
||||
blur
|
||||
border
|
||||
border-0
|
||||
border-2
|
||||
border-b
|
||||
border-base-content
|
||||
border-base-content/20
|
||||
border-base-content/25
|
||||
border-base-content/40
|
||||
border-base-content/5
|
||||
border-dashed
|
||||
border-t
|
||||
border-transparent
|
||||
bottom-0
|
||||
bottom-full
|
||||
break-words
|
||||
btn
|
||||
btn-accent
|
||||
btn-active
|
||||
btn-circle
|
||||
btn-disabled
|
||||
btn-error
|
||||
btn-info
|
||||
btn-outline
|
||||
btn-primary
|
||||
btn-secondary
|
||||
btn-sm
|
||||
btn-soft
|
||||
btn-square
|
||||
btn-success
|
||||
btn-text
|
||||
btn-warning
|
||||
card
|
||||
card-alert
|
||||
card-body
|
||||
card-border
|
||||
card-footer
|
||||
card-header
|
||||
card-title
|
||||
carousel
|
||||
carousel-body
|
||||
carousel-next
|
||||
carousel-prev
|
||||
carousel-slide
|
||||
chat
|
||||
chat-avatar
|
||||
chat-bubble
|
||||
chat-footer
|
||||
chat-header
|
||||
chat-receiver
|
||||
chat-sender
|
||||
checkbox
|
||||
checkbox-primary
|
||||
checkbox-xs
|
||||
col-span-1
|
||||
collapse
|
||||
combo-box-selected:block
|
||||
combo-box-selected:dropdown-active
|
||||
complete
|
||||
container
|
||||
contents
|
||||
cursor-default
|
||||
cursor-not-allowed
|
||||
cursor-pointer
|
||||
diff
|
||||
disabled
|
||||
divide-base-150/60
|
||||
divide-y
|
||||
divider
|
||||
drop-shadow
|
||||
dropdown
|
||||
dropdown-active
|
||||
dropdown-disabled
|
||||
dropdown-item
|
||||
dropdown-menu
|
||||
dropdown-open:opacity-100
|
||||
dropdown-open:rotate-180
|
||||
dropdown-toggle
|
||||
duration-300
|
||||
duration-500
|
||||
ease-in-out
|
||||
ease-linear
|
||||
end-3
|
||||
file-upload-complete:progress-success
|
||||
fill-base-content
|
||||
fill-black
|
||||
fill-gray-200
|
||||
filter
|
||||
filter-reset
|
||||
fixed
|
||||
flex
|
||||
flex-1
|
||||
flex-col
|
||||
flex-grow
|
||||
flex-nowrap
|
||||
flex-row
|
||||
flex-shrink-0
|
||||
flex-wrap
|
||||
focus-visible:outline-none
|
||||
focus-within:border-primary
|
||||
focus:border-primary
|
||||
focus:outline-1
|
||||
focus:outline-none
|
||||
focus:outline-primary
|
||||
focus:ring-0
|
||||
font-bold
|
||||
font-inter
|
||||
font-medium
|
||||
font-normal
|
||||
font-semibold
|
||||
gap-0.5
|
||||
gap-1
|
||||
gap-1.5
|
||||
gap-2
|
||||
gap-3
|
||||
gap-4
|
||||
gap-5
|
||||
gap-6
|
||||
gap-x-1
|
||||
gap-x-2
|
||||
gap-x-3
|
||||
gap-y-1
|
||||
gap-y-2.5
|
||||
gap-y-3
|
||||
grid
|
||||
grid-cols-1
|
||||
grid-cols-4
|
||||
grid-flow-col
|
||||
grow
|
||||
grow-0
|
||||
h-12
|
||||
h-2
|
||||
h-3
|
||||
h-32
|
||||
h-64
|
||||
h-8
|
||||
h-auto
|
||||
h-full
|
||||
h-screen
|
||||
helper-text
|
||||
hidden
|
||||
hover:bg-primary/5
|
||||
hover:bg-transparent
|
||||
hover:text-base-content
|
||||
hover:text-base-content/60
|
||||
hover:text-primary
|
||||
image-full
|
||||
inline
|
||||
inline-block
|
||||
inline-flex
|
||||
inline-grid
|
||||
input
|
||||
input-floating
|
||||
input-floating-label
|
||||
input-lg
|
||||
input-md
|
||||
input-sm
|
||||
input-xl
|
||||
inset-0
|
||||
inset-y-0
|
||||
inset-y-2
|
||||
invisible
|
||||
is-invalid
|
||||
is-valid
|
||||
isolate
|
||||
italic
|
||||
items-center
|
||||
items-end
|
||||
items-start
|
||||
join
|
||||
join-item
|
||||
justify-between
|
||||
justify-center
|
||||
justify-end
|
||||
justify-start
|
||||
justify-stretch
|
||||
label-text
|
||||
leading-3
|
||||
leading-3.5
|
||||
leading-6
|
||||
leading-none
|
||||
left-0
|
||||
lg:bg-base-100/20
|
||||
lg:flex
|
||||
lg:gap-y-0
|
||||
lg:grid-cols-2
|
||||
lg:hidden
|
||||
lg:justify-end
|
||||
lg:justify-start
|
||||
lg:min-w-0
|
||||
lg:p-10
|
||||
lg:pb-5
|
||||
lg:pl-64
|
||||
lg:pr-3.5
|
||||
lg:pt-5
|
||||
lg:ring-1
|
||||
lg:ring-base-content/10
|
||||
lg:rounded-lg
|
||||
lg:shadow-xs
|
||||
link
|
||||
link-animated
|
||||
link-hover
|
||||
list-disc
|
||||
list-inside
|
||||
list-none
|
||||
loading
|
||||
loading-lg
|
||||
loading-sm
|
||||
loading-spinner
|
||||
loading-xl
|
||||
loading-xs
|
||||
lowercase
|
||||
m-10
|
||||
mask
|
||||
max-h-52
|
||||
max-lg:flex-col
|
||||
max-lg:hidden
|
||||
max-w-7xl
|
||||
max-w-80
|
||||
max-w-full
|
||||
max-w-lg
|
||||
max-w-sm
|
||||
max-w-xl
|
||||
mb-0.5
|
||||
mb-1
|
||||
mb-1.5
|
||||
mb-2
|
||||
mb-2.5
|
||||
mb-3
|
||||
mb-4
|
||||
mb-5
|
||||
md:min-w-md
|
||||
md:table-cell
|
||||
md:text-3xl
|
||||
me-1
|
||||
me-1.5
|
||||
me-2
|
||||
me-2.5
|
||||
me-5
|
||||
menu
|
||||
menu-active
|
||||
menu-disabled
|
||||
menu-dropdown
|
||||
menu-dropdown-show
|
||||
menu-horizontal
|
||||
menu-title
|
||||
min-h-0
|
||||
min-h-svh
|
||||
min-w-0
|
||||
min-w-28
|
||||
min-w-48
|
||||
min-w-60
|
||||
min-w-[100px]
|
||||
min-w-sm
|
||||
ml-3
|
||||
ml-4
|
||||
modal
|
||||
modal-content
|
||||
modal-dialog
|
||||
modal-middle
|
||||
modal-title
|
||||
mr-4
|
||||
ms-0.5
|
||||
ms-1
|
||||
ms-2
|
||||
ms-3
|
||||
ms-auto
|
||||
mt-1
|
||||
mt-1.5
|
||||
mt-10
|
||||
mt-12
|
||||
mt-2
|
||||
mt-2.5
|
||||
mt-3
|
||||
mt-3.5
|
||||
mt-4
|
||||
mt-5
|
||||
mt-8
|
||||
mx-1
|
||||
mx-auto
|
||||
my-3
|
||||
my-auto
|
||||
object-cover
|
||||
opacity-0
|
||||
opacity-100
|
||||
open
|
||||
origin-top-left
|
||||
outline
|
||||
outline-0
|
||||
overflow-hidden
|
||||
overflow-x-auto
|
||||
overflow-y-auto
|
||||
overlay-open:duration-50
|
||||
overlay-open:opacity-100
|
||||
p-0.5
|
||||
p-1
|
||||
p-2
|
||||
p-3
|
||||
p-4
|
||||
p-5
|
||||
p-6
|
||||
p-8
|
||||
pin-input
|
||||
placeholder-base-content/60
|
||||
pointer-events-auto
|
||||
pointer-events-none
|
||||
progress
|
||||
progress-bar
|
||||
progress-indeterminate
|
||||
progress-primary
|
||||
pt-0
|
||||
pt-0.5
|
||||
pt-3
|
||||
px-1.5
|
||||
px-2
|
||||
px-2.5
|
||||
px-3
|
||||
px-4
|
||||
px-5
|
||||
py-0.5
|
||||
py-1.5
|
||||
py-2
|
||||
py-2.5
|
||||
py-6
|
||||
radial-progress
|
||||
radio
|
||||
range
|
||||
relative
|
||||
resize
|
||||
ring-0
|
||||
ring-1
|
||||
ring-white/10
|
||||
rounded-box
|
||||
rounded-field
|
||||
rounded-full
|
||||
rounded-lg
|
||||
rounded-md
|
||||
rounded-t-lg
|
||||
row-active
|
||||
row-hover
|
||||
rtl:!mr-0
|
||||
select
|
||||
select-disabled:opacity-40
|
||||
select-disabled:pointer-events-none
|
||||
select-floating
|
||||
select-floating-label
|
||||
selected
|
||||
selected:select-active
|
||||
shadow-base-300/20
|
||||
shadow-lg
|
||||
shadow-md
|
||||
shadow-xs
|
||||
shrink-0
|
||||
size-10
|
||||
size-4
|
||||
size-5
|
||||
size-8
|
||||
skeleton
|
||||
skeleton-animated
|
||||
sm:auto-cols-max
|
||||
sm:flex
|
||||
sm:items-center
|
||||
sm:items-end
|
||||
sm:justify-between
|
||||
sm:justify-end
|
||||
sm:max-w-2xl
|
||||
sm:max-w-3xl
|
||||
sm:max-w-4xl
|
||||
sm:max-w-5xl
|
||||
sm:max-w-6xl
|
||||
sm:max-w-7xl
|
||||
sm:max-w-lg
|
||||
sm:max-w-md
|
||||
sm:max-w-xl
|
||||
sm:mb-0
|
||||
sm:mt-5
|
||||
sm:mt-6
|
||||
sm:p-6
|
||||
sm:py-2
|
||||
sm:text-sm/5
|
||||
space-x-1
|
||||
space-y-1
|
||||
space-y-4
|
||||
sr-only
|
||||
static
|
||||
status
|
||||
status-error
|
||||
sticky
|
||||
switch
|
||||
tab
|
||||
tab-active
|
||||
table
|
||||
table-pin-cols
|
||||
table-pin-rows
|
||||
tabs
|
||||
tabs-bordered
|
||||
tabs-lg
|
||||
tabs-lifted
|
||||
tabs-md
|
||||
tabs-sm
|
||||
tabs-xl
|
||||
tabs-xs
|
||||
text-2xl
|
||||
text-4xl
|
||||
text-accent
|
||||
text-base
|
||||
text-base-content
|
||||
text-base-content/40
|
||||
text-base-content/50
|
||||
text-base-content/60
|
||||
text-base-content/70
|
||||
text-base-content/80
|
||||
text-base/6
|
||||
text-center
|
||||
text-error
|
||||
text-error-content
|
||||
text-gray-400
|
||||
text-info
|
||||
text-info-content
|
||||
text-left
|
||||
text-lg
|
||||
text-primary
|
||||
text-primary-content
|
||||
text-sm
|
||||
text-sm/5
|
||||
text-success
|
||||
text-success-content
|
||||
text-warning
|
||||
text-warning-content
|
||||
text-xl
|
||||
text-xs
|
||||
text-xs/5
|
||||
textarea
|
||||
textarea-floating
|
||||
textarea-floating-label
|
||||
theme-controller
|
||||
tooltip
|
||||
tooltip-content
|
||||
top-0
|
||||
top-1/2
|
||||
top-full
|
||||
transform
|
||||
transition
|
||||
transition-all
|
||||
transition-opacity
|
||||
translate-x-0
|
||||
truncate
|
||||
underline
|
||||
uppercase
|
||||
validate
|
||||
w-0
|
||||
w-0.5
|
||||
w-12
|
||||
w-4
|
||||
w-56
|
||||
w-64
|
||||
w-fit
|
||||
w-full
|
||||
whitespace-nowrap
|
||||
z-10
|
||||
z-40
|
||||
z-50
|
||||
@@ -1,8 +1,13 @@
|
||||
@using Moonlight.Client.UI.Layouts
|
||||
@using Moonlight.Client.Services
|
||||
@using Moonlight.Client.UI.Partials
|
||||
|
||||
@inject ApplicationAssemblyService ApplicationAssemblyService
|
||||
|
||||
<ApplicationRouter DefaultLayout="@typeof(MainLayout)"
|
||||
AppAssembly="@typeof(App).Assembly"
|
||||
AdditionalAssemblies="ApplicationAssemblyService.Assemblies" />
|
||||
AdditionalAssemblies="ApplicationAssemblyService.Assemblies">
|
||||
<LoginTemplate>
|
||||
<LoginSelector />
|
||||
</LoginTemplate>
|
||||
</ApplicationRouter>
|
||||
@@ -1,4 +1,5 @@
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using System.Security.Claims
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
|
||||
<div class="col-span-12 md:col-span-6">
|
||||
<div class="font-medium leading-[1.1] tracking-tight">
|
||||
@@ -18,7 +19,6 @@
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var identity = await AuthState;
|
||||
var usernameClaim = identity.User.Claims.ToArray().First(x => x.Type == "username");
|
||||
Username = usernameClaim.Value;
|
||||
Username = identity.User.FindFirst(ClaimTypes.Name)!.Value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
@using System.Security.Claims
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@using MoonCore.Blazor.FlyonUi.Auth
|
||||
@using Moonlight.Client.Interfaces
|
||||
@using Moonlight.Client.Models
|
||||
@using Moonlight.Client.UI.Layouts
|
||||
|
||||
@inject NavigationManager Navigation
|
||||
@inject AuthenticationStateManager AuthStateManager
|
||||
@inject IEnumerable<ISidebarItemProvider> SidebarItemProviders
|
||||
@inject IAuthorizationService AuthorizationService
|
||||
|
||||
@@ -210,8 +208,8 @@
|
||||
var authState = await AuthState;
|
||||
|
||||
Identity = authState.User;
|
||||
Username = Identity.Claims.First(x => x.Type == "username").Value;
|
||||
Email = Identity.Claims.First(x => x.Type == "email").Value;
|
||||
Username = Identity.FindFirst(ClaimTypes.Name)!.Value;
|
||||
Email = Identity.FindFirst(ClaimTypes.Email)!.Value;
|
||||
|
||||
var sidebarItems = new List<SidebarItem>();
|
||||
|
||||
@@ -260,8 +258,9 @@
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task Logout()
|
||||
private Task Logout()
|
||||
{
|
||||
await AuthStateManager.Logout();
|
||||
Navigation.NavigateTo("/api/auth/logout", true);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
87
Moonlight.Client/UI/Partials/LoginSelector.razor
Normal file
87
Moonlight.Client/UI/Partials/LoginSelector.razor
Normal file
@@ -0,0 +1,87 @@
|
||||
@using MoonCore.Helpers
|
||||
@using Moonlight.Shared.Http.Responses.Auth
|
||||
|
||||
@inject HttpApiClient ApiClient
|
||||
@inject NavigationManager Navigation
|
||||
|
||||
<div class="flex h-screen justify-center items-center">
|
||||
<div class="sm:max-w-lg">
|
||||
<div class="w-full card card-body text-center">
|
||||
<LazyLoader EnableDefaultSpacing="false" Load="Load">
|
||||
@if (ShowSelection)
|
||||
{
|
||||
<h5 class="card-title mb-2.5">Login to MoonCore</h5>
|
||||
|
||||
<p class="mb-4">Choose a login provider to start using the app</p>
|
||||
|
||||
<div class="flex flex-col w-full mt-5 gap-y-2.5">
|
||||
@foreach (var scheme in AuthSchemes)
|
||||
{
|
||||
var config = Configs.GetValueOrDefault(scheme.Identifier);
|
||||
|
||||
if (config == null) // Ignore all schemes which have no ui configured
|
||||
continue;
|
||||
|
||||
<button @onclick="() => Start(scheme)" class="btn btn-text w-full" style="background-color: @(config.Color)">
|
||||
<img src="@config.IconUrl"
|
||||
alt="scheme icon"
|
||||
class="size-5 object-cover fill-base-content"/>
|
||||
Sign in with @scheme.DisplayName
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="flex justify-center">
|
||||
<span class="loading loading-spinner loading-xl"></span>
|
||||
</div>
|
||||
}
|
||||
</LazyLoader>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code
|
||||
{
|
||||
private AuthSchemeResponse[] AuthSchemes;
|
||||
private Dictionary<string, AuthSchemeConfig> Configs = new();
|
||||
private bool ShowSelection = false;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
Configs["LocalAuth"] = new AuthSchemeConfig()
|
||||
{
|
||||
Color = "#7636e3",
|
||||
IconUrl = "/placeholder.jpg"
|
||||
};
|
||||
}
|
||||
|
||||
private async Task Load(LazyLoader arg)
|
||||
{
|
||||
AuthSchemes = await ApiClient.GetJson<AuthSchemeResponse[]>(
|
||||
"api/auth"
|
||||
);
|
||||
|
||||
// If we only have one auth scheme available
|
||||
// we want to auto redirect the user without
|
||||
// showing the selection screen
|
||||
|
||||
if (AuthSchemes.Length == 1)
|
||||
await Start(AuthSchemes[0]);
|
||||
else
|
||||
ShowSelection = true;
|
||||
}
|
||||
|
||||
private Task Start(AuthSchemeResponse scheme)
|
||||
{
|
||||
Navigation.NavigateTo($"/api/auth/{scheme.Identifier}", true);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
record AuthSchemeConfig
|
||||
{
|
||||
public string Color { get; set; }
|
||||
public string IconUrl { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
@using MoonCore.Helpers
|
||||
@using Moonlight.Client.Implementations
|
||||
@using MoonCore.Blazor.FlyonUi.Files.Manager
|
||||
@using MoonCore.Blazor.FlyonUi.Files.Manager.Operations
|
||||
|
||||
@attribute [Authorize(Policy = "permissions:admin.system.overview")]
|
||||
|
||||
@@ -13,7 +14,8 @@
|
||||
<NavTabs Index="2" Names="UiConstants.AdminNavNames" Links="UiConstants.AdminNavLinks"/>
|
||||
</div>
|
||||
|
||||
<FileManager FsAccess="FsAccess" TransferChunkSize="TransferChunkSize" UploadLimit="UploadLimit"/>
|
||||
<FileManager OnConfigure="OnConfigure" FsAccess="FsAccess" TransferChunkSize="TransferChunkSize"
|
||||
UploadLimit="UploadLimit"/>
|
||||
|
||||
@code
|
||||
{
|
||||
@@ -21,9 +23,21 @@
|
||||
|
||||
private static readonly long TransferChunkSize = ByteConverter.FromMegaBytes(20).Bytes;
|
||||
private static readonly long UploadLimit = ByteConverter.FromGigaBytes(20).Bytes;
|
||||
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
FsAccess = new SystemFsAccess(ApiClient);
|
||||
}
|
||||
|
||||
private void OnConfigure(FileManagerOptions options)
|
||||
{
|
||||
options.AddMultiOperation<DeleteOperation>();
|
||||
options.AddMultiOperation<MoveOperation>();
|
||||
options.AddMultiOperation<DownloadOperation>();
|
||||
|
||||
options.AddSingleOperation<RenameOperation>();
|
||||
|
||||
options.AddToolbarOperation<CreateFileOperation>();
|
||||
options.AddToolbarOperation<CreateFolderOperation>();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user