Settings - Added account management

This commit is contained in:
RobertBeekman committed 2024-02-08 20:58:16 +01:00
1 parent 3952a268d1
commit a2fe5d5d08
42 files changed
+681 -30

No files matched your search

@@ -33,6 +33,11 @@ public static class ContainerExtensions
.AddWorkshopClient()
.AddHttpMessageHandler<WorkshopClientStoreAccessor, AuthenticationDelegatingHandler>()
.ConfigureHttpClient(client => client.BaseAddress = new Uri(WorkshopConstants.WORKSHOP_URL + "/graphql"));
serviceCollection.AddHttpClient(WorkshopConstants.IDENTITY_CLIENT_NAME)
.AddHttpMessageHandler<AuthenticationDelegatingHandler>()
.ConfigureHttpClient(client => client.BaseAddress = new Uri(WorkshopConstants.AUTHORITY_URL));
serviceCollection.AddHttpClient(WorkshopConstants.WORKSHOP_CLIENT_NAME)
.AddHttpMessageHandler<AuthenticationDelegatingHandler>()
.ConfigureHttpClient(client => client.BaseAddress = new Uri(WorkshopConstants.WORKSHOP_URL));
@@ -49,7 +54,8 @@ public static class ContainerExtensions
container.Register<IAuthenticationService, AuthenticationService>(Reuse.Singleton);
container.Register<IWorkshopService, WorkshopService>(Reuse.Singleton);
container.Register<ILayoutProvider, WorkshopLayoutProvider>(Reuse.Singleton);
container.Register<IUserManagementService, UserManagementService>();
container.Register<EntryUploadHandlerFactory>(Reuse.Transient);
container.RegisterMany(workshopAssembly, type => type.IsAssignableTo<IEntryUploadHandler>(), Reuse.Transient);
container.RegisterMany(workshopAssembly, type => type.IsAssignableTo<IEntryInstallationHandler>(), Reuse.Transient);
@@ -1,4 +1,5 @@
using Artemis.WebClient.Workshop.Services;
using Artemis.WebClient.Workshop.Models;
using Artemis.WebClient.Workshop.Services;
namespace Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
@@ -1,4 +1,5 @@
using Artemis.UI.Shared.Utilities;
using Artemis.WebClient.Workshop.Models;
using Artemis.WebClient.Workshop.Services;
namespace Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
@@ -4,6 +4,7 @@ using Artemis.Core.Providers;
using Artemis.Core.Services;
using Artemis.UI.Shared.Extensions;
using Artemis.UI.Shared.Utilities;
using Artemis.WebClient.Workshop.Models;
using Artemis.WebClient.Workshop.Providers;
using Artemis.WebClient.Workshop.Services;
@@ -2,6 +2,7 @@
using Artemis.Core.Services;
using Artemis.UI.Shared.Extensions;
using Artemis.UI.Shared.Utilities;
using Artemis.WebClient.Workshop.Models;
using Artemis.WebClient.Workshop.Services;
namespace Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
@@ -1,21 +1,21 @@
namespace Artemis.WebClient.Workshop.Handlers.UploadHandlers;
public class ImageUploadResult
public class ApiResult
{
public bool IsSuccess { get; set; }
public string? Message { get; set; }
public static ImageUploadResult FromFailure(string? message)
public static ApiResult FromFailure(string? message)
{
return new ImageUploadResult
return new ApiResult
{
IsSuccess = false,
Message = message
};
}
public static ImageUploadResult FromSuccess()
public static ApiResult FromSuccess()
{
return new ImageUploadResult {IsSuccess = true};
return new ApiResult {IsSuccess = true};
}
}
@@ -1,8 +1,7 @@
using Artemis.Core;
using Artemis.WebClient.Workshop.Exceptions;
using IdentityModel.Client;
namespace Artemis.WebClient.Workshop.Services;
namespace Artemis.WebClient.Workshop.Models;
internal class AuthenticationToken
{
@@ -2,7 +2,7 @@
using Artemis.Core;
using Artemis.Storage.Entities.Workshop;
namespace Artemis.WebClient.Workshop.Services;
namespace Artemis.WebClient.Workshop.Models;
public class InstalledEntry
{
@@ -0,0 +1,3 @@
namespace Artemis.WebClient.Workshop.Models;
public record PersonalAccessToken(string Key, DateTime CreationTime, DateTime? Expiration, string? Description);
@@ -1,5 +1,6 @@
using Artemis.Core;
using Artemis.Core.Providers;
using Artemis.WebClient.Workshop.Models;
using Artemis.WebClient.Workshop.Services;
namespace Artemis.WebClient.Workshop.Providers;
@@ -8,6 +8,7 @@ using System.Security.Cryptography;
using System.Text;
using Artemis.Core;
using Artemis.WebClient.Workshop.Exceptions;
using Artemis.WebClient.Workshop.Models;
using Artemis.WebClient.Workshop.Repositories;
using DynamicData;
using IdentityModel;
@@ -0,0 +1,16 @@
using Artemis.Core.Services;
using Artemis.WebClient.Workshop.Handlers.UploadHandlers;
using Artemis.WebClient.Workshop.Models;
namespace Artemis.WebClient.Workshop.Services;
public interface IUserManagementService : IProtectedArtemisService
{
Task<ApiResult> ChangePassword(string currentPassword, string newPassword, CancellationToken cancellationToken);
Task<ApiResult> ChangeEmailAddress(string emailAddress, CancellationToken cancellationToken);
Task<ApiResult> ChangeAvatar(Stream avatar, CancellationToken cancellationToken);
Task<ApiResult> RemoveAccount(CancellationToken cancellationToken);
Task<string> CreatePersonAccessToken(string description, DateTimeOffset expirationDate, CancellationToken cancellationToken);
Task<ApiResult> DeletePersonAccessToken(PersonalAccessToken personalAccessToken, CancellationToken cancellationToken);
Task<List<PersonalAccessToken>> GetPersonAccessTokens(CancellationToken cancellationToken);
}
@@ -1,12 +1,13 @@
using Artemis.WebClient.Workshop.Handlers.UploadHandlers;
using Artemis.WebClient.Workshop.Models;
namespace Artemis.WebClient.Workshop.Services;
public interface IWorkshopService
{
Task<Stream?> GetEntryIcon(long entryId, CancellationToken cancellationToken);
Task<ImageUploadResult> SetEntryIcon(long entryId, Stream icon, CancellationToken cancellationToken);
Task<ImageUploadResult> UploadEntryImage(long entryId, ImageUploadRequest request, CancellationToken cancellationToken);
Task<ApiResult> SetEntryIcon(long entryId, Stream icon, CancellationToken cancellationToken);
Task<ApiResult> UploadEntryImage(long entryId, ImageUploadRequest request, CancellationToken cancellationToken);
Task DeleteEntryImage(Guid id, CancellationToken cancellationToken);
Task<WorkshopStatus> GetWorkshopStatus(CancellationToken cancellationToken);
Task<bool> ValidateWorkshopStatus(CancellationToken cancellationToken);
@@ -0,0 +1,103 @@
using System.Net.Http.Headers;
using System.Net.Http.Json;
using Artemis.WebClient.Workshop.Exceptions;
using Artemis.WebClient.Workshop.Handlers.UploadHandlers;
using Artemis.WebClient.Workshop.Models;
namespace Artemis.WebClient.Workshop.Services;
internal class UserManagementService : IUserManagementService
{
private readonly IHttpClientFactory _httpClientFactory;
public UserManagementService(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
/// <inheritdoc />
public async Task<ApiResult> ChangePassword(string currentPassword, string newPassword, CancellationToken cancellationToken)
{
HttpClient client = _httpClientFactory.CreateClient(WorkshopConstants.IDENTITY_CLIENT_NAME);
HttpResponseMessage response = await client.PostAsync("user/credentials", JsonContent.Create(new {CurrentPassword = currentPassword, NewPassword = newPassword}), cancellationToken);
return response.IsSuccessStatusCode
? ApiResult.FromSuccess()
: ApiResult.FromFailure(await response.Content.ReadAsStringAsync(cancellationToken));
}
/// <inheritdoc />
public async Task<ApiResult> ChangeEmailAddress(string emailAddress, CancellationToken cancellationToken)
{
HttpClient client = _httpClientFactory.CreateClient(WorkshopConstants.IDENTITY_CLIENT_NAME);
HttpResponseMessage response = await client.PostAsync("user/email", JsonContent.Create(new {EmailAddress = emailAddress}), cancellationToken);
return response.IsSuccessStatusCode
? ApiResult.FromSuccess()
: ApiResult.FromFailure(await response.Content.ReadAsStringAsync(cancellationToken));
}
public async Task<ApiResult> ChangeAvatar(Stream avatar, CancellationToken cancellationToken)
{
avatar.Seek(0, SeekOrigin.Begin);
// Submit the archive
HttpClient client = _httpClientFactory.CreateClient(WorkshopConstants.IDENTITY_CLIENT_NAME);
// Construct the request
MultipartFormDataContent content = new();
StreamContent streamContent = new(avatar);
streamContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
content.Add(streamContent, "file", "file.png");
// Submit
HttpResponseMessage response = await client.PutAsync($"user/avatar", content, cancellationToken);
return response.IsSuccessStatusCode
? ApiResult.FromSuccess()
: ApiResult.FromFailure(await response.Content.ReadAsStringAsync(cancellationToken));
}
/// <inheritdoc />
public async Task<ApiResult> RemoveAccount(CancellationToken cancellationToken)
{
HttpClient client = _httpClientFactory.CreateClient(WorkshopConstants.IDENTITY_CLIENT_NAME);
HttpResponseMessage response = await client.DeleteAsync("user", cancellationToken);
return response.IsSuccessStatusCode
? ApiResult.FromSuccess()
: ApiResult.FromFailure(await response.Content.ReadAsStringAsync(cancellationToken));
}
/// <inheritdoc />
public async Task<string> CreatePersonAccessToken(string description, DateTimeOffset expirationDate, CancellationToken cancellationToken)
{
HttpClient client = _httpClientFactory.CreateClient(WorkshopConstants.IDENTITY_CLIENT_NAME);
HttpResponseMessage response = await client.PostAsync("user/access-token", JsonContent.Create(new {Description = description, ExpirationDate = expirationDate}), cancellationToken);
response.EnsureSuccessStatusCode();
string? result = await response.Content.ReadFromJsonAsync<string>(cancellationToken: cancellationToken);
if (result == null)
throw new ArtemisWebClientException("Failed to deserialize access token");
return result;
}
/// <inheritdoc />
public async Task<ApiResult> DeletePersonAccessToken(PersonalAccessToken personalAccessToken, CancellationToken cancellationToken)
{
HttpClient client = _httpClientFactory.CreateClient(WorkshopConstants.IDENTITY_CLIENT_NAME);
HttpResponseMessage response = await client.DeleteAsync($"user/access-token/{personalAccessToken.Key}", cancellationToken);
return response.IsSuccessStatusCode
? ApiResult.FromSuccess()
: ApiResult.FromFailure(await response.Content.ReadAsStringAsync(cancellationToken));
}
/// <inheritdoc />
public async Task<List<PersonalAccessToken>> GetPersonAccessTokens(CancellationToken cancellationToken)
{
HttpClient client = _httpClientFactory.CreateClient(WorkshopConstants.IDENTITY_CLIENT_NAME);
HttpResponseMessage response = await client.GetAsync("user/access-token", cancellationToken);
response.EnsureSuccessStatusCode();
List<PersonalAccessToken>? result = await response.Content.ReadFromJsonAsync<List<PersonalAccessToken>>(cancellationToken: cancellationToken);
if (result == null)
throw new ArtemisWebClientException("Failed to deserialize access tokens");
return result;
}
}
@@ -2,8 +2,8 @@ using System.Net.Http.Headers;
using Artemis.Storage.Entities.Workshop;
using Artemis.Storage.Repositories.Interfaces;
using Artemis.UI.Shared.Routing;
using Artemis.UI.Shared.Utilities;
using Artemis.WebClient.Workshop.Handlers.UploadHandlers;
using Artemis.WebClient.Workshop.Models;
namespace Artemis.WebClient.Workshop.Services;
@@ -36,7 +36,7 @@ public class WorkshopService : IWorkshopService
}
}
public async Task<ImageUploadResult> SetEntryIcon(long entryId, Stream icon, CancellationToken cancellationToken)
public async Task<ApiResult> SetEntryIcon(long entryId, Stream icon, CancellationToken cancellationToken)
{
icon.Seek(0, SeekOrigin.Begin);
@@ -52,12 +52,12 @@ public class WorkshopService : IWorkshopService
// Submit
HttpResponseMessage response = await client.PostAsync($"entries/{entryId}/icon", content, cancellationToken);
if (!response.IsSuccessStatusCode)
return ImageUploadResult.FromFailure($"{response.StatusCode} - {await response.Content.ReadAsStringAsync(cancellationToken)}");
return ImageUploadResult.FromSuccess();
return ApiResult.FromFailure($"{response.StatusCode} - {await response.Content.ReadAsStringAsync(cancellationToken)}");
return ApiResult.FromSuccess();
}
/// <inheritdoc />
public async Task<ImageUploadResult> UploadEntryImage(long entryId, ImageUploadRequest request, CancellationToken cancellationToken)
public async Task<ApiResult> UploadEntryImage(long entryId, ImageUploadRequest request, CancellationToken cancellationToken)
{
request.File.Seek(0, SeekOrigin.Begin);
@@ -76,8 +76,8 @@ public class WorkshopService : IWorkshopService
// Submit
HttpResponseMessage response = await client.PostAsync($"entries/{entryId}/image", content, cancellationToken);
if (!response.IsSuccessStatusCode)
return ImageUploadResult.FromFailure($"{response.StatusCode} - {await response.Content.ReadAsStringAsync(cancellationToken)}");
return ImageUploadResult.FromSuccess();
return ApiResult.FromFailure($"{response.StatusCode} - {await response.Content.ReadAsStringAsync(cancellationToken)}");
return ApiResult.FromSuccess();
}
/// <inheritdoc />
@@ -6,5 +6,6 @@ public static class WorkshopConstants
// public const string WORKSHOP_URL = "https://localhost:7281";
public const string AUTHORITY_URL = "https://identity.artemis-rgb.com";
public const string WORKSHOP_URL = "https://workshop.artemis-rgb.com";
public const string IDENTITY_CLIENT_NAME = "IdentityApiClient";
public const string WORKSHOP_CLIENT_NAME = "WorkshopApiClient";
}