1
0
mirror of https://github.com/chylex/Minecraft-Phantom-Panel.git synced 2025-04-22 10:15:48 +02:00

Remove login session management logic from Web

This commit is contained in:
chylex 2024-03-31 12:07:33 +02:00
parent d591318340
commit c99f5bc6bf
Signed by: chylex
GPG Key ID: 4DE42C8F19A80548
21 changed files with 190 additions and 210 deletions

View File

@ -0,0 +1,10 @@
using MemoryPack;
namespace Phantom.Common.Data.Web.Users;
[MemoryPackable(GenerateType.VersionTolerant)]
public sealed partial record AuthenticatedUserInfo(
[property: MemoryPackOrder(0)] Guid Guid,
[property: MemoryPackOrder(1)] string Name,
[property: MemoryPackOrder(2)] PermissionSet Permissions
);

View File

@ -5,7 +5,6 @@ namespace Phantom.Common.Data.Web.Users;
[MemoryPackable(GenerateType.VersionTolerant)]
public sealed partial record LogInSuccess(
[property: MemoryPackOrder(0)] Guid UserGuid,
[property: MemoryPackOrder(1)] PermissionSet Permissions,
[property: MemoryPackOrder(2)] ImmutableArray<byte> Token
[property: MemoryPackOrder(0)] AuthenticatedUserInfo UserInfo,
[property: MemoryPackOrder(1)] ImmutableArray<byte> Token
);

View File

@ -0,0 +1,10 @@
using MemoryPack;
namespace Phantom.Common.Data;
[MemoryPackable]
public readonly partial record struct Optional<T>(T? Value) {
public static implicit operator Optional<T>(T? value) {
return new Optional<T>(value);
}
}

View File

@ -0,0 +1,13 @@
using System.Collections.Immutable;
using MemoryPack;
using Phantom.Common.Data;
using Phantom.Common.Data.Web.Users;
using Phantom.Utils.Actor;
namespace Phantom.Common.Messages.Web.ToController;
[MemoryPackable(GenerateType.VersionTolerant)]
public sealed partial record GetAuthenticatedUser(
[property: MemoryPackOrder(0)] Guid UserGuid,
[property: MemoryPackOrder(1)] ImmutableArray<byte> SessionToken
) : IMessageToController, ICanReply<Optional<AuthenticatedUserInfo>>;

View File

@ -1,4 +1,5 @@
using System.Collections.Immutable;
using Phantom.Common.Data;
using Phantom.Common.Data.Java;
using Phantom.Common.Data.Minecraft;
using Phantom.Common.Data.Replies;
@ -25,21 +26,22 @@ public static class WebMessageRegistries {
ToController.Add<UnregisterWebMessage>(1);
ToController.Add<LogInMessage, LogInSuccess?>(2);
ToController.Add<LogOutMessage>(3);
ToController.Add<CreateOrUpdateAdministratorUserMessage, CreateOrUpdateAdministratorUserResult>(4);
ToController.Add<CreateUserMessage, CreateUserResult>(5);
ToController.Add<DeleteUserMessage, DeleteUserResult>(6);
ToController.Add<GetUsersMessage, ImmutableArray<UserInfo>>(7);
ToController.Add<GetRolesMessage, ImmutableArray<RoleInfo>>(8);
ToController.Add<GetUserRolesMessage, ImmutableDictionary<Guid, ImmutableArray<Guid>>>(9);
ToController.Add<ChangeUserRolesMessage, ChangeUserRolesResult>(10);
ToController.Add<CreateOrUpdateInstanceMessage, InstanceActionResult<CreateOrUpdateInstanceResult>>(11);
ToController.Add<LaunchInstanceMessage, InstanceActionResult<LaunchInstanceResult>>(12);
ToController.Add<StopInstanceMessage, InstanceActionResult<StopInstanceResult>>(13);
ToController.Add<SendCommandToInstanceMessage, InstanceActionResult<SendCommandToInstanceResult>>(14);
ToController.Add<GetMinecraftVersionsMessage, ImmutableArray<MinecraftVersion>>(15);
ToController.Add<GetAgentJavaRuntimesMessage, ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>>>(16);
ToController.Add<GetAuditLogMessage, ImmutableArray<AuditLogItem>>(17);
ToController.Add<GetEventLogMessage, ImmutableArray<EventLogItem>>(18);
ToController.Add<GetAuthenticatedUser, Optional<AuthenticatedUserInfo>>(4);
ToController.Add<CreateOrUpdateAdministratorUserMessage, CreateOrUpdateAdministratorUserResult>(5);
ToController.Add<CreateUserMessage, CreateUserResult>(6);
ToController.Add<DeleteUserMessage, DeleteUserResult>(7);
ToController.Add<GetUsersMessage, ImmutableArray<UserInfo>>(8);
ToController.Add<GetRolesMessage, ImmutableArray<RoleInfo>>(9);
ToController.Add<GetUserRolesMessage, ImmutableDictionary<Guid, ImmutableArray<Guid>>>(10);
ToController.Add<ChangeUserRolesMessage, ChangeUserRolesResult>(11);
ToController.Add<CreateOrUpdateInstanceMessage, InstanceActionResult<CreateOrUpdateInstanceResult>>(12);
ToController.Add<LaunchInstanceMessage, InstanceActionResult<LaunchInstanceResult>>(13);
ToController.Add<StopInstanceMessage, InstanceActionResult<StopInstanceResult>>(14);
ToController.Add<SendCommandToInstanceMessage, InstanceActionResult<SendCommandToInstanceResult>>(15);
ToController.Add<GetMinecraftVersionsMessage, ImmutableArray<MinecraftVersion>>(16);
ToController.Add<GetAgentJavaRuntimesMessage, ImmutableDictionary<Guid, ImmutableArray<TaggedJavaRuntime>>>(17);
ToController.Add<GetAuditLogMessage, ImmutableArray<AuditLogItem>>(18);
ToController.Add<GetEventLogMessage, ImmutableArray<EventLogItem>>(19);
ToController.Add<ReplyMessage>(127);
ToWeb.Add<RegisterWebResultMessage>(0);

View File

@ -1,4 +1,5 @@
using System.Collections.Immutable;
using Phantom.Common.Data;
using Phantom.Common.Data.Java;
using Phantom.Common.Data.Minecraft;
using Phantom.Common.Data.Replies;
@ -71,6 +72,7 @@ sealed class WebMessageHandlerActor : ReceiveActor<IMessageToController> {
Receive<UnregisterWebMessage>(HandleUnregisterWeb);
ReceiveAndReplyLater<LogInMessage, LogInSuccess?>(HandleLogIn);
Receive<LogOutMessage>(HandleLogOut);
ReceiveAndReply<GetAuthenticatedUser, Optional<AuthenticatedUserInfo>>(GetAuthenticatedUser);
ReceiveAndReplyLater<CreateOrUpdateAdministratorUserMessage, CreateOrUpdateAdministratorUserResult>(HandleCreateOrUpdateAdministratorUser);
ReceiveAndReplyLater<CreateUserMessage, CreateUserResult>(HandleCreateUser);
ReceiveAndReplyLater<GetUsersMessage, ImmutableArray<UserInfo>>(HandleGetUsers);
@ -104,7 +106,11 @@ sealed class WebMessageHandlerActor : ReceiveActor<IMessageToController> {
private void HandleLogOut(LogOutMessage message) {
_ = userLoginManager.LogOut(message.UserGuid, message.SessionToken);
}
private Optional<AuthenticatedUserInfo> GetAuthenticatedUser(GetAuthenticatedUser message) {
return userLoginManager.GetAuthenticatedUser(message.UserGuid, message.SessionToken);
}
private Task<CreateOrUpdateAdministratorUserResult> HandleCreateOrUpdateAdministratorUser(CreateOrUpdateAdministratorUserMessage message) {
return userManager.CreateOrUpdateAdministrator(message.Username, message.Password);
}

View File

@ -1,5 +1,6 @@
using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using Phantom.Common.Data.Web.Users;
using Phantom.Controller.Database;
@ -9,7 +10,7 @@ namespace Phantom.Controller.Services.Users;
sealed class UserLoginManager {
private const int SessionIdBytes = 20;
private readonly ConcurrentDictionary<Guid, List<ImmutableArray<byte>>> sessionTokensByUserGuid = new ();
private readonly ConcurrentDictionary<Guid, UserSession> sessionsByUserGuid = new ();
private readonly UserManager userManager;
private readonly PermissionManager permissionManager;
@ -27,11 +28,11 @@ sealed class UserLoginManager {
return null;
}
var permissions = await permissionManager.FetchPermissionsForUserId(user.UserGuid);
var userInfo = new AuthenticatedUserInfo(user.UserGuid, user.Name, permissions);
var token = ImmutableArray.Create(RandomNumberGenerator.GetBytes(SessionIdBytes));
var sessionTokens = sessionTokensByUserGuid.GetOrAdd(user.UserGuid, static _ => new List<ImmutableArray<byte>>());
lock (sessionTokens) {
sessionTokens.Add(token);
}
sessionsByUserGuid.AddOrUpdate(user.UserGuid, UserSession.Create, UserSession.Add, new NewUserSession(userInfo, token));
await using (var db = dbProvider.Lazy()) {
var auditLogWriter = new AuditLogRepository(db).Writer(user.UserGuid);
@ -40,18 +41,18 @@ sealed class UserLoginManager {
await db.Ctx.SaveChangesAsync();
}
return new LogInSuccess(user.UserGuid, await permissionManager.FetchPermissionsForUserId(user.UserGuid), token);
return new LogInSuccess(userInfo, token);
}
public async Task LogOut(Guid userGuid, ImmutableArray<byte> sessionToken) {
if (!sessionTokensByUserGuid.TryGetValue(userGuid, out var sessionTokens)) {
return;
}
lock (sessionTokens) {
if (sessionTokens.RemoveAll(token => token.SequenceEqual(sessionToken)) == 0) {
public async Task LogOut(Guid userGuid, ImmutableArray<byte> token) {
while (true) {
if (!sessionsByUserGuid.TryGetValue(userGuid, out var oldSession)) {
return;
}
if (sessionsByUserGuid.TryUpdate(userGuid, oldSession.RemoveToken(token), oldSession)) {
break;
}
}
await using var db = dbProvider.Lazy();
@ -61,4 +62,46 @@ sealed class UserLoginManager {
await db.Ctx.SaveChangesAsync();
}
public AuthenticatedUserInfo? GetAuthenticatedUser(Guid userGuid, ImmutableArray<byte> token) {
return sessionsByUserGuid.TryGetValue(userGuid, out var session) && session.Tokens.Contains(token, TokenEqualityComparer.Instance) ? session.UserInfo : null;
}
private readonly record struct NewUserSession(AuthenticatedUserInfo UserInfo, ImmutableArray<byte> Token);
private sealed record UserSession(AuthenticatedUserInfo UserInfo, ImmutableList<ImmutableArray<byte>> Tokens) {
public static UserSession Create(Guid userGuid, NewUserSession newSession) {
return new UserSession(newSession.UserInfo, ImmutableList.Create(newSession.Token));
}
public static UserSession Add(Guid userGuid, UserSession oldSession, NewUserSession newSession) {
return new UserSession(newSession.UserInfo, oldSession.Tokens.Add(newSession.Token));
}
public UserSession RemoveToken(ImmutableArray<byte> token) {
return this with { Tokens = Tokens.Remove(token, TokenEqualityComparer.Instance) };
}
public bool Equals(UserSession? other) {
return ReferenceEquals(this, other);
}
public override int GetHashCode() {
return RuntimeHelpers.GetHashCode(this);
}
}
private sealed class TokenEqualityComparer : IEqualityComparer<ImmutableArray<byte>> {
public static TokenEqualityComparer Instance { get; } = new ();
private TokenEqualityComparer() {}
public bool Equals(ImmutableArray<byte> x, ImmutableArray<byte> y) {
return x.SequenceEqual(y);
}
public int GetHashCode(ImmutableArray<byte> obj) {
throw new NotImplementedException();
}
}
}

View File

@ -2,9 +2,8 @@
using Microsoft.AspNetCore.Components.Authorization;
using Phantom.Common.Data.Web.Users;
using Phantom.Utils.Logging;
using Phantom.Web.Services.Authorization;
using Phantom.Web.Services.Authentication;
using ILogger = Serilog.ILogger;
using UserInfo = Phantom.Web.Services.Authentication.UserInfo;
namespace Phantom.Web.Components;
@ -14,21 +13,18 @@ public abstract class PhantomComponent : ComponentBase, IDisposable {
[CascadingParameter]
public Task<AuthenticationState> AuthenticationStateTask { get; set; } = null!;
[Inject]
public PermissionManager PermissionManager { get; set; } = null!;
private readonly CancellationTokenSource cancellationTokenSource = new ();
protected CancellationToken CancellationToken => cancellationTokenSource.Token;
protected async Task<Guid?> GetUserGuid() {
var authenticationState = await AuthenticationStateTask;
return UserInfo.TryGetGuid(authenticationState.User);
return authenticationState.TryGetGuid();
}
protected async Task<bool> CheckPermission(Permission permission) {
var authenticationState = await AuthenticationStateTask;
return PermissionManager.CheckPermission(authenticationState.User, permission);
return authenticationState.CheckPermission(permission);
}
protected void InvokeAsyncChecked(Func<Task> task) {

View File

@ -0,0 +1,27 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Components.Authorization;
using Phantom.Common.Data.Web.Users;
namespace Phantom.Web.Services.Authentication;
public static class AuthenticationStateExtensions {
public static Guid? TryGetGuid(this AuthenticationState authenticationState) {
return authenticationState.User is CustomClaimsPrincipal customUser ? customUser.UserInfo.Guid : null;
}
public static PermissionSet GetPermissions(this ClaimsPrincipal user) {
return user is CustomClaimsPrincipal customUser ? customUser.UserInfo.Permissions : PermissionSet.None;
}
public static bool CheckPermission(this ClaimsPrincipal user, Permission permission) {
return user.GetPermissions().Check(permission);
}
public static PermissionSet GetPermissions(this AuthenticationState authenticationState) {
return authenticationState.User.GetPermissions();
}
public static bool CheckPermission(this AuthenticationState authenticationState, Permission permission) {
return authenticationState.User.CheckPermission(permission);
}
}

View File

@ -1,26 +1,30 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Server;
using Phantom.Common.Data;
using Phantom.Common.Data.Web.Users;
using Phantom.Common.Messages.Web.ToController;
using Phantom.Web.Services.Rpc;
namespace Phantom.Web.Services.Authentication;
public sealed class CustomAuthenticationStateProvider : ServerAuthenticationStateProvider {
private readonly UserSessionManager sessionManager;
private readonly UserSessionBrowserStorage sessionBrowserStorage;
private readonly ControllerConnection controllerConnection;
private bool isLoaded;
public CustomAuthenticationStateProvider(UserSessionManager sessionManager, UserSessionBrowserStorage sessionBrowserStorage) {
this.sessionManager = sessionManager;
public CustomAuthenticationStateProvider(UserSessionBrowserStorage sessionBrowserStorage, ControllerConnection controllerConnection) {
this.sessionBrowserStorage = sessionBrowserStorage;
this.controllerConnection = controllerConnection;
}
public override async Task<AuthenticationState> GetAuthenticationStateAsync() {
if (!isLoaded) {
var stored = await sessionBrowserStorage.Get();
if (stored != null) {
var session = sessionManager.FindWithToken(stored.UserGuid, stored.Token);
if (session != null) {
SetLoadedSession(session);
var session = await controllerConnection.Send<GetAuthenticatedUser, Optional<AuthenticatedUserInfo>>(new GetAuthenticatedUser(stored.UserGuid, stored.Token), TimeSpan.FromSeconds(30));
if (session.Value is {} userInfo) {
SetLoadedSession(userInfo);
}
}
}
@ -28,9 +32,9 @@ public sealed class CustomAuthenticationStateProvider : ServerAuthenticationStat
return await base.GetAuthenticationStateAsync();
}
internal void SetLoadedSession(UserInfo user) {
internal void SetLoadedSession(AuthenticatedUserInfo user) {
isLoaded = true;
SetAuthenticationState(Task.FromResult(new AuthenticationState(user.AsClaimsPrincipal)));
SetAuthenticationState(Task.FromResult(new AuthenticationState(new CustomClaimsPrincipal(user))));
}
internal void SetUnloadedSession() {

View File

@ -0,0 +1,18 @@
using System.Security.Claims;
using Phantom.Common.Data.Web.Users;
namespace Phantom.Web.Services.Authentication;
sealed class CustomClaimsPrincipal : ClaimsPrincipal {
internal AuthenticatedUserInfo UserInfo { get; }
internal CustomClaimsPrincipal(AuthenticatedUserInfo userInfo) : base(GetIdentity(userInfo)) {
UserInfo = userInfo;
}
private static ClaimsIdentity GetIdentity(AuthenticatedUserInfo userInfo) {
var identity = new ClaimsIdentity("Phantom");
identity.AddClaim(new Claim(ClaimTypes.Name, userInfo.Name));
return identity;
}
}

View File

@ -1,23 +0,0 @@
using System.Security.Claims;
using Phantom.Common.Data.Web.Users;
namespace Phantom.Web.Services.Authentication;
public sealed record UserInfo(Guid UserGuid, string Username, PermissionSet Permissions) {
private const string AuthenticationType = "Phantom";
internal ClaimsPrincipal AsClaimsPrincipal {
get {
var identity = new ClaimsIdentity(AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, Username));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, UserGuid.ToString()));
return new ClaimsPrincipal(identity);
}
}
public static Guid? TryGetGuid(ClaimsPrincipal principal) {
return principal.Identity is { IsAuthenticated: true, AuthenticationType: AuthenticationType } && principal.FindFirstValue(ClaimTypes.NameIdentifier) is {} guidStr && Guid.TryParse(guidStr, out var guid) ? guid : null;
}
}

View File

@ -10,14 +10,12 @@ public sealed class UserLoginManager {
private static readonly ILogger Logger = PhantomLogger.Create<UserLoginManager>();
private readonly Navigation navigation;
private readonly UserSessionManager sessionManager;
private readonly UserSessionBrowserStorage sessionBrowserStorage;
private readonly CustomAuthenticationStateProvider authenticationStateProvider;
private readonly ControllerConnection controllerConnection;
public UserLoginManager(Navigation navigation, UserSessionManager sessionManager, UserSessionBrowserStorage sessionBrowserStorage, CustomAuthenticationStateProvider authenticationStateProvider, ControllerConnection controllerConnection) {
public UserLoginManager(Navigation navigation, UserSessionBrowserStorage sessionBrowserStorage, CustomAuthenticationStateProvider authenticationStateProvider, ControllerConnection controllerConnection) {
this.navigation = navigation;
this.sessionManager = sessionManager;
this.sessionBrowserStorage = sessionBrowserStorage;
this.authenticationStateProvider = authenticationStateProvider;
this.controllerConnection = controllerConnection;
@ -38,13 +36,9 @@ public sealed class UserLoginManager {
Logger.Information("Successfully logged in {Username}.", username);
var userGuid = success.UserGuid;
var userInfo = new UserInfo(userGuid, username, success.Permissions);
var token = success.Token;
var userInfo = success.UserInfo;
await sessionBrowserStorage.Store(userGuid, token);
sessionManager.Add(userInfo, token);
await sessionBrowserStorage.Store(userInfo.Guid, success.Token);
authenticationStateProvider.SetLoadedSession(userInfo);
await navigation.NavigateTo(returnUrl ?? string.Empty);
@ -53,7 +47,7 @@ public sealed class UserLoginManager {
public async Task LogOut() {
var stored = await sessionBrowserStorage.Delete();
if (stored != null && sessionManager.Remove(stored.UserGuid, stored.Token)) {
if (stored != null) {
await controllerConnection.Send(new LogOutMessage(stored.UserGuid, stored.Token));
}

View File

@ -1,35 +0,0 @@
using System.Collections.Concurrent;
using System.Collections.Immutable;
namespace Phantom.Web.Services.Authentication;
public sealed class UserSessionManager {
private readonly ConcurrentDictionary<Guid, UserSessions> userSessions = new ();
internal void Add(UserInfo user, ImmutableArray<byte> token) {
userSessions.AddOrUpdate(
user.UserGuid,
static (_, u) => new UserSessions(u),
static (_, sessions, u) => sessions.WithUserInfo(u),
user
).AddToken(token);
}
internal UserInfo? Find(Guid userGuid) {
return userSessions.TryGetValue(userGuid, out var sessions) ? sessions.UserInfo : null;
}
internal UserInfo? FindWithToken(Guid userGuid, ImmutableArray<byte> token) {
return userSessions.TryGetValue(userGuid, out var sessions) && sessions.HasToken(token) ? sessions.UserInfo : null;
}
internal bool Remove(Guid userGuid, ImmutableArray<byte> token) {
if (userSessions.TryGetValue(userGuid, out var sessions)) {
sessions.RemoveToken(token);
return true;
}
else {
return false;
}
}
}

View File

@ -1,54 +0,0 @@
using System.Collections.Immutable;
using System.Security.Cryptography;
namespace Phantom.Web.Services.Authentication;
sealed class UserSessions {
public UserInfo UserInfo { get; }
private readonly List<ImmutableArray<byte>> tokens = new ();
public UserSessions(UserInfo userInfo) {
UserInfo = userInfo;
}
private UserSessions(UserInfo userInfo, List<ImmutableArray<byte>> tokens) : this(userInfo) {
this.tokens.AddRange(tokens);
}
public UserSessions WithUserInfo(UserInfo user) {
List<ImmutableArray<byte>> tokensCopy;
lock (tokens) {
tokensCopy = new List<ImmutableArray<byte>>(tokens);
}
return new UserSessions(user, tokensCopy);
}
public void AddToken(ImmutableArray<byte> token) {
lock (tokens) {
if (!HasToken(token)) {
tokens.Add(token);
}
}
}
public bool HasToken(ImmutableArray<byte> token) {
return FindTokenIndex(token) != -1;
}
private int FindTokenIndex(ImmutableArray<byte> token) {
lock (tokens) {
return tokens.FindIndex(t => CryptographicOperations.FixedTimeEquals(t.AsSpan(), token.AsSpan()));
}
}
public void RemoveToken(ImmutableArray<byte> token) {
lock (tokens) {
int index = FindTokenIndex(token);
if (index != -1) {
tokens.RemoveAt(index);
}
}
}
}

View File

@ -1,16 +1,11 @@
using Microsoft.AspNetCore.Authorization;
using Phantom.Web.Services.Authentication;
namespace Phantom.Web.Services.Authorization;
sealed class PermissionBasedPolicyHandler : AuthorizationHandler<PermissionBasedPolicyRequirement> {
private readonly PermissionManager permissionManager;
public PermissionBasedPolicyHandler(PermissionManager permissionManager) {
this.permissionManager = permissionManager;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionBasedPolicyRequirement requirement) {
if (permissionManager.CheckPermission(context.User, requirement.Permission)) {
if (context.User.CheckPermission(requirement.Permission)) {
context.Succeed(requirement);
}
else {

View File

@ -1,22 +0,0 @@
using System.Security.Claims;
using Phantom.Common.Data.Web.Users;
using Phantom.Web.Services.Authentication;
using UserInfo = Phantom.Web.Services.Authentication.UserInfo;
namespace Phantom.Web.Services.Authorization;
public sealed class PermissionManager {
private readonly UserSessionManager sessionManager;
public PermissionManager(UserSessionManager sessionManager) {
this.sessionManager = sessionManager;
}
public PermissionSet GetPermissions(ClaimsPrincipal user) {
return UserInfo.TryGetGuid(user) is {} guid && sessionManager.Find(guid) is {} info ? info.Permissions : PermissionSet.None;
}
public bool CheckPermission(ClaimsPrincipal user, Permission permission) {
return GetPermissions(user).Check(permission);
}
}

View File

@ -1,10 +1,9 @@
@using Microsoft.AspNetCore.Components.Authorization
@using Phantom.Common.Data.Web.Users
@inject PermissionManager PermissionManager
@using Phantom.Web.Services.Authentication
<AuthorizeView>
<Authorized>
@if (ChildContent != null && PermissionManager.CheckPermission(context.User, Permission)) {
@if (ChildContent != null && context.CheckPermission(Permission)) {
@ChildContent(context)
}
</Authorized>

View File

@ -22,7 +22,6 @@ public static class PhantomWebServices {
services.AddSingleton<EventLogManager>();
services.AddSingleton<UserManager>();
services.AddSingleton<UserSessionManager>();
services.AddSingleton<AuditLogManager>();
services.AddScoped<UserLoginManager>();
services.AddScoped<UserSessionBrowserStorage>();
@ -34,7 +33,6 @@ public static class PhantomWebServices {
services.AddScoped<AuthenticationStateProvider>(static services => services.GetRequiredService<CustomAuthenticationStateProvider>());
services.AddAuthorization(ConfigureAuthorization);
services.AddSingleton<PermissionManager>();
services.AddScoped<IAuthorizationHandler, PermissionBasedPolicyHandler>();
}

View File

@ -1,8 +1,7 @@
@using Phantom.Web.Services
@using Phantom.Web.Services.Authorization
@using Phantom.Web.Services.Authentication
@using Phantom.Web.Services
@using Phantom.Common.Data.Web.Users
@inject ApplicationProperties ApplicationProperties
@inject PermissionManager PermissionManager
<div class="navbar navbar-dark">
<div class="container-fluid">
@ -24,7 +23,7 @@
<NavMenuItem Label="Login" Icon="account-login" Href="login" />
</NotAuthorized>
<Authorized>
@{ var permissions = PermissionManager.GetPermissions(context.User); }
@{ var permissions = context.GetPermissions(); }
@if (permissions.Check(Permission.ViewInstances)) {
<NavMenuItem Label="Instances" Icon="folder" Href="instances" />

View File

@ -1,9 +1,10 @@
@page "/users"
@attribute [Authorize(Permission.ViewUsersPolicy)]
@using System.Collections.Immutable
@using Phantom.Common.Data.Web.Users
@using Phantom.Web.Services.Users
@using Phantom.Web.Services.Authentication
@using Phantom.Web.Services.Authorization
@using Phantom.Web.Services.Users
@using Phantom.Common.Data.Web.Users
@inherits Phantom.Web.Components.PhantomComponent
@inject UserManager UserManager
@inject RoleManager RoleManager
@ -17,7 +18,7 @@
<AuthorizeView>
<Authorized>
@{ var canEdit = PermissionManager.CheckPermission(context.User, Permission.EditUsers); }
@{ var canEdit = context.CheckPermission(Permission.EditUsers); }
<Table TItem="UserInfo" Items="allUsers">
<HeaderRow>
<Column>Username</Column>