Added documentation and improved permission validation

This commit is contained in:
2024-08-18 12:01:17 +02:00
parent 893431536d
commit 4aaf126c9d
16 changed files with 202 additions and 35 deletions

View File

@@ -5,7 +5,7 @@ using HopFrame.Web;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<DatabaseContext>();
builder.Services.AddHopFrameServices<DatabaseContext>();
builder.Services.AddHopFrame<DatabaseContext>();
// Add services to the container.
builder.Services.AddRazorComponents()

View File

@@ -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<DatabaseContext>();
builder.Services.AddHopFrame<DatabaseContext>();
```

View File

@@ -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";
}

View File

@@ -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;
}

View File

@@ -2,6 +2,13 @@ using HopFrame.Database.Models;
namespace HopFrame.Security.Services;
/// <summary>
/// permission system:<br/>
/// - "*" -> all rights<br/>
/// - "group.[name]" -> group member<br/>
/// - "[namespace].[name]" -> single permission<br/>
/// - "[namespace].*" -> all permissions in the namespace
/// </summary>
public interface IPermissionService {
Task<bool> HasPermission(string permission, Guid user);

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -16,7 +16,7 @@
@using HopFrame.Web.Pages.Administration.Layout
<PageTitle>Groups</PageTitle>
<AuthorizedView Permission="@AdminPermissions.ViewGroups" RedirectIfUnauthorized="administration/login?redirect=/administration/groups"/>
<AuthorizedView Permission="@Security.AdminPermissions.ViewGroups" RedirectIfUnauthorized="administration/login?redirect=/administration/groups"/>
<GroupAddModal ReloadPage="Reload" @ref="_groupAddModal"/>
@@ -32,7 +32,7 @@
<input class="form-control me-2 input-dark" type="search" placeholder="Search" aria-label="Search" @bind="_searchText">
<BSButton Color="BSColor.Success" IsOutlined="true" type="submit">Search</BSButton>
</form>
<AuthorizedView Permission="@AdminPermissions.AddGroup">
<AuthorizedView Permission="@Security.AdminPermissions.AddGroup">
<BSButton IsSubmit="false" Color="BSColor.Success" Target="add-user" OnClick="() => _groupAddModal.ShowAsync()">Add Group</BSButton>
</AuthorizedView>
</div>
@@ -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() {

View File

@@ -2,7 +2,7 @@
@using BlazorStrap.V5
@inherits LayoutComponentBase
<AuthorizedView Permission="@AdminPermissions.IsAdmin" RedirectIfUnauthorized="administration/login" />
<AuthorizedView Permission="@Security.AdminPermissions.IsAdmin" RedirectIfUnauthorized="administration/login" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">

View File

@@ -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
}
};

View File

@@ -16,7 +16,7 @@
@using HopFrame.Web.Components.Administration
<PageTitle>Users</PageTitle>
<AuthorizedView Permission="@AdminPermissions.ViewUsers" RedirectIfUnauthorized="administration/login?redirect=/administration/users"/>
<AuthorizedView Permission="@Security.AdminPermissions.ViewUsers" RedirectIfUnauthorized="administration/login?redirect=/administration/users"/>
<UserAddModal @ref="_userAddModal" ReloadPage="Reload"/>
<UserEditModal @ref="_userEditModal" ReloadPage="Reload"/>
@@ -33,7 +33,7 @@
<input class="form-control me-2 input-dark" type="search" placeholder="Search" aria-label="Search" @bind="_searchText">
<BSButton Color="BSColor.Success" IsOutlined="true" type="submit">Search</BSButton>
</form>
<AuthorizedView Permission="@AdminPermissions.AddUser">
<AuthorizedView Permission="@Security.AdminPermissions.AddUser">
<BSButton IsSubmit="false" Color="BSColor.Success" Target="add-user" OnClick="() => _userAddModal.ShowAsync()">Add User</BSButton>
</AuthorizedView>
</div>
@@ -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() {

View File

@@ -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<DatabaseContext>();
builder.Services.AddHopFrame<DatabaseContext>();
```
4. Add the authentication middleware to your app
```csharp
app.UseMiddleware<AuthMiddleware>();
```
5. Add the HopFrame pages to your Razor components
```csharp
app.MapRazorComponents<App>()
.AddHopFrameAdminPages()
.AddInteractiveServerRenderMode();
```

View File

@@ -10,7 +10,7 @@ using Microsoft.Extensions.DependencyInjection;
namespace HopFrame.Web;
public static class ServiceCollectionExtensions {
public static IServiceCollection AddHopFrameServices<TDbContext>(this IServiceCollection services) where TDbContext : HopDbContextBase {
public static IServiceCollection AddHopFrame<TDbContext>(this IServiceCollection services) where TDbContext : HopDbContextBase {
services.AddHttpClient();
services.AddScoped<IAuthService, AuthService<TDbContext>>();
services.AddTransient<AuthMiddleware>();

View File

@@ -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<DatabaseContext>();
builder.Services.AddHopFrame<DatabaseContext>();
```
## 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<DatabaseContext>();
builder.Services.AddHopFrame<DatabaseContext>();
```
4. Add the authentication middleware to your app
```csharp
app.UseMiddleware<AuthMiddleware>();
```
5. Add the HopFrame pages to your Razor components
```csharp
app.MapRazorComponents<App>()
.AddHopFrameAdminPages()
.AddInteractiveServerRenderMode();
```