diff --git a/NetEvent/Client/Pages/Administration/AdminMenu.razor.cs b/NetEvent/Client/Pages/Administration/AdminMenu.razor.cs index ffc9524c..3167f487 100644 --- a/NetEvent/Client/Pages/Administration/AdminMenu.razor.cs +++ b/NetEvent/Client/Pages/Administration/AdminMenu.razor.cs @@ -1,14 +1,11 @@ -using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components; using MudBlazor.ThemeManager; namespace NetEvent.Client.Pages.Administration { public partial class AdminMenu { - [Inject] - public HttpClient HttpClient { get; set; } - private ThemeManagerTheme _themeManager = new ThemeManagerTheme(); } -} \ No newline at end of file +} diff --git a/NetEvent/Client/Pages/Administration/Index.razor b/NetEvent/Client/Pages/Administration/Index.razor index 72717f48..c02bc068 100644 --- a/NetEvent/Client/Pages/Administration/Index.razor +++ b/NetEvent/Client/Pages/Administration/Index.razor @@ -13,7 +13,7 @@ Users @Users?.Count Users - + diff --git a/NetEvent/Client/Pages/Administration/Index.razor.cs b/NetEvent/Client/Pages/Administration/Index.razor.cs index 08e6bdc7..c3b6f895 100644 --- a/NetEvent/Client/Pages/Administration/Index.razor.cs +++ b/NetEvent/Client/Pages/Administration/Index.razor.cs @@ -1,20 +1,23 @@ -using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components; +using NetEvent.Client.Services; using NetEvent.Shared.Dto; namespace NetEvent.Client.Pages.Administration { public partial class Index { - [Inject] - public HttpClient HttpClient { get; set; } + private IUserService UserService { get; set; } = default!; - public List? Users { get; private set; } + public List? Users { get; private set; } + protected override async Task OnInitializedAsync() { - Users = await Utils.Get>(HttpClient, "api/users"); + using var cancellationTokenSource = new CancellationTokenSource(); + + Users = await UserService.GetUsersAsync(cancellationTokenSource.Token).ConfigureAwait(false); } } -} \ No newline at end of file +} diff --git a/NetEvent/Client/Pages/Administration/Settings.razor.cs b/NetEvent/Client/Pages/Administration/Settings.razor.cs index 43b0f2c0..5348ea93 100644 --- a/NetEvent/Client/Pages/Administration/Settings.razor.cs +++ b/NetEvent/Client/Pages/Administration/Settings.razor.cs @@ -1,15 +1,13 @@ -using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components; namespace NetEvent.Client.Pages.Administration { public partial class Settings { - [Inject] - public HttpClient HttpClient { get; set; } - + protected override async Task OnInitializedAsync() { } } -} \ No newline at end of file +} diff --git a/NetEvent/Client/Pages/Administration/Users.razor b/NetEvent/Client/Pages/Administration/Users.razor index 40447828..cfb610a1 100644 --- a/NetEvent/Client/Pages/Administration/Users.razor +++ b/NetEvent/Client/Pages/Administration/Users.razor @@ -16,13 +16,13 @@ ReadOnly="false" EditMode="DataGridEditMode.Form" EditTrigger="DataGridEditTrigger.Manual" - CommittedItemChanges="@CommittedUserChanges" + CommittedItemChanges="@CommittedUserChangesAsync" QuickFilter="@_usersQuickFilter" Hideable="true"> - Periodic Elements + Users - @@ -51,4 +51,36 @@ + + + + Roles + + + + + + + + + + + + + + + + + diff --git a/NetEvent/Client/Pages/Administration/Users.razor.cs b/NetEvent/Client/Pages/Administration/Users.razor.cs index 64b80eac..5529bdd3 100644 --- a/NetEvent/Client/Pages/Administration/Users.razor.cs +++ b/NetEvent/Client/Pages/Administration/Users.razor.cs @@ -1,5 +1,6 @@ -using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Identity; +using NetEvent.Client.Services; using NetEvent.Shared.Dto; namespace NetEvent.Client.Pages.Administration @@ -7,46 +8,52 @@ namespace NetEvent.Client.Pages.Administration public partial class Users { [Inject] - public HttpClient HttpClient { get; set; } - + private IUserService UserService { get; set; } = default!; + [Inject] + private IRoleService RoleService { get; set; } = default!; protected override async Task OnInitializedAsync() { - AllUsers = await Utils.Get>(HttpClient, "api/users"); - AllRoles = await Utils.Get>(HttpClient, "roles"); + using var cancellationTokenSource = new CancellationTokenSource(); + + AllUsers = await UserService.GetUsersAsync(cancellationTokenSource.Token); + AllRoles = await RoleService.GetRolesAsync(cancellationTokenSource.Token); } #region Users public List? AllUsers { get; private set; } - private string _usersSearchString; + + private string? _UsersSearchString; // quick filter - filter gobally across multiple columns with the same input private Func _usersQuickFilter => x => { - if (string.IsNullOrWhiteSpace(_usersSearchString)) + if (string.IsNullOrWhiteSpace(_UsersSearchString)) return true; - if (x.UserName.Contains(_usersSearchString, StringComparison.OrdinalIgnoreCase)) + if (x.UserName.Contains(_UsersSearchString, StringComparison.OrdinalIgnoreCase)) return true; - if (x.FirstName.Contains(_usersSearchString, StringComparison.OrdinalIgnoreCase)) + if (x.FirstName.Contains(_UsersSearchString, StringComparison.OrdinalIgnoreCase)) return true; - if (x.LastName.Contains(_usersSearchString, StringComparison.OrdinalIgnoreCase)) + if (x.LastName.Contains(_UsersSearchString, StringComparison.OrdinalIgnoreCase)) return true; - if (x.Email.Contains(_usersSearchString, StringComparison.OrdinalIgnoreCase)) + if (x.Email.Contains(_UsersSearchString, StringComparison.OrdinalIgnoreCase)) return true; return false; }; - void CommittedUserChanges(UserDto item) + private async Task CommittedUserChangesAsync(UserDto updatedUser) { - _ = Utils.Put(HttpClient, $"api/users/{item.Id}", item); + using var cancellationTokenSource = new CancellationTokenSource(); + + await UserService.UpdateUserAsync(updatedUser, cancellationTokenSource.Token).ConfigureAwait(false); } #endregion @@ -55,26 +62,28 @@ void CommittedUserChanges(UserDto item) public List? AllRoles { get; private set; } - private string _roleSearchString; + private string? _RoleSearchString; // quick filter - filter gobally across multiple columns with the same input private Func _roleQuickFilter => x => { - if (string.IsNullOrWhiteSpace(_usersSearchString)) + if (string.IsNullOrWhiteSpace(_UsersSearchString)) return true; - if (x.Name.Contains(_usersSearchString, StringComparison.OrdinalIgnoreCase)) + if (x.Name.Contains(_UsersSearchString, StringComparison.OrdinalIgnoreCase)) return true; return false; }; - void CommittedRoleChanges(IdentityRole item) + private async Task CommittedRoleChangesAsync(IdentityRole updatedRole) { - _ = Utils.Put(HttpClient, $"role/{item.Id}", item); + using var cancellationTokenSource = new CancellationTokenSource(); + + await RoleService.UpdateRoleAsync(updatedRole, cancellationTokenSource.Token).ConfigureAwait(false); } #endregion } -} \ No newline at end of file +} diff --git a/NetEvent/Client/Pages/Login.razor.cs b/NetEvent/Client/Pages/Login.razor.cs index ec8bc057..392e1739 100644 --- a/NetEvent/Client/Pages/Login.razor.cs +++ b/NetEvent/Client/Pages/Login.razor.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components; using NetEvent.Client.Services; using NetEvent.Shared.Dto; @@ -7,10 +7,10 @@ namespace NetEvent.Client.Pages public partial class Login { [Inject] - public CustomStateProvider AuthenticationStateProvider { get; set; } + private NetEventAuthenticationStateProvider AuthenticationStateProvider { get; set; } = default!; [Inject] - public NavigationManager NavigationManager { get; set; } + private NavigationManager NavigationManager { get; set; } = default!; public LoginRequest LoginRequest { get; set; } = new (); @@ -40,4 +40,4 @@ public async Task ExecuteLogin() } } } -} \ No newline at end of file +} diff --git a/NetEvent/Client/Program.cs b/NetEvent/Client/Program.cs index 602d88b3..605e1eb0 100644 --- a/NetEvent/Client/Program.cs +++ b/NetEvent/Client/Program.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Components.Authorization; +using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.Components.WebAssembly.Authentication; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using MudBlazor.Services; @@ -8,22 +8,23 @@ var builder = WebAssemblyHostBuilder.CreateDefault(args); builder.RootComponents.Add("#app"); - builder.Services.AddOptions(); builder.Services.AddAuthorizationCore(); -builder.Services.AddScoped(); -builder.Services.AddScoped(s => s.GetRequiredService()); +builder.Services.AddScoped(); +builder.Services.AddScoped(s => s.GetRequiredService()); builder.Services.AddScoped(); -builder.Services.AddTransient(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); - +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); -builder.Services.AddHttpClient("NetEvent.ServerAPI") +builder.Services.AddHttpClient(Constants.BackendApiHttpClientName) .ConfigureHttpClient(client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)); -builder.Services.AddHttpClient("NetEvent.ServerAPI.Secure") +builder.Services.AddHttpClient(Constants.BackendApiSecuredHttpClientName) .ConfigureHttpClient(client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)) .AddHttpMessageHandler(); builder.Services.AddMudServices(); -await builder.Build().RunAsync(); \ No newline at end of file +await builder.Build().RunAsync(); diff --git a/NetEvent/Client/Services/AuthService.cs b/NetEvent/Client/Services/AuthService.cs index 58ea4812..274971ed 100644 --- a/NetEvent/Client/Services/AuthService.cs +++ b/NetEvent/Client/Services/AuthService.cs @@ -5,49 +5,85 @@ namespace NetEvent.Client.Services { public class AuthService : IAuthService { - private const string _HttpClientName = "NetEvent.ServerAPI"; - private readonly IHttpClientFactory _HttpClientFactory; - public AuthService(IHttpClientFactory httpClientFactory) + private readonly ILogger _Logger; + + public AuthService(IHttpClientFactory httpClientFactory, ILogger logger) { _HttpClientFactory = httpClientFactory; + _Logger = logger; } - public async Task CurrentUserInfo() + + public async Task GetCurrentUserInfoAsync(CancellationToken cancellationToken) { try { - var client = _HttpClientFactory.CreateClient(_HttpClientName); + var client = _HttpClientFactory.CreateClient(Constants.BackendApiHttpClientName); + + var result = await client.GetFromJsonAsync("api/auth/user/current", cancellationToken); + + if(result == null) + { + _Logger.LogError("Unable to get current user from backend."); + return new CurrentUserDto() { IsAuthenticated = false }; + } - var result = await client.GetFromJsonAsync("api/auth/user/current"); return result; } catch (Exception ex) { - + _Logger.LogError(ex, "Unable to get current user from backend."); return new CurrentUserDto() { IsAuthenticated = false}; } } - public async Task Login(LoginRequest loginRequest) + public async Task LoginAsync(LoginRequest loginRequest, CancellationToken cancellationToken) { - var client = _HttpClientFactory.CreateClient(_HttpClientName); + var client = _HttpClientFactory.CreateClient(Constants.BackendApiHttpClientName); + + var result = await client.PostAsJsonAsync("api/auth/login", loginRequest, cancellationToken); - var result = await client.PostAsJsonAsync("api/auth/login", loginRequest); - if (result.StatusCode == System.Net.HttpStatusCode.BadRequest) throw new Exception(await result.Content.ReadAsStringAsync()); - result.EnsureSuccessStatusCode(); + try + { + result.EnsureSuccessStatusCode(); + } + catch (Exception ex) + { + _Logger.LogError(ex, "Unable to logout."); + throw; + } } - public async Task Logout() + public async Task LogoutAsync(CancellationToken cancellationToken) { - var client = _HttpClientFactory.CreateClient(_HttpClientName); - var result = await client.PostAsync("api/auth/logout", null); - result.EnsureSuccessStatusCode(); + var client = _HttpClientFactory.CreateClient(Constants.BackendApiHttpClientName); + var result = await client.PostAsync("api/auth/logout", null, cancellationToken); + + try + { + result.EnsureSuccessStatusCode(); + } + catch (Exception ex) + { + _Logger.LogError(ex, "Unable to logout."); + throw; + } } - public async Task Register(RegisterRequest registerRequest) + + public async Task RegisterAsync(RegisterRequest registerRequest, CancellationToken cancellationToken) { - var client = _HttpClientFactory.CreateClient(_HttpClientName); - var result = await client.PostAsJsonAsync("api/auth/register", registerRequest); - if (result.StatusCode == System.Net.HttpStatusCode.BadRequest) throw new Exception(await result.Content.ReadAsStringAsync()); - result.EnsureSuccessStatusCode(); + var client = _HttpClientFactory.CreateClient(Constants.BackendApiHttpClientName); + + var result = await client.PostAsJsonAsync("api/auth/register", registerRequest, cancellationToken); + + try + { + result.EnsureSuccessStatusCode(); + } + catch (Exception ex) + { + _Logger.LogError(ex, "Unable to register."); + throw; + } } } } diff --git a/NetEvent/Client/Services/Constants.cs b/NetEvent/Client/Services/Constants.cs new file mode 100644 index 00000000..8c7f6a4c --- /dev/null +++ b/NetEvent/Client/Services/Constants.cs @@ -0,0 +1,8 @@ +namespace NetEvent.Client.Services +{ + public static class Constants + { + public const string BackendApiHttpClientName = "NetEvent.ServerAPI"; + public const string BackendApiSecuredHttpClientName = "NetEvent.ServerAPI.Secure"; + } +} diff --git a/NetEvent/Client/Services/CustomStateProvider.cs b/NetEvent/Client/Services/CustomStateProvider.cs deleted file mode 100644 index ffccf4e4..00000000 --- a/NetEvent/Client/Services/CustomStateProvider.cs +++ /dev/null @@ -1,58 +0,0 @@ -using Microsoft.AspNetCore.Components.Authorization; -using NetEvent.Shared.Dto; -using System.Security.Claims; - -namespace NetEvent.Client.Services -{ - public class CustomStateProvider : AuthenticationStateProvider - { - private readonly IAuthService api; - private CurrentUserDto _currentUser; - public CustomStateProvider(IAuthService api) - { - this.api = api; - } - public override async Task GetAuthenticationStateAsync() - { - var identity = new ClaimsIdentity(); - try - { - var userInfo = await GetCurrentUser(); - if (userInfo.IsAuthenticated) - { - var claims = new[] { new Claim(ClaimTypes.Name, _currentUser.UserName) }.Concat(_currentUser.Claims.Select(c => new Claim(c.Key, c.Value))); - identity = new ClaimsIdentity(claims, "Server authentication"); - } - } - catch (HttpRequestException ex) - { - Console.WriteLine("Request failed:" + ex.ToString()); - } - return new AuthenticationState(new ClaimsPrincipal(identity)); - } - private async Task GetCurrentUser() - { - if (_currentUser != null && _currentUser.IsAuthenticated) return _currentUser; - _currentUser = await api.CurrentUserInfo(); - return _currentUser; - } - public async Task Logout() - { - await api.Logout(); - _currentUser = null; - NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); - } - - public async Task Login(LoginRequest loginParameters) - { - await api.Login(loginParameters); - NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); - } - - public async Task Register(RegisterRequest registerParameters) - { - await api.Register(registerParameters); - NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); - } - } -} diff --git a/NetEvent/Client/Services/IAuthService.cs b/NetEvent/Client/Services/IAuthService.cs index 7981ae1a..98832954 100644 --- a/NetEvent/Client/Services/IAuthService.cs +++ b/NetEvent/Client/Services/IAuthService.cs @@ -4,9 +4,9 @@ namespace NetEvent.Client.Services { public interface IAuthService { - Task Login(LoginRequest loginRequest); - Task Register(RegisterRequest registerRequest); - Task Logout(); - Task CurrentUserInfo(); + Task LoginAsync(LoginRequest loginRequest, CancellationToken cancellationToken); + Task RegisterAsync(RegisterRequest registerRequest, CancellationToken cancellationToken); + Task LogoutAsync(CancellationToken cancellationToken); + Task GetCurrentUserInfoAsync(CancellationToken cancellationToken); } } diff --git a/NetEvent/Client/Services/IOrganizationDataService.cs b/NetEvent/Client/Services/IOrganizationDataService.cs new file mode 100644 index 00000000..a465a417 --- /dev/null +++ b/NetEvent/Client/Services/IOrganizationDataService.cs @@ -0,0 +1,9 @@ +using NetEvent.Shared.Dto; + +namespace NetEvent.Client.Services +{ + public interface IOrganizationDataService + { + Task> GetOrganizationDataAsync(CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/NetEvent/Client/Services/IRoleService.cs b/NetEvent/Client/Services/IRoleService.cs new file mode 100644 index 00000000..ef9911e5 --- /dev/null +++ b/NetEvent/Client/Services/IRoleService.cs @@ -0,0 +1,10 @@ +using Microsoft.AspNetCore.Identity; + +namespace NetEvent.Client.Services +{ + public interface IRoleService + { + Task> GetRolesAsync(CancellationToken cancellationToken); + Task UpdateRoleAsync(IdentityRole updatedRole, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/NetEvent/Client/Services/IThemeService.cs b/NetEvent/Client/Services/IThemeService.cs new file mode 100644 index 00000000..8ea2bc95 --- /dev/null +++ b/NetEvent/Client/Services/IThemeService.cs @@ -0,0 +1,10 @@ +using MudBlazor.ThemeManager; + +namespace NetEvent.Client.Services +{ + public interface IThemeService + { + Task GetThemeAsync(CancellationToken cancellationToken); + Task UpdateThemeAsync(ThemeManagerTheme updatedTheme, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/NetEvent/Client/Services/IUserService.cs b/NetEvent/Client/Services/IUserService.cs new file mode 100644 index 00000000..7a1107a5 --- /dev/null +++ b/NetEvent/Client/Services/IUserService.cs @@ -0,0 +1,10 @@ +using NetEvent.Shared.Dto; + +namespace NetEvent.Client.Services +{ + public interface IUserService + { + Task> GetUsersAsync(CancellationToken cancellationToken); + Task UpdateUserAsync(UserDto updatedUser, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/NetEvent/Client/Services/NetEventAuthenticationStateProvider.cs b/NetEvent/Client/Services/NetEventAuthenticationStateProvider.cs new file mode 100644 index 00000000..9880a75a --- /dev/null +++ b/NetEvent/Client/Services/NetEventAuthenticationStateProvider.cs @@ -0,0 +1,81 @@ +using Microsoft.AspNetCore.Components.Authorization; +using NetEvent.Shared.Dto; +using System.Security.Claims; + +namespace NetEvent.Client.Services +{ + public class NetEventAuthenticationStateProvider : AuthenticationStateProvider + { + private readonly IAuthService _Api; + private readonly ILogger _Logger; + + private CurrentUserDto? _CurrentUser; + + public NetEventAuthenticationStateProvider(IAuthService api, ILogger logger) + { + _Api = api; + _Logger = logger; + } + + public override async Task GetAuthenticationStateAsync() + { + var identity = new ClaimsIdentity(); + try + { + var userInfo = await GetCurrentUser(); + if (userInfo != null && userInfo.IsAuthenticated) + { + var claims = new[] { new Claim(ClaimTypes.Name, userInfo.UserName) }.Concat(userInfo.Claims.Select(c => new Claim(c.Key, c.Value))); + identity = new ClaimsIdentity(claims, "Server authentication"); + } + } + catch (Exception ex) + { + _Logger.LogError(ex, "Unable to get identity"); + } + + return new AuthenticationState(new ClaimsPrincipal(identity)); + } + + private async Task GetCurrentUser() + { + using var cancellationTokenSource = new CancellationTokenSource(); + + if (_CurrentUser != null && _CurrentUser.IsAuthenticated) + { + return _CurrentUser; + } + + _CurrentUser = await _Api.GetCurrentUserInfoAsync(cancellationTokenSource.Token).ConfigureAwait(false); + return _CurrentUser; + } + + public async Task Logout() + { + using var cancellationTokenSource = new CancellationTokenSource(); + + await _Api.LogoutAsync(cancellationTokenSource.Token).ConfigureAwait(false); + _CurrentUser = null; + + NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); + } + + public async Task Login(LoginRequest loginParameters) + { + using var cancellationTokenSource = new CancellationTokenSource(); + + await _Api.LoginAsync(loginParameters, cancellationTokenSource.Token).ConfigureAwait(false); + + NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); + } + + public async Task Register(RegisterRequest registerParameters) + { + using var cancellationTokenSource = new CancellationTokenSource(); + + await _Api.RegisterAsync(registerParameters, cancellationTokenSource.Token).ConfigureAwait(false); + + NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); + } + } +} diff --git a/NetEvent/Client/Services/OrganizationDataService.cs b/NetEvent/Client/Services/OrganizationDataService.cs new file mode 100644 index 00000000..8fb090be --- /dev/null +++ b/NetEvent/Client/Services/OrganizationDataService.cs @@ -0,0 +1,40 @@ +using NetEvent.Shared.Dto; +using System.Net.Http.Json; + +namespace NetEvent.Client.Services +{ + public class OrganizationDataService : IOrganizationDataService + { + private readonly IHttpClientFactory _HttpClientFactory; + private readonly ILogger _Logger; + + public OrganizationDataService(IHttpClientFactory httpClientFactory, ILogger logger) + { + _HttpClientFactory = httpClientFactory; + _Logger = logger; + } + + public async Task> GetOrganizationDataAsync(CancellationToken cancellationToken) + { + try + { + var client = _HttpClientFactory.CreateClient(Constants.BackendApiHttpClientName); + + var result = await client.GetFromJsonAsync>("api/organization/all", cancellationToken); + + if (result == null) + { + _Logger.LogError("Unable to get organization data from backend"); + return new List(); + } + + return result; + } + catch (Exception ex) + { + _Logger.LogError(ex, "Unable to get organization data from backend"); + return new List(); + } + } + } +} diff --git a/NetEvent/Client/Services/RoleService.cs b/NetEvent/Client/Services/RoleService.cs new file mode 100644 index 00000000..138c59cd --- /dev/null +++ b/NetEvent/Client/Services/RoleService.cs @@ -0,0 +1,62 @@ +using System.Net.Http.Json; +using Microsoft.AspNetCore.Identity; +using NetEvent.Shared.Dto; + +namespace NetEvent.Client.Services +{ + public class RoleService : IRoleService + { + private readonly IHttpClientFactory _HttpClientFactory; + private readonly ILogger _Logger; + + public RoleService(IHttpClientFactory httpClientFactory, ILogger logger) + { + _HttpClientFactory = httpClientFactory; + _Logger = logger; + } + + public async Task> GetRolesAsync(CancellationToken cancellationToken) + { + try + { + var client = _HttpClientFactory.CreateClient(Constants.BackendApiHttpClientName); + + var roles = await client.GetFromJsonAsync>("/api/roles", cancellationToken).ConfigureAwait(false); + + if (roles == null) + { + _Logger.LogError("Unable to get roles data from backend"); + return new List(); + } + + return roles; + } + catch (Exception ex) + { + _Logger.LogError(ex, "Unable to get roles data from backend"); + return new List(); + } + } + + public async Task UpdateRoleAsync(IdentityRole updatedRole, CancellationToken cancellationToken) + { + try + { + var client = _HttpClientFactory.CreateClient(Constants.BackendApiHttpClientName); + + var content = JsonContent.Create(updatedRole); + + var response = await client.PutAsync($"api/role/{updatedRole.Id}", content, cancellationToken); + + response.EnsureSuccessStatusCode(); + + return true; + } + catch (Exception ex) + { + _Logger.LogError(ex, "Unable to update role in backend."); + } + return false; + } + } +} diff --git a/NetEvent/Client/Services/ThemeService.cs b/NetEvent/Client/Services/ThemeService.cs new file mode 100644 index 00000000..9354feac --- /dev/null +++ b/NetEvent/Client/Services/ThemeService.cs @@ -0,0 +1,67 @@ +using System.Net.Http.Json; +using MudBlazor.ThemeManager; +using NetEvent.Shared.Dto; +using Newtonsoft.Json; + +namespace NetEvent.Client.Services +{ + public class ThemeService : IThemeService + { + private readonly IHttpClientFactory _HttpClientFactory; + private readonly ILogger _Logger; + + public ThemeService(IHttpClientFactory httpClientFactory, ILogger logger) + { + _HttpClientFactory = httpClientFactory; + _Logger = logger; + } + + public async Task GetThemeAsync(CancellationToken cancellationToken) + { + try + { + var client = _HttpClientFactory.CreateClient(Constants.BackendApiHttpClientName); + + var themeDto = await client.GetFromJsonAsync("api/themes/theme", cancellationToken).ConfigureAwait(false); + + if (themeDto?.ThemeData == null) + { + return null; + } + + var newThemeManager = JsonConvert.DeserializeObject(themeDto.ThemeData); + + return newThemeManager; + + } + catch (Exception ex) + { + _Logger.LogError(ex, "Unable to get theme from backend."); + } + + return null; + } + + public async Task UpdateThemeAsync(ThemeManagerTheme updatedTheme, CancellationToken cancellationToken) + { + try + { + var themeData = JsonConvert.SerializeObject(updatedTheme); + var client = _HttpClientFactory.CreateClient(Constants.BackendApiHttpClientName); + + var content = JsonContent.Create(new ThemeDto { ThemeData = themeData }); + + var response = await client.PutAsync("api/themes/theme", content, cancellationToken); + + response.EnsureSuccessStatusCode(); + + return true; + } + catch (Exception ex) + { + _Logger.LogError(ex, "Unable to update theme in backend."); + } + return false; + } + } +} diff --git a/NetEvent/Client/Services/UserService.cs b/NetEvent/Client/Services/UserService.cs new file mode 100644 index 00000000..b1de648b --- /dev/null +++ b/NetEvent/Client/Services/UserService.cs @@ -0,0 +1,62 @@ +using System.Net.Http.Json; +using NetEvent.Shared.Dto; + +namespace NetEvent.Client.Services +{ + public class UserService : IUserService + { + private readonly IHttpClientFactory _HttpClientFactory; + private readonly ILogger _Logger; + + public UserService(IHttpClientFactory httpClientFactory, ILogger logger) + { + _HttpClientFactory = httpClientFactory; + _Logger = logger; + } + + public async Task> GetUsersAsync(CancellationToken cancellationToken) + { + + try + { + var client = _HttpClientFactory.CreateClient(Constants.BackendApiHttpClientName); + + var users = await client.GetFromJsonAsync>("api/users", cancellationToken).ConfigureAwait(false); + + if (users == null) + { + _Logger.LogError("Unable to get users data from backend"); + return new List(); + } + + return users; + } + catch (Exception ex) + { + _Logger.LogError(ex, "Unable to get users data from backend"); + return new List(); + } + } + + public async Task UpdateUserAsync(UserDto updatedUser, CancellationToken cancellationToken) + { + try + { + var client = _HttpClientFactory.CreateClient(Constants.BackendApiHttpClientName); + + var content = JsonContent.Create(updatedUser); + + var response = await client.PutAsync($"api/user/{updatedUser.Id}", content, cancellationToken); + + response.EnsureSuccessStatusCode(); + + return true; + } + catch (Exception ex) + { + _Logger.LogError(ex, "Unable to update user in backend."); + } + return false; + } + } +} diff --git a/NetEvent/Client/Shared/MainLayout.razor.cs b/NetEvent/Client/Shared/MainLayout.razor.cs index 501d161f..e000b63a 100644 --- a/NetEvent/Client/Shared/MainLayout.razor.cs +++ b/NetEvent/Client/Shared/MainLayout.razor.cs @@ -1,14 +1,21 @@ +using System.Globalization; using Microsoft.AspNetCore.Components; using MudBlazor.ThemeManager; -using NetEvent.Shared.Dto; -using Newtonsoft.Json; +using NetEvent.Client.Services; +using NetEvent.Shared.Constants; namespace NetEvent.Client.Shared { public partial class MainLayout { [Inject] - public HttpClient HttpClient { get; set; } + private IThemeService ThemeService { get; set; } = default!; + + [Inject] + private IOrganizationDataService OrganizationDataService { get; set; } = default!; + + [Inject] + private ILogger Logger { get; set; } = default!; private ThemeManagerTheme _ThemeManager = new(); bool _drawerOpen = true; @@ -20,14 +27,47 @@ void DrawerToggle() protected override async Task OnInitializedAsync() { - var theme = await HttpClient.Get("api/themes/theme"); - if (theme?.ThemeData != null) + await SetThemeAsync().ConfigureAwait(false); + + await SetCultureAsync().ConfigureAwait(false); + } + + private async Task SetThemeAsync() + { + using var cancellationTokenSource = new CancellationTokenSource(); + + var theme = await ThemeService.GetThemeAsync(cancellationTokenSource.Token).ConfigureAwait(false); + + if (theme != null) { - var newThemeManager = JsonConvert.DeserializeObject(theme.ThemeData); - if (newThemeManager != null) + _ThemeManager.Theme.Palette.AppbarBackground = theme.Theme.Palette.AppbarBackground; + } + } + + private async Task SetCultureAsync() + { + using var cancellationTokenSource = new CancellationTokenSource(); + + try + { + var orgData = await OrganizationDataService.GetOrganizationDataAsync(cancellationTokenSource.Token).ConfigureAwait(false); + + var organizationCulture = orgData.FirstOrDefault(a => a.Key.Equals(OrganizationDataConstants.CultureKey)); + + if (organizationCulture == null) { - _ThemeManager.Theme.Palette.AppbarBackground = newThemeManager.Theme.Palette.AppbarBackground; + return; } + + var culture = organizationCulture.Value; + + var cultureInfo = new CultureInfo(culture); + CultureInfo.DefaultThreadCurrentCulture = cultureInfo; + CultureInfo.DefaultThreadCurrentUICulture = cultureInfo; + } + catch (Exception ex) + { + Logger.LogError(ex, "Unable to set culture from backend."); } } @@ -38,10 +78,11 @@ void ToggleThemeManager(bool value) _themeManagerOpen = value; } - private async Task UpdateTheme(ThemeManagerTheme value) + private async Task UpdateTheme(ThemeManagerTheme updatedTheme) { - var themeData = JsonConvert.SerializeObject(value); - await HttpClient.Put("api/themes/theme", new ThemeDto { ThemeData = themeData }); + using var cancellationTokenSource = new CancellationTokenSource(); + + await ThemeService.UpdateThemeAsync(updatedTheme, cancellationTokenSource.Token).ConfigureAwait(false); } } -} \ No newline at end of file +} diff --git a/NetEvent/Client/Shared/NavigationBar.razor b/NetEvent/Client/Shared/NavigationBar.razor index 2cd9f154..9ee03970 100644 --- a/NetEvent/Client/Shared/NavigationBar.razor +++ b/NetEvent/Client/Shared/NavigationBar.razor @@ -3,7 +3,7 @@ @using NetEvent.Client.Services @inject NavigationManager Navigation -@inject CustomStateProvider SignOutManager +@inject NetEventAuthenticationStateProvider SignOutManager diff --git a/NetEvent/Client/Utils.cs b/NetEvent/Client/Utils.cs deleted file mode 100644 index d8dc4a25..00000000 --- a/NetEvent/Client/Utils.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System.Text; -using System.Text.Json; - -namespace NetEvent.Client -{ - public static class Utils - { - public static async Task Get(this HttpClient httpClient, string apiMethod) - { - try - { - var request = new HttpRequestMessage(HttpMethod.Get, apiMethod); - request.Headers.Add("Accept", "application/json"); - request.Headers.Add("User-Agent", "NetEvent.Client"); - - //var client = clientFactory.CreateClient(); - - var response = await httpClient.SendAsync(request); - - if (response.IsSuccessStatusCode) - { - var responseString = await response.Content.ReadAsStreamAsync(); - var result = JsonSerializer.Deserialize(responseString, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); - if (result != null) - { - return result; - } - } - else - { - // TODO ErrorHandling??? - } - } - catch (Exception e) - { - - } - return default; - } - - - public static async Task Put(this HttpClient httpClient, string apiMethod, T putData) - { - try - { - var request = new HttpRequestMessage(HttpMethod.Put, apiMethod); - request.Headers.Add("Accept", "application/json"); - request.Headers.Add("User-Agent", "NetEvent.Client"); - - var json = JsonSerializer.Serialize(putData); - request.Content = new StringContent(json, Encoding.UTF8, "application/json"); - - //var client = clientFactory.CreateClient(); - - var response = await httpClient.SendAsync(request); - - if (response.IsSuccessStatusCode) - { - var responseString = await response.Content.ReadAsStreamAsync(); - var result = JsonSerializer.Deserialize(responseString, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); - if (result != null) - { - return; - } - - //using var responseStream = await response.Content.ReadAsStreamAsync(); - //var result = await JsonSerializer.DeserializeAsync(responseStream); - //if (result != null) - //{ - // return result; - //} - } - else - { - // TODO ErrorHandling??? - } - } - catch (Exception e) - { - - } - - throw new NotImplementedException("Error Handling is not yet implemented!"); - } - } -} diff --git a/NetEvent/Server/Migrations/Psql/20220428220239_AddCulture.Designer.cs b/NetEvent/Server/Migrations/Psql/20220428220239_AddCulture.Designer.cs new file mode 100644 index 00000000..ff91aff4 --- /dev/null +++ b/NetEvent/Server/Migrations/Psql/20220428220239_AddCulture.Designer.cs @@ -0,0 +1,371 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetEvent.Server.Data; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace NetEvent.Server.Migrations.Psql +{ + [DbContext(typeof(PsqlApplicationDbContext))] + [Migration("20220428220239_AddCulture")] + partial class AddCulture + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("Role", (string)null); + + b.HasData( + new + { + Id = "admin", + ConcurrencyStamp = "c5719bd7-ae7e-4a73-9ab4-749ccd170ece", + Name = "Admin", + NormalizedName = "ADMIN" + }, + new + { + Id = "user", + ConcurrencyStamp = "40d745a6-bf89-451e-b041-d4f631dcade2", + Name = "User", + NormalizedName = "USER" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("RoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("UserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("UserRoles", (string)null); + + b.HasData( + new + { + UserId = "BAFC89CF-4F3E-4595-8256-CCA19C260FBD", + RoleId = "admin" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("UserTokens", (string)null); + }); + + modelBuilder.Entity("NetEvent.Server.Models.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("ProfilePicture") + .HasColumnType("bytea"); + + b.Property("SecurityStamp") + .IsRequired() + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("User", (string)null); + + b.HasData( + new + { + Id = "BAFC89CF-4F3E-4595-8256-CCA19C260FBD", + AccessFailedCount = 0, + ConcurrencyStamp = "0e150be1-623e-4fe6-8f14-35451efec037", + Email = "admin@admin.de", + EmailConfirmed = true, + FirstName = "Admin", + LastName = "istrator", + LockoutEnabled = false, + NormalizedEmail = "ADMIN@ADMIN.DE", + NormalizedUserName = "ADMIN", + PasswordHash = "AQAAAAEAACcQAAAAEHbIiyBeJvGwJA6MfKoNVUAa7QzsLRa/lh1RxYZYbK5IcmxgT9vGct4aiS0rFJJy4g==", + PhoneNumberConfirmed = false, + SecurityStamp = "737e3334-e483-4ef3-9366-2c31e098182e", + TwoFactorEnabled = false, + UserName = "admin" + }); + }); + + modelBuilder.Entity("NetEvent.Server.Models.OrganizationData", b => + { + b.Property("Key") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("Key"); + + b.ToTable("OrganizationData", (string)null); + + b.HasData( + new + { + Key = "Culture", + Value = "en-US" + }); + }); + + modelBuilder.Entity("NetEvent.Shared.Dto.ThemeDto", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ThemeData") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Themes", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("NetEvent.Server.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("NetEvent.Server.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("NetEvent.Server.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("NetEvent.Server.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/NetEvent/Server/Migrations/Psql/20220428220239_AddCulture.cs b/NetEvent/Server/Migrations/Psql/20220428220239_AddCulture.cs new file mode 100644 index 00000000..1ca24e27 --- /dev/null +++ b/NetEvent/Server/Migrations/Psql/20220428220239_AddCulture.cs @@ -0,0 +1,85 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace NetEvent.Server.Migrations.Psql +{ + public partial class AddCulture : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Value", + table: "OrganizationData", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "text"); + + migrationBuilder.InsertData( + table: "OrganizationData", + columns: new[] { "Key", "Value" }, + values: new object[] { "Culture", "en-US" }); + + migrationBuilder.UpdateData( + table: "Role", + keyColumn: "Id", + keyValue: "admin", + column: "ConcurrencyStamp", + value: "c5719bd7-ae7e-4a73-9ab4-749ccd170ece"); + + migrationBuilder.UpdateData( + table: "Role", + keyColumn: "Id", + keyValue: "user", + column: "ConcurrencyStamp", + value: "40d745a6-bf89-451e-b041-d4f631dcade2"); + + migrationBuilder.UpdateData( + table: "User", + keyColumn: "Id", + keyValue: "BAFC89CF-4F3E-4595-8256-CCA19C260FBD", + columns: new[] { "ConcurrencyStamp", "PasswordHash", "SecurityStamp" }, + values: new object[] { "0e150be1-623e-4fe6-8f14-35451efec037", "AQAAAAEAACcQAAAAEHbIiyBeJvGwJA6MfKoNVUAa7QzsLRa/lh1RxYZYbK5IcmxgT9vGct4aiS0rFJJy4g==", "737e3334-e483-4ef3-9366-2c31e098182e" }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "OrganizationData", + keyColumn: "Key", + keyValue: "Culture"); + + migrationBuilder.AlterColumn( + name: "Value", + table: "OrganizationData", + type: "text", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + + migrationBuilder.UpdateData( + table: "Role", + keyColumn: "Id", + keyValue: "admin", + column: "ConcurrencyStamp", + value: "43c133fb-9d03-4384-926f-0cc0bdf8ed76"); + + migrationBuilder.UpdateData( + table: "Role", + keyColumn: "Id", + keyValue: "user", + column: "ConcurrencyStamp", + value: "0f0d9301-e452-45e5-9b8b-f2352f7766cc"); + + migrationBuilder.UpdateData( + table: "User", + keyColumn: "Id", + keyValue: "BAFC89CF-4F3E-4595-8256-CCA19C260FBD", + columns: new[] { "ConcurrencyStamp", "PasswordHash", "SecurityStamp" }, + values: new object[] { "d23693f6-f4a2-4225-8b9a-0d704b0f6b36", "AQAAAAEAACcQAAAAEJ1Hma7SypiJ0f8Fpew1kKBIvqOnfIeNuwJqBqwQB6ytPaou838eJEO3au2h7J0MxA==", "f7cb9a70-b856-442a-a110-a71d9d81c485" }); + } + } +} diff --git a/NetEvent/Server/Migrations/Psql/PsqlApplicationDbContextModelSnapshot.cs b/NetEvent/Server/Migrations/Psql/PsqlApplicationDbContextModelSnapshot.cs index f7161d6c..900a55b0 100644 --- a/NetEvent/Server/Migrations/Psql/PsqlApplicationDbContextModelSnapshot.cs +++ b/NetEvent/Server/Migrations/Psql/PsqlApplicationDbContextModelSnapshot.cs @@ -51,14 +51,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) new { Id = "admin", - ConcurrencyStamp = "43c133fb-9d03-4384-926f-0cc0bdf8ed76", + ConcurrencyStamp = "c5719bd7-ae7e-4a73-9ab4-749ccd170ece", Name = "Admin", NormalizedName = "ADMIN" }, new { Id = "user", - ConcurrencyStamp = "0f0d9301-e452-45e5-9b8b-f2352f7766cc", + ConcurrencyStamp = "40d745a6-bf89-451e-b041-d4f631dcade2", Name = "User", NormalizedName = "USER" }); @@ -262,7 +262,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { Id = "BAFC89CF-4F3E-4595-8256-CCA19C260FBD", AccessFailedCount = 0, - ConcurrencyStamp = "d23693f6-f4a2-4225-8b9a-0d704b0f6b36", + ConcurrencyStamp = "0e150be1-623e-4fe6-8f14-35451efec037", Email = "admin@admin.de", EmailConfirmed = true, FirstName = "Admin", @@ -270,9 +270,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) LockoutEnabled = false, NormalizedEmail = "ADMIN@ADMIN.DE", NormalizedUserName = "ADMIN", - PasswordHash = "AQAAAAEAACcQAAAAEJ1Hma7SypiJ0f8Fpew1kKBIvqOnfIeNuwJqBqwQB6ytPaou838eJEO3au2h7J0MxA==", + PasswordHash = "AQAAAAEAACcQAAAAEHbIiyBeJvGwJA6MfKoNVUAa7QzsLRa/lh1RxYZYbK5IcmxgT9vGct4aiS0rFJJy4g==", PhoneNumberConfirmed = false, - SecurityStamp = "f7cb9a70-b856-442a-a110-a71d9d81c485", + SecurityStamp = "737e3334-e483-4ef3-9366-2c31e098182e", TwoFactorEnabled = false, UserName = "admin" }); @@ -284,15 +284,21 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("text"); b.Property("Value") - .IsRequired() .HasColumnType("text"); b.HasKey("Key"); b.ToTable("OrganizationData", (string)null); + + b.HasData( + new + { + Key = "Culture", + Value = "en-US" + }); }); - modelBuilder.Entity("NetEvent.Shared.Dto.Theme", b => + modelBuilder.Entity("NetEvent.Shared.Dto.ThemeDto", b => { b.Property("Id") .ValueGeneratedOnAdd() diff --git a/NetEvent/Server/Migrations/Sqlite/20220428220235_AddCulture.Designer.cs b/NetEvent/Server/Migrations/Sqlite/20220428220235_AddCulture.Designer.cs new file mode 100644 index 00000000..1f9518c4 --- /dev/null +++ b/NetEvent/Server/Migrations/Sqlite/20220428220235_AddCulture.Designer.cs @@ -0,0 +1,362 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetEvent.Server.Data; + +#nullable disable + +namespace NetEvent.Server.Migrations.Sqlite +{ + [DbContext(typeof(SqliteApplicationDbContext))] + [Migration("20220428220235_AddCulture")] + partial class AddCulture + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.4"); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("Role", (string)null); + + b.HasData( + new + { + Id = "admin", + ConcurrencyStamp = "b28356af-7c77-43b5-9ae6-94d13a920993", + Name = "Admin", + NormalizedName = "ADMIN" + }, + new + { + Id = "user", + ConcurrencyStamp = "7d5abe61-7b08-476a-a045-b3f9ec1db046", + Name = "User", + NormalizedName = "USER" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("RoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("UserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("UserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("UserRoles", (string)null); + + b.HasData( + new + { + UserId = "BAFC89CF-4F3E-4595-8256-CCA19C260FBD", + RoleId = "admin" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("UserTokens", (string)null); + }); + + modelBuilder.Entity("NetEvent.Server.Models.ApplicationUser", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("ProfilePicture") + .HasColumnType("BLOB"); + + b.Property("SecurityStamp") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("User", (string)null); + + b.HasData( + new + { + Id = "BAFC89CF-4F3E-4595-8256-CCA19C260FBD", + AccessFailedCount = 0, + ConcurrencyStamp = "c4f79989-e421-4916-9111-c755b5ff3f7c", + Email = "admin@admin.de", + EmailConfirmed = true, + FirstName = "Admin", + LastName = "istrator", + LockoutEnabled = false, + NormalizedEmail = "ADMIN@ADMIN.DE", + NormalizedUserName = "ADMIN", + PasswordHash = "AQAAAAEAACcQAAAAEOYecvBveTxk0Uak/0OlhtvUSmeGR0l671OBLIQA+WLpfW1dGPiqPuKUIVOhRYV8lQ==", + PhoneNumberConfirmed = false, + SecurityStamp = "d7bce49e-fef8-44bc-9eaa-f3baaeb7d32e", + TwoFactorEnabled = false, + UserName = "admin" + }); + }); + + modelBuilder.Entity("NetEvent.Server.Models.OrganizationData", b => + { + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Key"); + + b.ToTable("OrganizationData", (string)null); + + b.HasData( + new + { + Key = "Culture", + Value = "en-US" + }); + }); + + modelBuilder.Entity("NetEvent.Shared.Dto.ThemeDto", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ThemeData") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Themes", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("NetEvent.Server.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("NetEvent.Server.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("NetEvent.Server.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("NetEvent.Server.Models.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/NetEvent/Server/Migrations/Sqlite/20220428220235_AddCulture.cs b/NetEvent/Server/Migrations/Sqlite/20220428220235_AddCulture.cs new file mode 100644 index 00000000..10b221ea --- /dev/null +++ b/NetEvent/Server/Migrations/Sqlite/20220428220235_AddCulture.cs @@ -0,0 +1,85 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace NetEvent.Server.Migrations.Sqlite +{ + public partial class AddCulture : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Value", + table: "OrganizationData", + type: "TEXT", + nullable: true, + oldClrType: typeof(string), + oldType: "TEXT"); + + migrationBuilder.InsertData( + table: "OrganizationData", + columns: new[] { "Key", "Value" }, + values: new object[] { "Culture", "en-US" }); + + migrationBuilder.UpdateData( + table: "Role", + keyColumn: "Id", + keyValue: "admin", + column: "ConcurrencyStamp", + value: "b28356af-7c77-43b5-9ae6-94d13a920993"); + + migrationBuilder.UpdateData( + table: "Role", + keyColumn: "Id", + keyValue: "user", + column: "ConcurrencyStamp", + value: "7d5abe61-7b08-476a-a045-b3f9ec1db046"); + + migrationBuilder.UpdateData( + table: "User", + keyColumn: "Id", + keyValue: "BAFC89CF-4F3E-4595-8256-CCA19C260FBD", + columns: new[] { "ConcurrencyStamp", "PasswordHash", "SecurityStamp" }, + values: new object[] { "c4f79989-e421-4916-9111-c755b5ff3f7c", "AQAAAAEAACcQAAAAEOYecvBveTxk0Uak/0OlhtvUSmeGR0l671OBLIQA+WLpfW1dGPiqPuKUIVOhRYV8lQ==", "d7bce49e-fef8-44bc-9eaa-f3baaeb7d32e" }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "OrganizationData", + keyColumn: "Key", + keyValue: "Culture"); + + migrationBuilder.AlterColumn( + name: "Value", + table: "OrganizationData", + type: "TEXT", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "TEXT", + oldNullable: true); + + migrationBuilder.UpdateData( + table: "Role", + keyColumn: "Id", + keyValue: "admin", + column: "ConcurrencyStamp", + value: "8cba3bb0-7bd3-451a-98c9-b5410db49d2c"); + + migrationBuilder.UpdateData( + table: "Role", + keyColumn: "Id", + keyValue: "user", + column: "ConcurrencyStamp", + value: "cf7db04d-68f7-468e-9983-fe6e94f0082f"); + + migrationBuilder.UpdateData( + table: "User", + keyColumn: "Id", + keyValue: "BAFC89CF-4F3E-4595-8256-CCA19C260FBD", + columns: new[] { "ConcurrencyStamp", "PasswordHash", "SecurityStamp" }, + values: new object[] { "c65b7069-d295-4dc0-bc16-893a0709919c", "AQAAAAEAACcQAAAAENtabIRe2VSwWrntrDz2wxI1TokRrpCsatjk3EJAkUpvMnyD/iqXU+hK3U9/GNpFuw==", "b0e612a1-6cf0-4559-81cb-af8288ff72cf" }); + } + } +} diff --git a/NetEvent/Server/Migrations/Sqlite/SqliteApplicationDbContextModelSnapshot.cs b/NetEvent/Server/Migrations/Sqlite/SqliteApplicationDbContextModelSnapshot.cs index bfab4aa8..39fe565b 100644 --- a/NetEvent/Server/Migrations/Sqlite/SqliteApplicationDbContextModelSnapshot.cs +++ b/NetEvent/Server/Migrations/Sqlite/SqliteApplicationDbContextModelSnapshot.cs @@ -46,14 +46,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) new { Id = "admin", - ConcurrencyStamp = "8cba3bb0-7bd3-451a-98c9-b5410db49d2c", + ConcurrencyStamp = "b28356af-7c77-43b5-9ae6-94d13a920993", Name = "Admin", NormalizedName = "ADMIN" }, new { Id = "user", - ConcurrencyStamp = "cf7db04d-68f7-468e-9983-fe6e94f0082f", + ConcurrencyStamp = "7d5abe61-7b08-476a-a045-b3f9ec1db046", Name = "User", NormalizedName = "USER" }); @@ -253,7 +253,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { Id = "BAFC89CF-4F3E-4595-8256-CCA19C260FBD", AccessFailedCount = 0, - ConcurrencyStamp = "c65b7069-d295-4dc0-bc16-893a0709919c", + ConcurrencyStamp = "c4f79989-e421-4916-9111-c755b5ff3f7c", Email = "admin@admin.de", EmailConfirmed = true, FirstName = "Admin", @@ -261,9 +261,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) LockoutEnabled = false, NormalizedEmail = "ADMIN@ADMIN.DE", NormalizedUserName = "ADMIN", - PasswordHash = "AQAAAAEAACcQAAAAENtabIRe2VSwWrntrDz2wxI1TokRrpCsatjk3EJAkUpvMnyD/iqXU+hK3U9/GNpFuw==", + PasswordHash = "AQAAAAEAACcQAAAAEOYecvBveTxk0Uak/0OlhtvUSmeGR0l671OBLIQA+WLpfW1dGPiqPuKUIVOhRYV8lQ==", PhoneNumberConfirmed = false, - SecurityStamp = "b0e612a1-6cf0-4559-81cb-af8288ff72cf", + SecurityStamp = "d7bce49e-fef8-44bc-9eaa-f3baaeb7d32e", TwoFactorEnabled = false, UserName = "admin" }); @@ -275,15 +275,21 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("Value") - .IsRequired() .HasColumnType("TEXT"); b.HasKey("Key"); b.ToTable("OrganizationData", (string)null); + + b.HasData( + new + { + Key = "Culture", + Value = "en-US" + }); }); - modelBuilder.Entity("NetEvent.Shared.Dto.Theme", b => + modelBuilder.Entity("NetEvent.Shared.Dto.ThemeDto", b => { b.Property("Id") .ValueGeneratedOnAdd() diff --git a/NetEvent/Server/Modules/Organization/OrganizationModule.cs b/NetEvent/Server/Modules/Organization/OrganizationModule.cs index a785d4f2..cd3274d8 100644 --- a/NetEvent/Server/Modules/Organization/OrganizationModule.cs +++ b/NetEvent/Server/Modules/Organization/OrganizationModule.cs @@ -5,6 +5,7 @@ using NetEvent.Server.Models; using NetEvent.Server.Modules.Organization.Endpoints.GetOrganization; using NetEvent.Server.Modules.Organization.Endpoints.PostOrganization; +using NetEvent.Shared.Constants; namespace NetEvent.Server.Modules.Authorization { @@ -30,6 +31,7 @@ public override void OnModelCreating(ModelBuilder builder) builder.Entity(entity => { entity.ToTable(name: "OrganizationData"); + entity.HasData(new OrganizationData { Key = OrganizationDataConstants.CultureKey, Value = "en-US" }); }); } } diff --git a/NetEvent/Server/Modules/Themes/Endpoints/GetTheme/GetThemeHandler.cs b/NetEvent/Server/Modules/Themes/Endpoints/GetTheme/GetThemeHandler.cs index 2325aa3c..474cbdfd 100644 --- a/NetEvent/Server/Modules/Themes/Endpoints/GetTheme/GetThemeHandler.cs +++ b/NetEvent/Server/Modules/Themes/Endpoints/GetTheme/GetThemeHandler.cs @@ -16,13 +16,13 @@ public GetThemeHandler(ApplicationDbContext applicationDbContext, ILogger Handle(GetThemeRequest request, CancellationToken cancellationToken) { - var theme = _ApplicationDbContext.Themes.FirstOrDefault(); + var theme = _ApplicationDbContext.Themes.OrderBy(a=> a.Id).FirstOrDefault(); if(theme == null) { var errorMessage = "No theme set."; _Logger.LogError(errorMessage); - return Task.FromResult(new GetThemeResponse(ReturnType.NotFound, errorMessage)); + return Task.FromResult(new GetThemeResponse(ReturnType.Error, errorMessage)); } return Task.FromResult(new GetThemeResponse(theme)); diff --git a/NetEvent/Server/Modules/Themes/ThemesModule.cs b/NetEvent/Server/Modules/Themes/ThemesModule.cs index 5970a91d..c3119f94 100644 --- a/NetEvent/Server/Modules/Themes/ThemesModule.cs +++ b/NetEvent/Server/Modules/Themes/ThemesModule.cs @@ -20,6 +20,7 @@ public override IEndpointRouteBuilder MapEndpoints(IEndpointRouteBuilder endpoin public override IServiceCollection RegisterModule(IServiceCollection builder) { + builder.AddMediatR(typeof(ThemesModule)); return builder; } diff --git a/NetEvent/Shared/Constants/OrganizationDataConstants.cs b/NetEvent/Shared/Constants/OrganizationDataConstants.cs new file mode 100644 index 00000000..50c34017 --- /dev/null +++ b/NetEvent/Shared/Constants/OrganizationDataConstants.cs @@ -0,0 +1,7 @@ +namespace NetEvent.Shared.Constants +{ + public static class OrganizationDataConstants + { + public const string CultureKey = "Culture"; + } +}