From 4aaf126c9d6d3b402c6e0c419827e23b7e5a4259 Mon Sep 17 00:00:00 2001 From: Leon Hoppe Date: Sun, 18 Aug 2024 12:01:17 +0200 Subject: [PATCH] Added documentation and improved permission validation --- FrontendTest/Program.cs | 2 +- HopFrame.Api/README.md | 28 ++++++++ HopFrame.Security/AdminPermissions.cs | 15 ++++ .../Authorization/PermissionValidator.cs | 8 ++- .../Services/IPermissionService.cs | 7 ++ HopFrame.Web/AdminPermissions.cs | 19 ++--- .../Administration/GroupAddModal.razor | 8 +-- .../Administration/UserAddModal.razor | 2 +- .../Administration/UserEditModal.razor | 12 ++-- .../Pages/Administration/GroupsPage.razor | 8 +-- .../Administration/Layout/AdminLayout.razor | 2 +- .../Administration/Layout/AdminMenu.razor | 4 +- .../Pages/Administration/UsersPage.razor | 8 +-- HopFrame.Web/README.md | 41 +++++++++++ HopFrame.Web/ServiceCollectionExtensions.cs | 2 +- README.md | 71 +++++++++++++++++++ 16 files changed, 202 insertions(+), 35 deletions(-) create mode 100644 HopFrame.Security/AdminPermissions.cs diff --git a/FrontendTest/Program.cs b/FrontendTest/Program.cs index 20102b4..af54f28 100644 --- a/FrontendTest/Program.cs +++ b/FrontendTest/Program.cs @@ -5,7 +5,7 @@ using HopFrame.Web; var builder = WebApplication.CreateBuilder(args); builder.Services.AddDbContext(); -builder.Services.AddHopFrameServices(); +builder.Services.AddHopFrame(); // Add services to the container. builder.Services.AddRazorComponents() diff --git a/HopFrame.Api/README.md b/HopFrame.Api/README.md index 2a42044..17d0ac1 100644 --- a/HopFrame.Api/README.md +++ b/HopFrame.Api/README.md @@ -1,2 +1,30 @@ # HopFrame API module This module contains some useful endpoints for user login / register management. + +## Ho to use the Web API version + +1. Add the HopFrame.Api library to your project: + + ``` + dotnet add package HopFrame.Api + ``` + +2. Create a DbContext that inherits the ``HopDbContext`` and add a data source + + ```csharp + public class DatabaseContext : HopDbContextBase { + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { + base.OnConfiguring(optionsBuilder); + + optionsBuilder.UseSqlite("..."); + } + } + ``` + +3. Add the DbContext and HopFrame to your services + + ```csharp + builder.Services.AddDbContext(); + builder.Services.AddHopFrame(); + ``` + diff --git a/HopFrame.Security/AdminPermissions.cs b/HopFrame.Security/AdminPermissions.cs new file mode 100644 index 0000000..7f45afc --- /dev/null +++ b/HopFrame.Security/AdminPermissions.cs @@ -0,0 +1,15 @@ +namespace HopFrame.Security; + +public static class AdminPermissions { + public const string IsAdmin = "hopframe.admin"; + + public const string ViewUsers = "hopframe.admin.users.view"; + public const string EditUser = "hopframe.admin.users.edit"; + public const string DeleteUser = "hopframe.admin.users.delete"; + public const string AddUser = "hopframe.admin.users.add"; + + public const string ViewGroups = "hopframe.admin.groups.view"; + public const string EditGroup = "hopframe.admin.groups.edit"; + public const string DeleteGroup = "hopframe.admin.groups.delete"; + public const string AddGroup = "hopframe.admin.groups.add"; +} \ No newline at end of file diff --git a/HopFrame.Security/Authorization/PermissionValidator.cs b/HopFrame.Security/Authorization/PermissionValidator.cs index d4280b9..d3a844b 100644 --- a/HopFrame.Security/Authorization/PermissionValidator.cs +++ b/HopFrame.Security/Authorization/PermissionValidator.cs @@ -6,12 +6,16 @@ public static class PermissionValidator { var permLow = permission.ToLower(); var permsLow = permissions.Select(perm => perm.ToLower()).ToArray(); - if (permsLow.Any(perm => perm == permLow || perm == "*")) return true; + if (permsLow.Any(perm => + perm == permLow || + (perm.Length > permLow.Length && perm.StartsWith(permLow) && perm.ToCharArray()[permLow.Length] == '.') || + perm == "*")) + return true; foreach (var perm in permsLow) { if (!perm.EndsWith(".*")) continue; - var permissionGroup = perm.Replace(".*", ""); + var permissionGroup = perm.Substring(0, perm.Length - 1); if (permLow.StartsWith(permissionGroup)) return true; } diff --git a/HopFrame.Security/Services/IPermissionService.cs b/HopFrame.Security/Services/IPermissionService.cs index 2071ce9..38a9000 100644 --- a/HopFrame.Security/Services/IPermissionService.cs +++ b/HopFrame.Security/Services/IPermissionService.cs @@ -2,6 +2,13 @@ using HopFrame.Database.Models; namespace HopFrame.Security.Services; +/// +/// permission system:
+/// - "*" -> all rights
+/// - "group.[name]" -> group member
+/// - "[namespace].[name]" -> single permission
+/// - "[namespace].*" -> all permissions in the namespace +///
public interface IPermissionService { Task HasPermission(string permission, Guid user); diff --git a/HopFrame.Web/AdminPermissions.cs b/HopFrame.Web/AdminPermissions.cs index 499cdb0..365ae87 100644 --- a/HopFrame.Web/AdminPermissions.cs +++ b/HopFrame.Web/AdminPermissions.cs @@ -1,15 +1,16 @@ namespace HopFrame.Web; +[Obsolete("Use HopFrame.Security.AdminPermissions instead")] public static class AdminPermissions { - public const string IsAdmin = "hopframe.admin"; + public const string IsAdmin = Security.AdminPermissions.IsAdmin; - public const string ViewUsers = "hopframe.admin.users.view"; - public const string EditUser = "hopframe.admin.users.edit"; - public const string DeleteUser = "hopframe.admin.users.delete"; - public const string AddUser = "hopframe.admin.users.add"; + public const string ViewUsers = Security.AdminPermissions.ViewUsers; + public const string EditUser = Security.AdminPermissions.EditUser; + public const string DeleteUser = Security.AdminPermissions.DeleteUser; + public const string AddUser = Security.AdminPermissions.AddUser; - public const string ViewGroups = "hopframe.admin.groups.view"; - public const string EditGroup = "hopframe.admin.groups.edit"; - public const string DeleteGroup = "hopframe.admin.groups.delete"; - public const string AddGroup = "hopframe.admin.groups.add"; + public const string ViewGroups = Security.AdminPermissions.ViewGroups; + public const string EditGroup = Security.AdminPermissions.EditGroup; + public const string DeleteGroup = Security.AdminPermissions.DeleteGroup; + public const string AddGroup = Security.AdminPermissions.AddGroup; } \ No newline at end of file diff --git a/HopFrame.Web/Components/Administration/GroupAddModal.razor b/HopFrame.Web/Components/Administration/GroupAddModal.razor index 95c710b..d1e2723 100644 --- a/HopFrame.Web/Components/Administration/GroupAddModal.razor +++ b/HopFrame.Web/Components/Administration/GroupAddModal.razor @@ -167,7 +167,7 @@ } if (_isEdit) { - if (!(await Permissions.HasPermission(AdminPermissions.EditGroup, Context.User.Id))) { + if (!(await Permissions.HasPermission(Security.AdminPermissions.EditGroup, Context.User.Id))) { await NoEditPermissions(); return; } @@ -202,7 +202,7 @@ } if (_isEdit) { - if (!(await Permissions.HasPermission(AdminPermissions.EditGroup, Context.User.Id))) { + if (!(await Permissions.HasPermission(Security.AdminPermissions.EditGroup, Context.User.Id))) { await NoEditPermissions(); return; } @@ -219,7 +219,7 @@ private async Task AddGroup() { if (_isEdit) { - if (!(await Permissions.HasPermission(AdminPermissions.EditGroup, Context.User.Id))) { + if (!(await Permissions.HasPermission(Security.AdminPermissions.EditGroup, Context.User.Id))) { await NoEditPermissions(); return; } @@ -239,7 +239,7 @@ return; } - if (!(await Permissions.HasPermission(AdminPermissions.AddGroup, Context.User.Id))) { + if (!(await Permissions.HasPermission(Security.AdminPermissions.AddGroup, Context.User.Id))) { await NoAddPermissions(); return; } diff --git a/HopFrame.Web/Components/Administration/UserAddModal.razor b/HopFrame.Web/Components/Administration/UserAddModal.razor index 2a743af..96ff62b 100644 --- a/HopFrame.Web/Components/Administration/UserAddModal.razor +++ b/HopFrame.Web/Components/Administration/UserAddModal.razor @@ -69,7 +69,7 @@ } private async Task AddUser() { - if (!(await Permissions.HasPermission(AdminPermissions.AddUser, Auth.User.Id))) { + if (!(await Permissions.HasPermission(Security.AdminPermissions.AddUser, Auth.User.Id))) { await NoAddPermissions(); return; } diff --git a/HopFrame.Web/Components/Administration/UserEditModal.razor b/HopFrame.Web/Components/Administration/UserEditModal.razor index 77f82f6..930dba6 100644 --- a/HopFrame.Web/Components/Administration/UserEditModal.razor +++ b/HopFrame.Web/Components/Administration/UserEditModal.razor @@ -118,7 +118,7 @@ private string _permissionToAdd; public async Task ShowAsync(User user) { - if (!(await Permissions.HasPermission(AdminPermissions.EditUser, Auth.User.Id))) { + if (!(await Permissions.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id))) { await NoEditPermissions(); return; } @@ -130,7 +130,7 @@ } private async Task AddGroup() { - if (!(await Permissions.HasPermission(AdminPermissions.EditUser, Auth.User.Id))) { + if (!(await Permissions.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id))) { await NoEditPermissions(); return; } @@ -158,7 +158,7 @@ } private async Task RemoveGroup(PermissionGroup group) { - if (!(await Permissions.HasPermission(AdminPermissions.EditUser, Auth.User.Id))) { + if (!(await Permissions.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id))) { await NoEditPermissions(); return; } @@ -186,7 +186,7 @@ } private async Task AddPermission() { - if (!(await Permissions.HasPermission(AdminPermissions.EditUser, Auth.User.Id))) { + if (!(await Permissions.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id))) { await NoEditPermissions(); return; } @@ -213,7 +213,7 @@ } private async Task RemovePermission(Permission perm) { - if (!(await Permissions.HasPermission(AdminPermissions.EditUser, Auth.User.Id))) { + if (!(await Permissions.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id))) { await NoEditPermissions(); return; } @@ -241,7 +241,7 @@ } private async void EditUser() { - if (!(await Permissions.HasPermission(AdminPermissions.EditUser, Auth.User.Id))) { + if (!(await Permissions.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id))) { await NoEditPermissions(); return; } diff --git a/HopFrame.Web/Pages/Administration/GroupsPage.razor b/HopFrame.Web/Pages/Administration/GroupsPage.razor index 1308db9..9040d63 100644 --- a/HopFrame.Web/Pages/Administration/GroupsPage.razor +++ b/HopFrame.Web/Pages/Administration/GroupsPage.razor @@ -16,7 +16,7 @@ @using HopFrame.Web.Pages.Administration.Layout Groups - + @@ -32,7 +32,7 @@ Search - + Add Group @@ -112,8 +112,8 @@ protected override async Task OnInitializedAsync() { _groups = await Permissions.GetPermissionGroups(); - _hasEditPrivileges = await Permissions.HasPermission(AdminPermissions.EditGroup, Auth.User.Id); - _hasDeletePrivileges = await Permissions.HasPermission(AdminPermissions.DeleteGroup, Auth.User.Id); + _hasEditPrivileges = await Permissions.HasPermission(Security.AdminPermissions.EditGroup, Auth.User.Id); + _hasDeletePrivileges = await Permissions.HasPermission(Security.AdminPermissions.DeleteGroup, Auth.User.Id); } private async Task Reload() { diff --git a/HopFrame.Web/Pages/Administration/Layout/AdminLayout.razor b/HopFrame.Web/Pages/Administration/Layout/AdminLayout.razor index e3a4611..0e92e3e 100644 --- a/HopFrame.Web/Pages/Administration/Layout/AdminLayout.razor +++ b/HopFrame.Web/Pages/Administration/Layout/AdminLayout.razor @@ -2,7 +2,7 @@ @using BlazorStrap.V5 @inherits LayoutComponentBase - + diff --git a/HopFrame.Web/Pages/Administration/Layout/AdminMenu.razor b/HopFrame.Web/Pages/Administration/Layout/AdminMenu.razor index 275afd2..233cf52 100644 --- a/HopFrame.Web/Pages/Administration/Layout/AdminMenu.razor +++ b/HopFrame.Web/Pages/Administration/Layout/AdminMenu.razor @@ -53,13 +53,13 @@ Name = "Users", Url = "administration/users", Description = "On this page you can manage all user accounts.", - Permission = AdminPermissions.ViewUsers + Permission = Security.AdminPermissions.ViewUsers }, new () { Name = "Groups", Url = "administration/groups", Description = "On this page you can view, create, edit and delete permission groups.", - Permission = AdminPermissions.ViewGroups + Permission = Security.AdminPermissions.ViewGroups } }; diff --git a/HopFrame.Web/Pages/Administration/UsersPage.razor b/HopFrame.Web/Pages/Administration/UsersPage.razor index 812cdf7..0bd7e96 100644 --- a/HopFrame.Web/Pages/Administration/UsersPage.razor +++ b/HopFrame.Web/Pages/Administration/UsersPage.razor @@ -16,7 +16,7 @@ @using HopFrame.Web.Components.Administration Users - + @@ -33,7 +33,7 @@ Search - + Add User @@ -123,8 +123,8 @@ _userGroups.Add(user.Id, groups.LastOrDefault()); } - _hasEditPrivileges = await PermissionsService.HasPermission(AdminPermissions.EditUser, Auth.User.Id); - _hasDeletePrivileges = await PermissionsService.HasPermission(AdminPermissions.DeleteUser, Auth.User.Id); + _hasEditPrivileges = await PermissionsService.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id); + _hasDeletePrivileges = await PermissionsService.HasPermission(Security.AdminPermissions.DeleteUser, Auth.User.Id); } private async Task Reload() { diff --git a/HopFrame.Web/README.md b/HopFrame.Web/README.md index ec39f17..38d2b5d 100644 --- a/HopFrame.Web/README.md +++ b/HopFrame.Web/README.md @@ -1,2 +1,43 @@ # HopFrame Web module This module contains useful helpers for Blazor Apps and an Admin Dashboard. + +## How to use the Blazor API + +1. Add the HopFrame.Web library to your project + + ``` + dotnet add package HopFrame.Web + ``` + +2. Create a DbContext that inherits the ``HopDbContext`` and add a data source + + ```csharp + public class DatabaseContext : HopDbContextBase { + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { + base.OnConfiguring(optionsBuilder); + + optionsBuilder.UseSqlite("..."); + } + } + ``` + +3. Add the DbContext and HopFrame to your services + + ```csharp + builder.Services.AddDbContext(); + builder.Services.AddHopFrame(); + ``` + +4. Add the authentication middleware to your app + + ```csharp + app.UseMiddleware(); + ``` + +5. Add the HopFrame pages to your Razor components + + ```csharp + app.MapRazorComponents() + .AddHopFrameAdminPages() + .AddInteractiveServerRenderMode(); + ``` diff --git a/HopFrame.Web/ServiceCollectionExtensions.cs b/HopFrame.Web/ServiceCollectionExtensions.cs index 8e3c453..c6438d5 100644 --- a/HopFrame.Web/ServiceCollectionExtensions.cs +++ b/HopFrame.Web/ServiceCollectionExtensions.cs @@ -10,7 +10,7 @@ using Microsoft.Extensions.DependencyInjection; namespace HopFrame.Web; public static class ServiceCollectionExtensions { - public static IServiceCollection AddHopFrameServices(this IServiceCollection services) where TDbContext : HopDbContextBase { + public static IServiceCollection AddHopFrame(this IServiceCollection services) where TDbContext : HopDbContextBase { services.AddHttpClient(); services.AddScoped>(); services.AddTransient(); diff --git a/README.md b/README.md index 84f0ab2..b7ed1b7 100644 --- a/README.md +++ b/README.md @@ -6,3 +6,74 @@ A simple backend management api for ASP.NET Core Web APIs - [x] User authentication - [x] Permission management - [x] Frontend dashboards + +# Usage +There are two different versions of HopFrame, either the Web API version or the full Blazor web version. + +## Ho to use the Web API version + +1. Add the HopFrame.Api library to your project: + + ``` + dotnet add package HopFrame.Api + ``` + +2. Create a DbContext that inherits the ``HopDbContext`` and add a data source + + ```csharp + public class DatabaseContext : HopDbContextBase { + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { + base.OnConfiguring(optionsBuilder); + + optionsBuilder.UseSqlite("..."); + } + } + ``` + +3. Add the DbContext and HopFrame to your services + + ```csharp + builder.Services.AddDbContext(); + builder.Services.AddHopFrame(); + ``` + +## How to use the Blazor API + +1. Add the HopFrame.Web library to your project + + ``` + dotnet add package HopFrame.Web + ``` + +2. Create a DbContext that inherits the ``HopDbContext`` and add a data source + + ```csharp + public class DatabaseContext : HopDbContextBase { + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { + base.OnConfiguring(optionsBuilder); + + optionsBuilder.UseSqlite("..."); + } + } + ``` + +3. Add the DbContext and HopFrame to your services + + ```csharp + builder.Services.AddDbContext(); + builder.Services.AddHopFrame(); + ``` + +4. Add the authentication middleware to your app + + ```csharp + app.UseMiddleware(); + ``` + +5. Add the HopFrame pages to your Razor components + + ```csharp + app.MapRazorComponents() + .AddHopFrameAdminPages() + .AddInteractiveServerRenderMode(); + ```