diff --git a/MoonlightServers.ApiServer/Database/ServersDataContext.cs b/MoonlightServers.ApiServer/Database/ServersDataContext.cs index 132df06..498852f 100644 --- a/MoonlightServers.ApiServer/Database/ServersDataContext.cs +++ b/MoonlightServers.ApiServer/Database/ServersDataContext.cs @@ -1,3 +1,4 @@ +using System.Text.Json; using Microsoft.EntityFrameworkCore; using MoonCore.Extended.SingleDb; using Moonlight.ApiServer.Configuration; @@ -40,12 +41,15 @@ public class ServersDataContext : DatabaseContext #region Shares modelBuilder.Ignore(); - + modelBuilder.Ignore(); + modelBuilder.Entity(builder => { builder.OwnsOne(x => x.Content, navigationBuilder => { navigationBuilder.ToJson(); + + navigationBuilder.OwnsMany(x => x.Permissions); }); }); diff --git a/MoonlightServers.ApiServer/Http/Controllers/Client/ServersController.cs b/MoonlightServers.ApiServer/Http/Controllers/Client/ServersController.cs index 7799ef4..12236f2 100644 --- a/MoonlightServers.ApiServer/Http/Controllers/Client/ServersController.cs +++ b/MoonlightServers.ApiServer/Http/Controllers/Client/ServersController.cs @@ -9,6 +9,7 @@ using MoonCore.Models; using Moonlight.ApiServer.Database.Entities; using MoonlightServers.ApiServer.Database.Entities; using MoonlightServers.ApiServer.Extensions; +using MoonlightServers.ApiServer.Mappers; using MoonlightServers.ApiServer.Models; using MoonlightServers.ApiServer.Services; using MoonlightServers.Shared.Constants; @@ -153,7 +154,7 @@ public class ServersController : Controller Share = new() { SharedBy = owners.First(y => y.Id == x.Server.OwnerId).Username, - Permissions = x.Content.Permissions + Permissions = ShareMapper.MapToPermissionLevels(x.Content) } }).ToArray(); @@ -223,7 +224,7 @@ public class ServersController : Controller response.Share = new() { SharedBy = owner.Username, - Permissions = authorizationResult.Share.Content.Permissions + Permissions = ShareMapper.MapToPermissionLevels(authorizationResult.Share.Content) }; } diff --git a/MoonlightServers.ApiServer/Http/Controllers/Client/SharesController.cs b/MoonlightServers.ApiServer/Http/Controllers/Client/SharesController.cs index a6ef3f7..e3a8a22 100644 --- a/MoonlightServers.ApiServer/Http/Controllers/Client/SharesController.cs +++ b/MoonlightServers.ApiServer/Http/Controllers/Client/SharesController.cs @@ -7,6 +7,8 @@ using MoonCore.Extended.Abstractions; using MoonCore.Models; using Moonlight.ApiServer.Database.Entities; using MoonlightServers.ApiServer.Database.Entities; +using MoonlightServers.ApiServer.Mappers; +using MoonlightServers.ApiServer.Models; using MoonlightServers.ApiServer.Services; using MoonlightServers.Shared.Constants; using MoonlightServers.Shared.Enums; @@ -68,7 +70,7 @@ public class SharesController : Controller { Id = x.Id, Username = users.First(y => y.Id == x.UserId).Username, - Permissions = x.Content.Permissions + Permissions = ShareMapper.MapToPermissionLevels(x.Content) }).ToArray(); return new PagedData() @@ -104,13 +106,13 @@ public class SharesController : Controller { Id = share.Id, Username = user.Username, - Permissions = share.Content.Permissions + Permissions = ShareMapper.MapToPermissionLevels(share.Content) }; return mappedItem; } - [HttpPost("")] + [HttpPost] public async Task Create( [FromRoute] int serverId, [FromBody] CreateShareRequest request @@ -128,10 +130,7 @@ public class SharesController : Controller var share = new ServerShare() { Server = server, - Content = new() - { - Permissions = request.Permissions - }, + Content = ShareMapper.MapToServerShareContent(request.Permissions), CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow, UserId = user.Id @@ -143,7 +142,7 @@ public class SharesController : Controller { Id = finalShare.Id, Username = user.Username, - Permissions = finalShare.Content.Permissions + Permissions = ShareMapper.MapToPermissionLevels(finalShare.Content) }; return mappedItem; @@ -165,7 +164,8 @@ public class SharesController : Controller if (share == null) throw new HttpApiException("A share with that id cannot be found", 404); - share.Content.Permissions = request.Permissions; + share.Content = ShareMapper.MapToServerShareContent(request.Permissions); + share.UpdatedAt = DateTime.UtcNow; await ShareRepository.Update(share); @@ -181,7 +181,7 @@ public class SharesController : Controller { Id = share.Id, Username = user.Username, - Permissions = share.Content.Permissions + Permissions = ShareMapper.MapToPermissionLevels(share.Content) }; return mappedItem; diff --git a/MoonlightServers.ApiServer/Http/Controllers/Client/VariablesController.cs b/MoonlightServers.ApiServer/Http/Controllers/Client/VariablesController.cs index d51c719..f721ed0 100644 --- a/MoonlightServers.ApiServer/Http/Controllers/Client/VariablesController.cs +++ b/MoonlightServers.ApiServer/Http/Controllers/Client/VariablesController.cs @@ -93,7 +93,7 @@ public class VariablesController : Controller }; } - [HttpPut("")] + [HttpPut] public async Task UpdateSingle( [FromRoute] int serverId, [FromBody] UpdateServerVariableRequest request @@ -123,7 +123,7 @@ public class VariablesController : Controller }; } - [HttpPatch("")] + [HttpPatch] public async Task Update( [FromRoute] int serverId, [FromBody] UpdateServerVariableRangeRequest request diff --git a/MoonlightServers.ApiServer/Implementations/ServerAuthFilters/ShareAuthFilter.cs b/MoonlightServers.ApiServer/Implementations/ServerAuthFilters/ShareAuthFilter.cs index a36471f..5dedd62 100644 --- a/MoonlightServers.ApiServer/Implementations/ServerAuthFilters/ShareAuthFilter.cs +++ b/MoonlightServers.ApiServer/Implementations/ServerAuthFilters/ShareAuthFilter.cs @@ -45,13 +45,13 @@ public class ShareAuthFilter : IServerAuthorizationFilter if (string.IsNullOrEmpty(permissionId) || requiredLevel == ServerPermissionLevel.None) return ServerAuthorizationResult.Success(share); - if ( - share.Content.Permissions.TryGetValue(permissionId, out var shareLevel) && - shareLevel >= requiredLevel - ) - { + var possiblePermShare = share.Content.Permissions.FirstOrDefault(x => x.Identifier == permissionId); + + if (possiblePermShare == null) + return null; + + if (possiblePermShare.Level >= requiredLevel) return ServerAuthorizationResult.Success(share); - } return null; } diff --git a/MoonlightServers.ApiServer/Mappers/ShareMapper.cs b/MoonlightServers.ApiServer/Mappers/ShareMapper.cs new file mode 100644 index 0000000..2bcd4bb --- /dev/null +++ b/MoonlightServers.ApiServer/Mappers/ShareMapper.cs @@ -0,0 +1,27 @@ +using MoonlightServers.ApiServer.Models; +using MoonlightServers.Shared.Enums; +using Riok.Mapperly.Abstractions; + +namespace MoonlightServers.ApiServer.Mappers; + +[Mapper] +public static partial class ShareMapper +{ + public static ServerShareContent MapToServerShareContent(Dictionary permissionLevels) + { + return new ServerShareContent() + { + Permissions = permissionLevels.Select(x => new ServerShareContent.SharePermission() + { + Identifier = x.Key, + Level = x.Value + }).ToList() + }; + } + + public static Dictionary MapToPermissionLevels( + ServerShareContent content) + { + return content.Permissions.ToDictionary(x => x.Identifier, x => x.Level); + } +} \ No newline at end of file diff --git a/MoonlightServers.ApiServer/Models/ServerShareContent.cs b/MoonlightServers.ApiServer/Models/ServerShareContent.cs index 4b6b3ba..e79035c 100644 --- a/MoonlightServers.ApiServer/Models/ServerShareContent.cs +++ b/MoonlightServers.ApiServer/Models/ServerShareContent.cs @@ -2,7 +2,13 @@ using MoonlightServers.Shared.Enums; namespace MoonlightServers.ApiServer.Models; -public class ServerShareContent +public record ServerShareContent { - public Dictionary Permissions { get; set; } = new(); + public List Permissions { get; set; } = new(); + + public record SharePermission + { + public string Identifier { get; set; } + public ServerPermissionLevel Level { get; set; } + } } \ No newline at end of file diff --git a/MoonlightServers.Frontend/Implementations/DefaultPermissionProvider.cs b/MoonlightServers.Frontend/Implementations/DefaultPermissionProvider.cs new file mode 100644 index 0000000..0942ade --- /dev/null +++ b/MoonlightServers.Frontend/Implementations/DefaultPermissionProvider.cs @@ -0,0 +1,59 @@ +using MoonlightServers.Frontend.Interfaces; +using MoonlightServers.Frontend.Models; +using MoonlightServers.Shared.Constants; +using MoonlightServers.Shared.Http.Responses.Client.Servers; + +namespace MoonlightServers.Frontend.Implementations; + +public class DefaultPermissionProvider : IServerPermissionProvider +{ + public Task GetPermissions(ServerDetailResponse server) + { + ServerPermission[] permissions = [ + new() + { + Identifier = ServerPermissionConstants.Console, + Description = "Allows access to the console", + DisplayName = "Console", + Icon = "icon-square-terminal" + }, + new() + { + Identifier = ServerPermissionConstants.Files, + Description = "Allows access to the files", + DisplayName = "Files", + Icon = "icon-folder" + }, + new() + { + Identifier = ServerPermissionConstants.Power, + Description = "Allows actions like turning off the server and starting it", + DisplayName = "Power", + Icon = "icon-power" + }, + new() + { + Identifier = ServerPermissionConstants.Settings, + Description = "Gives permissions to perform re-installs or change other general settings", + DisplayName = "Settings", + Icon = "icon-settings" + }, + new() + { + Identifier = ServerPermissionConstants.Shares, + Description = "Dangerous! This allows control to the available shares and their access level for a server", + DisplayName = "Shares", + Icon = "icon-users" + }, + new() + { + Identifier = ServerPermissionConstants.Variables, + Description = "Allows access to the server software specific settings", + DisplayName = "Variables", + Icon = "icon-variable" + } + ]; + + return Task.FromResult(permissions); + } +} \ No newline at end of file diff --git a/MoonlightServers.Frontend/Implementations/DefaultServerTabProvider.cs b/MoonlightServers.Frontend/Implementations/DefaultServerTabProvider.cs index 52731a7..5b9dca8 100644 --- a/MoonlightServers.Frontend/Implementations/DefaultServerTabProvider.cs +++ b/MoonlightServers.Frontend/Implementations/DefaultServerTabProvider.cs @@ -1,6 +1,8 @@ using MoonlightServers.Frontend.Interfaces; using MoonlightServers.Frontend.Models; using MoonlightServers.Frontend.UI.Components.Servers.ServerTabs; +using MoonlightServers.Shared.Constants; +using MoonlightServers.Shared.Enums; using MoonlightServers.Shared.Http.Responses.Client.Servers; namespace MoonlightServers.Frontend.Implementations; @@ -11,11 +13,11 @@ public class DefaultServerTabProvider : IServerTabProvider { ServerTab[] tabs = [ - ServerTab.CreateFromComponent("Console", "console", 0, permission => permission.Identifier == "console"), - ServerTab.CreateFromComponent("Files", "files", 1, permission => permission.Identifier == "files"), - ServerTab.CreateFromComponent("Shares", "shares", 2, permission => permission.Identifier == "shares"), - ServerTab.CreateFromComponent("Variables", "variables", 9, permission => permission.Identifier == "variables"), - ServerTab.CreateFromComponent("Settings", "settings", 10, permission => permission.Identifier == "settings"), + ServerTab.CreateFromComponent("Console", "console", 0, ServerPermissionConstants.Console, ServerPermissionLevel.Read), + ServerTab.CreateFromComponent("Files", "files", 1, ServerPermissionConstants.Files, ServerPermissionLevel.Read), + ServerTab.CreateFromComponent("Shares", "shares", 2, ServerPermissionConstants.Shares, ServerPermissionLevel.ReadWrite), + ServerTab.CreateFromComponent("Variables", "variables", 9, ServerPermissionConstants.Variables, ServerPermissionLevel.Read), + ServerTab.CreateFromComponent("Settings", "settings", 10, ServerPermissionConstants.Settings, ServerPermissionLevel.Read), ]; return Task.FromResult(tabs); diff --git a/MoonlightServers.Frontend/Interfaces/IServerPermissionProvider.cs b/MoonlightServers.Frontend/Interfaces/IServerPermissionProvider.cs new file mode 100644 index 0000000..6832ad6 --- /dev/null +++ b/MoonlightServers.Frontend/Interfaces/IServerPermissionProvider.cs @@ -0,0 +1,9 @@ +using MoonlightServers.Frontend.Models; +using MoonlightServers.Shared.Http.Responses.Client.Servers; + +namespace MoonlightServers.Frontend.Interfaces; + +public interface IServerPermissionProvider +{ + public Task GetPermissions(ServerDetailResponse server); +} \ No newline at end of file diff --git a/MoonlightServers.Frontend/Models/ServerPermission.cs b/MoonlightServers.Frontend/Models/ServerPermission.cs new file mode 100644 index 0000000..5290bc8 --- /dev/null +++ b/MoonlightServers.Frontend/Models/ServerPermission.cs @@ -0,0 +1,9 @@ +namespace MoonlightServers.Frontend.Models; + +public record ServerPermission +{ + public string Identifier { get; set; } + public string DisplayName { get; set; } + public string Description { get; set; } + public string Icon { get; set; } +} \ No newline at end of file diff --git a/MoonlightServers.Frontend/Services/ServerService.cs b/MoonlightServers.Frontend/Services/ServerService.cs index c093b32..ae07885 100644 --- a/MoonlightServers.Frontend/Services/ServerService.cs +++ b/MoonlightServers.Frontend/Services/ServerService.cs @@ -112,10 +112,10 @@ public class ServerService #region Variables - public async Task GetVariables(int serverId) + public async Task> GetVariables(int serverId, int page, int pageSize) { - return await HttpApiClient.GetJson( - $"api/client/servers/{serverId}/variables" + return await HttpApiClient.GetJson>( + $"api/client/servers/{serverId}/variables?page={page}&pageSize={pageSize}" ); } diff --git a/MoonlightServers.Frontend/Startup/PluginStartup.cs b/MoonlightServers.Frontend/Startup/PluginStartup.cs index f228d4f..35065b7 100644 --- a/MoonlightServers.Frontend/Startup/PluginStartup.cs +++ b/MoonlightServers.Frontend/Startup/PluginStartup.cs @@ -14,6 +14,7 @@ public class PluginStartup : IPluginStartup { builder.Services.AddSingleton(); builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.Services.AutoAddServices(); diff --git a/MoonlightServers.Frontend/UI/Components/Servers/CreateShareModal.razor b/MoonlightServers.Frontend/UI/Components/Servers/CreateShareModal.razor index 3d13f9e..839abe5 100644 --- a/MoonlightServers.Frontend/UI/Components/Servers/CreateShareModal.razor +++ b/MoonlightServers.Frontend/UI/Components/Servers/CreateShareModal.razor @@ -1,7 +1,7 @@ @using MoonCore.Blazor.FlyonUi.Components @using MoonlightServers.Shared.Enums @using MoonlightServers.Shared.Http.Requests.Client.Servers.Shares -@using MoonlightServers.Shared.Models +@using MoonlightServers.Shared.Http.Responses.Client.Servers @inherits MoonCore.Blazor.FlyonUi.Modals.Components.BaseModal @@ -14,48 +14,7 @@ -
- @foreach (var name in Names) - { - var i = Permissions.TryGetValue(name, out var permission) ? (int)permission : -1; - -
- @name - This is a long description -
- -
-
- @if (i == -1) - { - - } - else - { - - } - - @if (i == 0) - { - - } - else - { - - } - - @if (i == 1) - { - - } - else - { - - } -
-
- } -
+
@@ -65,6 +24,7 @@ @code { + [Parameter] public ServerDetailResponse Server { get; set; } [Parameter] public string Username { get; set; } [Parameter] public Func OnSubmit { get; set; } @@ -96,24 +56,14 @@ Permissions[name] = level; await InvokeAsync(StateHasChanged); } - - private async Task Reset(string name) - { - Permissions.Remove(name); - await InvokeAsync(StateHasChanged); - } - + private async Task Submit() => await HandleForm.Submit(); private async Task OnValidSubmit() { - Request.Permissions = Permissions.Select(x => new GrantedServerPermission() - { - Identifier = x.Key, - Level = x.Value - }).ToList(); - + Request.Permissions = Permissions; + await OnSubmit.Invoke(Request); await Hide(); } diff --git a/MoonlightServers.Frontend/UI/Components/Servers/PermissionEditor.razor b/MoonlightServers.Frontend/UI/Components/Servers/PermissionEditor.razor new file mode 100644 index 0000000..405a40d --- /dev/null +++ b/MoonlightServers.Frontend/UI/Components/Servers/PermissionEditor.razor @@ -0,0 +1,95 @@ +@using MoonlightServers.Frontend.Interfaces +@using MoonCore.Blazor.FlyonUi.Components +@using MoonlightServers.Frontend.Models +@using MoonlightServers.Shared.Enums +@using MoonlightServers.Shared.Http.Responses.Client.Servers + +@inject IEnumerable PermissionProviders + + +
+ @foreach (var permission in AvailablePermissions) + { + var level = PermissionLevels.GetValueOrDefault(permission.Identifier, ServerPermissionLevel.None); + +
+ +
+ @permission.DisplayName + @permission.Description +
+
+ +
+
+ @if (level == ServerPermissionLevel.None) + { + + } + else + { + + } + + @if (level == ServerPermissionLevel.Read) + { + + } + else + { + + } + + @if (level == ServerPermissionLevel.ReadWrite) + { + + } + else + { + + } +
+
+ } +
+
+ +@code +{ + [Parameter] public ServerDetailResponse Server { get; set; } + [Parameter] public Dictionary PermissionLevels { get; set; } + + private ServerPermission[] AvailablePermissions; + + private async Task Load(LazyLoader _) + { + var permissions = new List(); + + foreach (var provider in PermissionProviders) + { + permissions.AddRange( + await provider.GetPermissions(Server) + ); + } + + AvailablePermissions = permissions.ToArray(); + } + + private async Task Set(string name, ServerPermissionLevel level) + { + PermissionLevels[name] = level; + await InvokeAsync(StateHasChanged); + } +} diff --git a/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/SharesTab.razor b/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/SharesTab.razor index 75abc00..88dc272 100644 --- a/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/SharesTab.razor +++ b/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/SharesTab.razor @@ -75,6 +75,7 @@ await ModalService.Launch(parameters => { parameters["Username"] = UsernameInput; + parameters["Server"] = Server; parameters["OnSubmit"] = SubmitCreate; }, size: "max-w-2xl"); } @@ -92,6 +93,7 @@ await ModalService.Launch(parameters => { parameters["Share"] = share; + parameters["Server"] = Server; parameters["OnSubmit"] = (UpdateShareRequest request) => SubmitUpdate(share.Id, request); }, size: "max-w-2xl"); } diff --git a/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/VariablesTab.razor b/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/VariablesTab.razor index ba46a08..842191b 100644 --- a/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/VariablesTab.razor +++ b/MoonlightServers.Frontend/UI/Components/Servers/ServerTabs/VariablesTab.razor @@ -1,5 +1,6 @@ @using MoonCore.Blazor.FlyonUi.Components @using MoonCore.Blazor.FlyonUi.Toasts +@using MoonCore.Models @using MoonlightServers.Frontend.Services @using MoonlightServers.Shared.Http.Responses.Client.Servers.Variables @@ -8,7 +9,7 @@ @inject ServerService ServerService @inject ToastService ToastService - +
@foreach (var variable in Variables) { @@ -37,10 +38,13 @@ @code { private ServerVariableDetailResponse[] Variables; + private LazyLoader LazyLoader; private async Task Load(LazyLoader _) { - Variables = await ServerService.GetVariables(Server.Id); + Variables = await PagedData.All(async (page, pageSize) + => await ServerService.GetVariables(Server.Id, page, pageSize) + ); } private async Task UpdateVariable(ServerVariableDetailResponse variable, ChangeEventArgs args) @@ -52,11 +56,10 @@ Key = variable.Key, Value = value }); - + // Fetch the current data to make sure the user sees the latest data - Variables = await ServerService.GetVariables(Server.Id); - await InvokeAsync(StateHasChanged); - + await LazyLoader.Reload(); + await ToastService.Success("Successfully updated variable"); } } \ No newline at end of file diff --git a/MoonlightServers.Frontend/UI/Components/Servers/UpdateShareModal.razor b/MoonlightServers.Frontend/UI/Components/Servers/UpdateShareModal.razor index 799d3e9..a080f7d 100644 --- a/MoonlightServers.Frontend/UI/Components/Servers/UpdateShareModal.razor +++ b/MoonlightServers.Frontend/UI/Components/Servers/UpdateShareModal.razor @@ -1,6 +1,7 @@ @using MoonCore.Blazor.FlyonUi.Components @using MoonlightServers.Shared.Enums @using MoonlightServers.Shared.Http.Requests.Client.Servers.Shares +@using MoonlightServers.Shared.Http.Responses.Client.Servers @using MoonlightServers.Shared.Http.Responses.Client.Servers.Shares @using MoonlightServers.Shared.Models @@ -11,48 +12,7 @@
-
- @foreach (var name in Names) - { - var i = Permissions.TryGetValue(name, out var permission) ? (int)permission : -1; - -
- @name - This is a long description -
- -
-
- @if (i == -1) - { - - } - else - { - - } - - @if (i == 0) - { - - } - else - { - - } - - @if (i == 1) - { - - } - else - { - - } -
-
- } -
+
@@ -62,6 +22,7 @@ @code { + [Parameter] public ServerDetailResponse Server { get; set; } [Parameter] public ServerShareResponse Share { get; set; } [Parameter] public Func OnSubmit { get; set; } @@ -83,8 +44,8 @@ protected override void OnInitialized() { Request = new(); - - Permissions = Share.Permissions.ToDictionary(x => x.Identifier, x => x.Level); + + Permissions = Share.Permissions; } private async Task Set(string name, ServerPermissionLevel level) @@ -93,22 +54,12 @@ await InvokeAsync(StateHasChanged); } - private async Task Reset(string name) - { - Permissions.Remove(name); - await InvokeAsync(StateHasChanged); - } - private async Task Submit() => await HandleForm.Submit(); private async Task OnValidSubmit() { - Request.Permissions = Permissions.Select(x => new GrantedServerPermission() - { - Identifier = x.Key, - Level = x.Value - }).ToList(); + Request.Permissions = Permissions; await OnSubmit.Invoke(Request); await Hide(); diff --git a/MoonlightServers.Frontend/UI/Views/Client/Manage.razor b/MoonlightServers.Frontend/UI/Views/Client/Manage.razor index 8ae64e2..6679cb1 100644 --- a/MoonlightServers.Frontend/UI/Views/Client/Manage.razor +++ b/MoonlightServers.Frontend/UI/Views/Client/Manage.razor @@ -223,12 +223,12 @@ if (string.IsNullOrEmpty(tab.PermissionId) || tab.PermissionLevel == ServerPermissionLevel.None) return false; - // If permission is required but not set, we dont have access to it + // If permission is required but not set, we don't have access to it if (!Server.Share.Permissions.TryGetValue(tab.PermissionId, out var level)) return true; - // True if the acquired level is higher or equal than the required permission level for the tba - return level >= tab.PermissionLevel; + // False if the acquired level is higher or equal than the required permission level for the tab so it won't get removed + return level < tab.PermissionLevel; }); }