diff --git a/HopFrame.Web/AuthMiddleware.cs b/HopFrame.Web/AuthMiddleware.cs index ef18647..e0eccf8 100644 --- a/HopFrame.Web/AuthMiddleware.cs +++ b/HopFrame.Web/AuthMiddleware.cs @@ -30,6 +30,6 @@ public class AuthMiddleware(IAuthService auth, IPermissionService perms) : IMidd context.User.AddIdentity(new ClaimsIdentity(claims, HopFrameAuthentication.SchemeName)); } - await next.Invoke(context); + await next?.Invoke(context); } } \ No newline at end of file diff --git a/HopFrame.Web/Model/RegisterData.cs b/HopFrame.Web/Model/RegisterData.cs index 4164672..b987c5a 100644 --- a/HopFrame.Web/Model/RegisterData.cs +++ b/HopFrame.Web/Model/RegisterData.cs @@ -4,4 +4,8 @@ namespace HopFrame.Web.Model; public class RegisterData : UserRegister { public string RepeatedPassword { get; set; } + + public bool PasswordsMatch => Password == RepeatedPassword; + public bool PasswordIsValid => Password.Length >= 8; + public bool EmailIsValid => Email.Contains('@') && Email.Contains('.') && !Email.EndsWith('.'); } \ No newline at end of file diff --git a/HopFrame.Web/Model/UserAdd.cs b/HopFrame.Web/Model/UserAdd.cs new file mode 100644 index 0000000..da06366 --- /dev/null +++ b/HopFrame.Web/Model/UserAdd.cs @@ -0,0 +1,5 @@ +namespace HopFrame.Web.Model; + +public class UserAdd : RegisterData { + public string Group { get; set; } +} \ No newline at end of file diff --git a/HopFrame.Web/Pages/Administration/Components/UserAddModal.razor b/HopFrame.Web/Pages/Administration/Components/UserAddModal.razor new file mode 100644 index 0000000..d905830 --- /dev/null +++ b/HopFrame.Web/Pages/Administration/Components/UserAddModal.razor @@ -0,0 +1,69 @@ +@rendermode InteractiveServer + +@using BlazorStrap +@using static Microsoft.AspNetCore.Components.Web.RenderMode +@using BlazorStrap.Shared.Components.Modal +@using BlazorStrap.V5 +@using HopFrame.Database.Models +@using HopFrame.Security.Services +@using HopFrame.Web.Model + + + + Add user + +
+ E-Mail + +
+ +
+ Username + +
+ +
+ Password + +
+ +
+ Primary group + + + + @foreach (var group in _allGroups) { + + } + +
+
+ + Cancel + Save + +
+
+ +@inject IPermissionService Permissions + +@code { + [Parameter] public Action OnSubmit { get; set; } + + private IList _allGroups = new List(); + private UserAdd _user; + + private BSModalBase _modal; + + protected override async Task OnInitializedAsync() { + _allGroups = await Permissions.GetPermissionGroups(); + } + + public Task ShowAsync() { + return _modal.ShowAsync(); + } + + private void Submit() { + OnSubmit.Invoke(_user); + } +} \ No newline at end of file diff --git a/HopFrame.Web/Pages/Administration/Layout/AdminLayout.razor b/HopFrame.Web/Pages/Administration/Layout/AdminLayout.razor index 758e3d0..6489682 100644 --- a/HopFrame.Web/Pages/Administration/Layout/AdminLayout.razor +++ b/HopFrame.Web/Pages/Administration/Layout/AdminLayout.razor @@ -6,12 +6,12 @@ -
+
-
+
@Body diff --git a/HopFrame.Web/Pages/Administration/Layout/AdminMenu.razor b/HopFrame.Web/Pages/Administration/Layout/AdminMenu.razor index 18a272c..dd4fbdc 100644 --- a/HopFrame.Web/Pages/Administration/Layout/AdminMenu.razor +++ b/HopFrame.Web/Pages/Administration/Layout/AdminMenu.razor @@ -1,11 +1,17 @@ -@using BlazorStrap +@rendermode InteractiveServer + +@using BlazorStrap @using BlazorStrap.V5 +@using HopFrame.Security.Claims +@using HopFrame.Web.Pages.Administration.Components +@using HopFrame.Web.Services +@using static Microsoft.AspNetCore.Components.Web.RenderMode - + - @*logo*@ + logo HopFrame @@ -18,6 +24,14 @@ @nav.Value } + + + logged in as @Context?.User.Username + + + + + logout @@ -25,6 +39,8 @@ @inject NavigationManager Navigator +@inject ITokenContext Context +@inject IAuthService Auth @code { public static IDictionary Subpages = new Dictionary { @@ -35,4 +51,8 @@ public bool IsNavItemActive(string element) { return Navigator.Uri.Contains(element); } + + private async void Logout() { + Navigator.NavigateTo("login?redirect=/administration", true); + } } diff --git a/HopFrame.Web/Pages/Administration/UserEditPage.razor b/HopFrame.Web/Pages/Administration/UserEditPage.razor index f15eec3..b32a03b 100644 --- a/HopFrame.Web/Pages/Administration/UserEditPage.razor +++ b/HopFrame.Web/Pages/Administration/UserEditPage.razor @@ -16,32 +16,32 @@ Edit @User.Username -

Edit @User.Username (@User.Id)

+

Edit @User.Username (@User.Id)

@**@
- +
- +
- +
- +
- +
    • @@ -74,7 +74,7 @@
- +
    • diff --git a/HopFrame.Web/Pages/Administration/UsersPage.razor b/HopFrame.Web/Pages/Administration/UsersPage.razor index a1805db..1a68fac 100644 --- a/HopFrame.Web/Pages/Administration/UsersPage.razor +++ b/HopFrame.Web/Pages/Administration/UsersPage.razor @@ -13,11 +13,14 @@ @using Microsoft.AspNetCore.Components.Web @using HopFrame.Web.Components @using BlazorStrap.V5 +@using HopFrame.Web.Model @using HopFrame.Web.Pages.Administration.Components Users + +

      Users administration @@ -26,13 +29,14 @@

      - + Add User
      - + # @@ -97,7 +101,7 @@ @code { private IList _users = new List(); - private readonly IDictionary _userGroups = new Dictionary(); + private IDictionary _userGroups = new Dictionary(); private OrderType _currentOrder = OrderType.None; private OrderDirection _currentOrderDirection = OrderDirection.Asc; @@ -107,6 +111,8 @@ private bool _hasEditPrivileges = false; private bool _hasDeletePrivileges = false; + private UserAddModal _userAddModal; + protected override async Task OnInitializedAsync() { _users = await UserService.GetUsers(); @@ -119,8 +125,16 @@ _hasDeletePrivileges = await PermissionsService.HasPermission(AdminPermissions.DeleteUsers, Auth.User.Id); } - private void Reload() { - Navigator.Refresh(true); + private async Task Reload() { + _users = new List(); + _userGroups = new Dictionary(); + + _users = await UserService.GetUsers(); + + foreach (var user in _users) { + var groups = await PermissionsService.GetUserPermissionGroups(user); + _userGroups.Add(user.Id, groups.FirstOrDefault()); + } } private async Task Search() { @@ -176,6 +190,9 @@ if (result.IsConfirmed) { await UserService.DeleteUser(user); + await Reload(); + + StateHasChanged(); await Alerts.FireAsync(new SweetAlertOptions { Title = "Deleted!", @@ -183,8 +200,6 @@ Timer = 1500, ShowConfirmButton = false }); - - Reload(); } } @@ -204,4 +219,51 @@ Desc = 1 } + private async void CreateUser(UserAdd newUser) { + string errorMessage = null; + + if (_users.Any(user => user.Username == newUser.Username)) { + errorMessage = "Username is already taken!"; + } + else if (_users.Any(user => user.Email == newUser.Email)) { + errorMessage = "E-Mail is already taken!"; + } + else if (!newUser.PasswordIsValid) { + errorMessage = "The password needs to be at least 8 characters long!"; + } + else if (!newUser.EmailIsValid) { + errorMessage = "Invalid E-Mail address!"; + } + + if (!string.IsNullOrWhiteSpace(errorMessage)) { + await Alerts.FireAsync(new SweetAlertOptions { + Title = "Something went wrong!", + Text = errorMessage, + Icon = SweetAlertIcon.Error, + ShowConfirmButton = false, + Timer = 1500 + }); + + return; + } + + var user = await UserService.AddUser(newUser); + + if (!string.IsNullOrWhiteSpace(newUser.Group)) { + await PermissionsService.AddPermission(user, newUser.Group); + } + + await Reload(); + + StateHasChanged(); + + await Alerts.FireAsync(new SweetAlertOptions { + Title = "New user added!", + Icon = SweetAlertIcon.Success, + ShowConfirmButton = false, + Timer = 1500 + + }); + } + } \ No newline at end of file diff --git a/HopFrame.Web/Pages/Administration/UsersPage.razor.css b/HopFrame.Web/Pages/Administration/UsersPage.razor.css index fa12d28..445d132 100644 --- a/HopFrame.Web/Pages/Administration/UsersPage.razor.css +++ b/HopFrame.Web/Pages/Administration/UsersPage.razor.css @@ -1,13 +1,22 @@ .title { display: flex; flex-direction: row; - justify-content: space-between; + gap: 10px; + margin-bottom: 10px; +} + +#search { + margin-left: auto; } th, h3 { user-select: none; } +h3 { + color: white; +} + .reload, .sorter { cursor: pointer; } diff --git a/HopFrame.Web/Pages/Register.razor b/HopFrame.Web/Pages/Register.razor index 9da3da6..9869775 100644 --- a/HopFrame.Web/Pages/Register.razor +++ b/HopFrame.Web/Pages/Register.razor @@ -87,15 +87,15 @@ private void ValidateForm(object sender, ValidationRequestedEventArgs e) { _messages.Clear(); - if (RegisterData.Password != RegisterData.RepeatedPassword) { + if (!RegisterData.PasswordsMatch) { _messages.Add(() => RegisterData.RepeatedPassword, "Passwords doesn't mach"); } - if (RegisterData.Password.Length < 8) { + if (!RegisterData.PasswordIsValid) { _messages.Add(() => RegisterData.Password, "Password needs to be at least 8 characters long"); } - if (!RegisterData.Email.Contains("@") || !RegisterData.Email.Contains(".") || RegisterData.Email.EndsWith(".")) { + if (!RegisterData.EmailIsValid) { _messages.Add(() => RegisterData.Email, "Please enter a valid email address"); } } diff --git a/HopFrame.Web/Services/Implementation/AuthService.cs b/HopFrame.Web/Services/Implementation/AuthService.cs index 3e5dea7..09ca680 100644 --- a/HopFrame.Web/Services/Implementation/AuthService.cs +++ b/HopFrame.Web/Services/Implementation/AuthService.cs @@ -116,7 +116,8 @@ public class AuthService( old.UserId == token.UserId && old.CreatedAt + HopFrameAuthentication.AccessTokenTime < DateTime.Now) .ToList(); - context.Tokens.RemoveRange(oldAccessTokens); + if (oldAccessTokens.Count != 0) + context.Tokens.RemoveRange(oldAccessTokens); var oldRefreshTokens = context.Tokens .AsEnumerable() @@ -125,7 +126,8 @@ public class AuthService( old.UserId == token.UserId && old.CreatedAt + HopFrameAuthentication.RefreshTokenTime < DateTime.Now) .ToList(); - context.Tokens.RemoveRange(oldRefreshTokens); + if (oldRefreshTokens.Count != 0) + context.Tokens.RemoveRange(oldRefreshTokens); await context.SaveChangesAsync();