diff --git a/LANCommander.SDK/Client.cs b/LANCommander.SDK/Client.cs index 8100d0f6..03bdf824 100644 --- a/LANCommander.SDK/Client.cs +++ b/LANCommander.SDK/Client.cs @@ -27,6 +27,7 @@ namespace LANCommander.SDK private AuthToken Token; private bool Connected = false; + private bool IgnoreVersion = false; public Uri BaseUrl; public string DefaultInstallDirectory; @@ -44,6 +45,7 @@ namespace LANCommander.SDK public readonly LobbyService Lobbies; public readonly ServerService Servers; public readonly PlaySessionService PlaySessions; + public readonly TagService Tags; private Settings _Settings { get; set; } public Settings Settings @@ -79,6 +81,7 @@ namespace LANCommander.SDK Lobbies = new LobbyService(this); Servers = new ServerService(this); PlaySessions = new PlaySessionService(this); + Tags = new TagService(this); BaseCmdlet.Client = this; @@ -116,6 +119,7 @@ namespace LANCommander.SDK Lobbies = new LobbyService(this, logger); Servers = new ServerService(this, logger); PlaySessions = new PlaySessionService(this, logger); + Tags = new TagService(this, logger); BaseCmdlet.Client = this; @@ -142,6 +146,9 @@ namespace LANCommander.SDK Lobbies = new LobbyService(this); Servers = new ServerService(this); PlaySessions = new PlaySessionService(this); + Tags = new TagService(this); + + IgnoreVersion = true; BaseCmdlet.Client = this; } @@ -202,7 +209,7 @@ namespace LANCommander.SDK .AddHeader("Authorization", $"Bearer {Token.AccessToken}") .AddHeader("X-API-Version", GetCurrentVersion().ToString()); - if (!ignoreVersion) + if (!ignoreVersion && !IgnoreVersion) request.Interceptors = new List() { new VersionInterceptor() }; var response = ApiClient.Post(request); @@ -228,7 +235,7 @@ namespace LANCommander.SDK .AddHeader("Authorization", $"Bearer {Token.AccessToken}") .AddHeader("X-API-Version", GetCurrentVersion().ToString()); - if (!ignoreVersion) + if (!ignoreVersion && !IgnoreVersion) request.Interceptors = new List() { new VersionInterceptor() }; var response = ApiClient.Post(request); @@ -255,7 +262,7 @@ namespace LANCommander.SDK .AddHeader("Authorization", $"Bearer {Token.AccessToken}") .AddHeader("X-API-Version", GetCurrentVersion().ToString()); - if (!ignoreVersion) + if (!ignoreVersion && !IgnoreVersion) request.Interceptors = new List() { new VersionInterceptor() }; var response = await ApiClient.PostAsync(request); @@ -281,7 +288,7 @@ namespace LANCommander.SDK .AddHeader("Authorization", $"Bearer {Token.AccessToken}") .AddHeader("X-API-Version", GetCurrentVersion().ToString()); - if (!ignoreVersion) + if (!ignoreVersion && !IgnoreVersion) request.Interceptors = new List() { new VersionInterceptor() }; var response = await ApiClient.PostAsync(request); @@ -307,7 +314,7 @@ namespace LANCommander.SDK .AddHeader("Authorization", $"Bearer {Token.AccessToken}") .AddHeader("X-API-Version", GetCurrentVersion().ToString()); - if (!ignoreVersion) + if (!ignoreVersion && !IgnoreVersion) request.Interceptors = new List() { new VersionInterceptor() }; var response = await ApiClient.PutAsync(request); @@ -333,7 +340,7 @@ namespace LANCommander.SDK .AddHeader("Authorization", $"Bearer {Token.AccessToken}") .AddHeader("X-API-Version", GetCurrentVersion().ToString()); - if (!ignoreVersion) + if (!ignoreVersion && !IgnoreVersion) request.Interceptors = new List() { new VersionInterceptor() }; var response = ApiClient.Get(request); @@ -359,7 +366,7 @@ namespace LANCommander.SDK .AddHeader("Authorization", $"Bearer {Token.AccessToken}") .AddHeader("X-API-Version", GetCurrentVersion().ToString()); - if (!ignoreVersion) + if (!ignoreVersion && !IgnoreVersion) request.Interceptors = new List() { new VersionInterceptor() }; var response = await ApiClient.GetAsync(request); @@ -385,7 +392,7 @@ namespace LANCommander.SDK .AddHeader("Authorization", $"Bearer {Token.AccessToken}") .AddHeader("X-API-Version", GetCurrentVersion().ToString()); - if (!ignoreVersion) + if (!ignoreVersion && !IgnoreVersion) request.Interceptors = new List() { new VersionInterceptor() }; var response = await ApiClient.DeleteAsync(request); @@ -496,7 +503,7 @@ namespace LANCommander.SDK .AddHeader("Authorization", $"Bearer {Token.AccessToken}") .AddHeader("X-API-Version", GetCurrentVersion().ToString()); - if (!ignoreVersion) + if (!ignoreVersion && !IgnoreVersion) request.Interceptors = new List() { new VersionInterceptor() }; request.AddFile(fileName, data, fileName); @@ -521,7 +528,7 @@ namespace LANCommander.SDK .AddHeader("Authorization", $"Bearer {Token.AccessToken}") .AddHeader("X-API-Version", GetCurrentVersion().ToString()); - if (!ignoreVersion) + if (!ignoreVersion && !IgnoreVersion) request.Interceptors = new List() { new VersionInterceptor() }; request.AddFile(fileName, data, fileName); @@ -594,7 +601,7 @@ namespace LANCommander.SDK Password = password }); - if (!ignoreVersion) + if (!ignoreVersion && !IgnoreVersion) request.Interceptors = new List() { new VersionInterceptor() }; var response = await ApiClient.ExecuteAsync(request); @@ -778,7 +785,7 @@ namespace LANCommander.SDK .AddHeader("Authorization", $"Bearer {Token.AccessToken}") .AddHeader("X-API-Version", GetCurrentVersion().ToString()); - if (!ignoreVersion) + if (!ignoreVersion && !IgnoreVersion) request.Interceptors = new List() { new VersionInterceptor() }; if (String.IsNullOrEmpty(token.AccessToken) || String.IsNullOrEmpty(token.RefreshToken)) @@ -829,7 +836,7 @@ namespace LANCommander.SDK .AddHeader("Authorization", $"Bearer {token.AccessToken}") .AddHeader("X-API-Version", GetCurrentVersion().ToString()); - if (!ignoreVersion) + if (!ignoreVersion && !IgnoreVersion) request.Interceptors = new List() { new VersionInterceptor() }; if (String.IsNullOrEmpty(token.AccessToken) || String.IsNullOrEmpty(token.RefreshToken)) diff --git a/LANCommander.SDK/Services/TagService.cs b/LANCommander.SDK/Services/TagService.cs new file mode 100644 index 00000000..cd3a3607 --- /dev/null +++ b/LANCommander.SDK/Services/TagService.cs @@ -0,0 +1,58 @@ +using Force.Crc32; +using LANCommander.SDK.Extensions; +using LANCommander.SDK.Helpers; +using LANCommander.SDK.Models; +using LANCommander.SDK.PowerShell; +using Microsoft.Extensions.Logging; +using RestSharp; +using SharpCompress.Archives; +using SharpCompress.Archives.Zip; +using SharpCompress.Common; +using SharpCompress.Readers; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace LANCommander.SDK.Services +{ + public class TagService + { + private readonly ILogger Logger; + + private readonly Client Client; + + public TagService(Client client) + { + Client = client; + } + + public TagService(Client client, ILogger logger) + { + Client = client; + Logger = logger; + } + + public async Task CreateAsync(Tag tag) + { + return await Client.PostRequestAsync("/api/Tags", tag); + } + + public async Task UpdateAsync(Tag tag) + { + return await Client.PostRequestAsync($"/api/Tags/{tag.Id}", tag); + } + + public async Task DeleteAsync(Tag tag) + { + await Client.DeleteRequestAsync($"/api/Tags/{tag.Id}"); + } + } +} diff --git a/LANCommander.Server.Services/Mappers/MappingProfile.cs b/LANCommander.Server.Services/Mappers/MappingProfile.cs index ece31241..2404679d 100644 --- a/LANCommander.Server.Services/Mappers/MappingProfile.cs +++ b/LANCommander.Server.Services/Mappers/MappingProfile.cs @@ -26,7 +26,7 @@ namespace LANCommander.Server CreateMap(); CreateMap(); CreateMap(); - CreateMap(); + CreateMap().ReverseMap(); CreateMap(); CreateMap(); diff --git a/LANCommander.Server.Tests/Data/VersioningTests.cs b/LANCommander.Server.Tests/Data/VersioningTests.cs new file mode 100644 index 00000000..129e3c39 --- /dev/null +++ b/LANCommander.Server.Tests/Data/VersioningTests.cs @@ -0,0 +1,34 @@ +using Shouldly; +using LANCommander.Server.Services; + +namespace LANCommander.Server.Tests.Data; + +[Collection("Application")] +public class VersioningTests(ApplicationFixture fixture) : BaseTest(fixture) +{ + [Fact] + public async Task CreatedByShouldWork() + { + // Simple service that's not bound to change much + var tagService = GetService(); + + var user = await EnsureAdminUserCreatedAsync(); + + await Client.AuthenticateAsync(TestConstants.AdminUserName, TestConstants.AdminInitialPassword); + + var response = await Client.Tags.CreateAsync(new SDK.Models.Tag + { + Name = "Test Tag", + }); + + var tag = await tagService + .Include(t => t.CreatedBy) + .GetAsync(response.Id); + + tag.Name.ShouldBe("Test Tag"); + tag.CreatedById.ShouldBe(user.Id); + tag.CreatedBy.ShouldNotBeNull(); + tag.CreatedBy.UserName.ShouldBe(user.UserName); + tag.CreatedBy.Id.ShouldBe(user.Id); + } +} \ No newline at end of file diff --git a/LANCommander.Server/Endpoints/TagEndpoints.cs b/LANCommander.Server/Endpoints/TagEndpoints.cs new file mode 100644 index 00000000..ef8f5739 --- /dev/null +++ b/LANCommander.Server/Endpoints/TagEndpoints.cs @@ -0,0 +1,64 @@ +using AutoMapper; +using LANCommander.Server.Services; +using Microsoft.AspNetCore.Mvc; + +namespace LANCommander.Server.Endpoints; + +public static class TagEndpoints +{ + public static void MapTagEndpoints(this IEndpointRouteBuilder routes) + { + var group = routes.MapGroup("/api/Tags"); + + group.MapPost("/", CreateAsync) + .RequireAuthorization("Administrator"); + + group.MapPost("/{id:guid}", UpdateAsync) + .RequireAuthorization("Administrator"); + + group.MapDelete("/{id:guid}", DeleteAsync) + .RequireAuthorization("Administrator"); + } + + internal static async Task CreateAsync( + [FromBody] SDK.Models.Tag tag, + [FromServices] TagService tagService, + [FromServices] IMapper mapper) + { + var entity = await tagService.AddAsync(mapper.Map(tag)); + + return TypedResults.Ok(entity); + } + + internal static async Task UpdateAsync( + Guid id, + [FromBody] SDK.Models.Tag tag, + [FromServices] TagService tagService, + [FromServices] IMapper mapper) + { + var existing = await tagService.GetAsync(id); + + if (existing == null) + return TypedResults.NotFound(); + + tag.Id = existing.Id; + + existing = await tagService.UpdateAsync(mapper.Map(tag)); + + return TypedResults.Ok(existing); + } + + internal static async Task DeleteAsync( + [FromBody] SDK.Models.Tag tag, + [FromServices] TagService tagService) + { + var existing = await tagService.GetAsync(tag.Id); + + if (existing == null) + return TypedResults.NotFound(); + + await tagService.DeleteAsync(existing); + + return TypedResults.NoContent(); + } +} \ No newline at end of file diff --git a/LANCommander.Server/Startup/Endpoints.cs b/LANCommander.Server/Startup/Endpoints.cs index 28931b19..e2dbd59b 100644 --- a/LANCommander.Server/Startup/Endpoints.cs +++ b/LANCommander.Server/Startup/Endpoints.cs @@ -25,6 +25,7 @@ public static class Endpoints { endpoints.MapDownloadEndpoints(); endpoints.MapSaveEndpoints(); + endpoints.MapTagEndpoints(); endpoints.MapControllers(); endpoints.MapFallbackToPage("/_Host"); }); diff --git a/LANCommander.Server/Startup/Identity.cs b/LANCommander.Server/Startup/Identity.cs index 7868a92d..81ca09c8 100644 --- a/LANCommander.Server/Startup/Identity.cs +++ b/LANCommander.Server/Startup/Identity.cs @@ -52,6 +52,11 @@ public static class Identity }) .AddAuthenticationProviders(settings) .AddIdentityCookies(); + + builder.Services.AddAuthorizationBuilder() + .AddPolicy("Administrator", policy => + policy + .RequireRole("Administrator")); builder.Services.Configure(options => {