Merge branch 'feature/documentation' into 'dev'

Resolve "Documentation"

Closes #18

See merge request leon.hoppe/HopFrame!10
This commit is contained in:
2024-11-22 13:17:35 +00:00
32 changed files with 565 additions and 356 deletions

View File

@@ -1,26 +0,0 @@
@startuml ApiModels
namespace HopFrame.Security {
class UserLogin {
+Email: string
+Password: string
}
class UserRegister {
+Username: string
+Email: string
+Password: string
}
}
namespace HopFrame.Api {
class SingleValueResult<TValue> {
+Value: TValue
}
class UserPasswordValidation {
+Password: string
}
}
@enduml

View File

@@ -1,37 +0,0 @@
@startuml BaseModels
set namespaceSeparator none
namespace HopFrame.Database {
class User {
+Id: Guid
+Username: string
+Email: string
+CreatedAt: DateTime
+Permissions: IList<Permission>
}
class Permission {
+Id: long
+PermissionName: string
+Owner: Guid
+GrantedAt: DateTime
}
class PermissionGroup {
+Name: string
+IsDefaultGroup: bool
+Description: string
+CreatedAt: DateTime
+Permissions: IList<Permission>
}
interface IPermissionOwner {}
}
IPermissionOwner <|-- User
IPermissionOwner <|-- PermissionGroup
User .. Permission
PermissionGroup .. Permission
@enduml

View File

@@ -1,38 +0,0 @@
@startuml DatabaseModels
set namespaceSeparator none
namespace HopFrame.Database {
class UserEntry {
+Id: string
+Username: string
+Email: string
+Password: string
+CreatedAt: DateTime
}
class TokenEntry {
+Type: int
+Token: string
+UserId: string
+CreatedAt: DateTime
}
class PermissionEntry {
+RecordId: long
+PermissionText: string
+UserId: string
+GrantedAt: DateTime
}
class GroupEntry {
+Name: string
+Default: bool
+Description: string
+CreatedAt: DateTime
}
}
UserEntry *-- TokenEntry
UserEntry *-- PermissionEntry
@enduml

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,13 +0,0 @@
# HopFrame documentation
These sides contain all documentation available for the HopFrame modules
## Content
| Topic | Description | Document |
|----------|------------------------------------------------|-----------------------|
| Models | All models used by the HopFrame | [link](./models.md) |
| Services | All services provided by the HopFrame | [link](./services.md) |
| Usage | How to properly implement the HopFrame modules | [link](./usage.md) |
## Dependencies
Both the HopFrame.Api and HopFrame.Web modules are dependent on the HopFrame.Database and HopFrame.Security modules.
So all models and services provided by these modules are available in the other modules as well.

30
docs/api/authorization.md Normal file
View File

@@ -0,0 +1,30 @@
# HopFrame Authentication
With the provided HopFrame services, you can secure your endpoints so that only logged-in users or users with the right permissions can access the endpoint.
## Usage
You can secure your endpoints by adding the `Authorized` attribute.
```csharp
// Everyone can access this endpoint
[HttpGet("hello")]
public ActionResult<string> HelloWorld() {
return "Hello, World!";
}
```
```csharp
// Only logged-in users can access this endpoint
[HttpGet("hello"), Authorized]
public ActionResult<string> HelloWorld() {
return "Hello, World!";
}
```
```csharp
// Only logged-in users with the specified permissions can access this endpoint
[HttpGet("hello"), Authorized("test.permission", "test.permission.another")]
public ActionResult<string> HelloWorld() {
return "Hello, World!";
}
```

21
docs/api/endpoints.md Normal file
View File

@@ -0,0 +1,21 @@
# HopFrame Endpoints
HopFrame currently only supports endpoints for authentication out of the box.
> **Hint:** with the help of the [repositories](../repositories.md) you can very easily create missing endpoints for HopFrame components yourself.
## All currently supported endpoints
> **Hint:** you can use the build-in [swagger](https://swagger.io/) ui to explore and test all endpoints of your application __including__ HopFrame endpoints.
### SecurityController
Base endpoint: `/api/v1/authentication`\
**Important:** All primitive data types (including `string`) are return as a [`SingleValueResult`](./models.md#SingleValueResult)
| Method | Endpoint | Payload | Returns |
|--------|---------------|--------------------------------------------------------------|-----------------------|
| PUT | /login | [UserLogin](../models.md#UserLogin) | access token (string) |
| POST | /register | [UserRegister](../models.md#UserRegister) | access token (string) |
| GET | /authenticate | | access token (string) |
| DELETE | /logout | | |
| DELETE | /delete | [UserPasswordValidation](./models.md#UserPasswordValidation) | |

16
docs/api/installation.md Normal file
View File

@@ -0,0 +1,16 @@
# Ho to use the Web API version
This Installation adds all HopFrame [endpoints](./endpoints.md) and [repositories](../repositories.md) to the application.
1. Add the HopFrame.Api library to your project:
```
dotnet add package HopFrame.Api
```
2. Create a [DbContext](../database.md) that inherits the ``HopDbContext`` and add a data source
3. Add the HopFrame services to your application, provide the previously created `DatabaseContext` that inherits from `HopDbContextBase`
```csharp
builder.Services.AddHopFrame<DatabaseContext>();
```

33
docs/api/logicresults.md Normal file
View File

@@ -0,0 +1,33 @@
# LogicResults
LogicResults provide another layer of abstraction above the ActionResults.
They help you sending the right `HttpStatusCode` with the right data.
## Usage
1. Create an endpoint that returns an `ActionResult`:
```csharp
[HttpGet("hello")]
public ActionResult<string> Hello() {
return new ActionResult<string>("Hello, World!");
}
```
2. Now instead of directly returning the `ActionResult`, return a `LogicResult`
```csharp
[HttpGet("hello")]
public ActionResult<string> Hello() {
return LogicResult<string>.Ok("Hello, World!");
}
```
3. This allows you to very easily change the return type by simply calling the right function
```csharp
[HttpGet("hello")]
public ActionResult<string> Hello() {
if (!Auth.IsLoggedIn)
return LogicResult<string>.Forbidden();
return LogicResult<string>.Ok("Hello, World!");
}
```
> **Hint:** You can also provide an error message for status codes that are not in the 200 range.

16
docs/api/models.md Normal file
View File

@@ -0,0 +1,16 @@
# HopFrame Models
All models used by the RestAPI are listed below
## SingleValueResult
```csharp
public struct SingleValueResult<TValue>(TValue value) {
public TValue Value { get; set; } = value;
}
```
## UserPasswordValidation
```csharp
public sealed class UserPasswordValidation {
public string Password { get; set; }
}
```

133
docs/blazor/admin.md Normal file
View File

@@ -0,0 +1,133 @@
# HopFrame Admin Pages
Admin pages can be defined through a `AdminContext` similar to how a `DbContext` is defined. They generate administration pages like [`/administration/users`](./pages.md)
simply by reading the structure of the provided model and optionally some additional configuration.
> **Fun fact:** The already existing pages `/administration/users` and `/administration/groups` are also generated using an internal `AdminContext`.
## Usage
1. Create a class that inherits the `AdminPagesContext` base class
```csharp
public class AdminContext : AdminPagesContext {
}
```
2. Add your admin pages as properties to the class
```csharp
public class AdminContext : AdminPagesContext {
public AdminPage<Address> Addresses { get; set; }
public AdminPage<Employee> Employees { get; set; }
}
```
3. **Optionally** you can further configure your pages in the `OnModelCreating` method
```csharp
public class AdminContext : AdminPagesContext {
public AdminPage<Address> Addresses { get; set; }
public AdminPage<Employee> Employees { get; set; }
public override void OnModelCreating(IAdminContextGenerator generator) {
base.OnModelCreating(generator);
generator.Page<Employee>()
.Property(e => e.Address)
.IsSelector();
generator.Page<Address>()
.Property(a => a.Employee)
.Ignore();
generator.Page<Address>()
.Property(a => a.AddressId)
.IsSelector<Employee>()
.Parser<Employee>((model, e) => model.AddressId = e.EmployeeId);
generator.Page<Employee>()
.ConfigureRepository<EmployeeProvider>()
.ListingProperty(e => e.Name);
generator.Page<Address>()
.ConfigureRepository<AddressProvider>()
.ListingProperty(a => a.City);
}
}
```
4. **Optionally** you can also add some of the following attributes to your classes / properties to further configure the admin pages:\
\
Attributes for classes:
```csharp
[AttributeUsage(AttributeTargets.Class)]
public sealed class AdminButtonConfigAttribute(bool showCreateButton = true, bool showDeleteButton = true, bool showUpdateButton = true) : Attribute {
public bool ShowCreateButton { get; set; } = showCreateButton;
public bool ShowDeleteButton { get; set; } = showDeleteButton;
public bool ShowUpdateButton { get; set; } = showUpdateButton;
}
```
```csharp
[AttributeUsage(AttributeTargets.Class)]
public sealed class AdminPermissionsAttribute(string view = null, string create = null, string update = null, string delete = null) : Attribute {
public AdminPagePermissions Permissions { get; set; } = new() {
Create = create,
Update = update,
Delete = delete,
View = view
};
}
```
```csharp
[AttributeUsage(AttributeTargets.Class)]
public class AdminUrlAttribute(string url) : Attribute {
public string Url { get; set; } = url;
}
```
Attributes for properties:
```csharp
[AttributeUsage(AttributeTargets.Property)]
public sealed class AdminHideValueAttribute : Attribute;
```
```csharp
[AttributeUsage(AttributeTargets.Property)]
public sealed class AdminIgnoreAttribute(bool onlyForListing = false) : Attribute {
public bool OnlyForListing { get; set; } = onlyForListing;
}
```
```csharp
[AttributeUsage(AttributeTargets.Property)]
public sealed class AdminPrefixAttribute(string prefix) : Attribute {
public string Prefix { get; set; } = prefix;
}
```
```csharp
[AttributeUsage(AttributeTargets.Property)]
public sealed class AdminUneditableAttribute : Attribute;
```
```csharp
[AttributeUsage(AttributeTargets.Property)]
public class AdminUniqueAttribute : Attribute;
```
```csharp
[AttributeUsage(AttributeTargets.Property)]
public sealed class AdminUnsortableAttribute : Attribute;
```
```csharp
[AttributeUsage(AttributeTargets.Property)]
public sealed class ListingPropertyAttribute : Attribute;
```

20
docs/blazor/auth.md Normal file
View File

@@ -0,0 +1,20 @@
# Auth Service
The `IAuthService` provides some useful methods to handle user authentication (login/register).
## Usage
Simply define the `IAuthService` as a dependency
```csharp
public interface IAuthService {
Task Register(UserRegister register);
Task<bool> Login(UserLogin login);
Task Logout();
Task<Token> RefreshLogin();
Task<bool> IsLoggedIn();
}
```
## Automatically refresh user sessions
1. Make sure you have implemented the `AuthMiddleware` how it's described in step 5 of the [installation](./installation.md).
2. After that, the access token of the user gets automatically refreshed as long as the refresh token is valid.

View File

@@ -0,0 +1,20 @@
# HopFrame Authentication
With the provided HopFrame services, you can secure your blazor pages so that only logged-in users or users with the right permissions can access the page.
## Usage
You can secure your Blazor pages by using the `AuthorizedView` component.
Everything placed inside this component will only be displayed if the authorization was successful.
You can also redirect the user if the authorization fails by specifying a `RedirectIfUnauthorized` url.
```html
<!-- You can either specify one 'Permission', multiple 'Permissions' or none if the user only needs to be logged-in -->
<AuthorizedView Permission="test.permission">
<p>This paragraph is only visible if the user is logged-in and has the required permission</p>
</AuthorizedView>
```
```html
<!-- This component will redirect the user to the login page if the user is unauthorized -->
<AuthorizedView RedirectIfUnauthorized="/login" />
```

View File

@@ -0,0 +1,36 @@
## How to use the Blazor API
This Installation adds all HopFrame [pages](./pages.md) and [repositories](../repositories.md) to the application.
1. Add the HopFrame.Web library to your project
```
dotnet add package HopFrame.Web
```
2. Create a [DbContext](../database.md) that inherits the ``HopDbContext`` and add a data source
3. Add the HopFrame services to your application, provide the previously created `DatabaseContext` that inherits from `HopDbContextBase`
```csharp
builder.Services.AddHopFrame<DatabaseContext>();
```
4. **Optional:** You can also add your [AdminContext](./admin.md)
```csharp
builder.Services.AddAdminContext<AdminContext>();
```
5. Add the authentication middleware to your app
```csharp
app.UseMiddleware<AuthMiddleware>();
```
6. Add the HopFrame pages to your Razor components
```csharp
app.MapRazorComponents<App>()
.AddHopFrameAdminPages()
.AddInteractiveServerRenderMode();
```

14
docs/blazor/pages.md Normal file
View File

@@ -0,0 +1,14 @@
# HopFrame Pages
By default, the HopFrame provides some blazor pages for managing user accounts and permissions
## All currently supported blazor pages
| Page | Endpoint | Permission | Usage |
|-----------------|------------------------|----------------------------|--------------------------------------------------------------------------------------------------------|
| Admin Dashboard | /administration | hopframe.admin | This page provides an overview to all admin pages built-in and created by [AdminContexts](./admin.md). |
| Admin Login | /administration/login | | This page is a simple login screen so no login screen needs to be created to access the admin pages. |
| User Dashboard | /administration/users | hopframe.admin.users.view | This page serves as a management site for all users and their permissions. |
| Group Dashboard | /administration/groups | hopframe.admin.groups.view | This page serves as a management site for all groups and their permissions. |
> **Hint:** All pages created by [AdminContexts](./admin.md) are also under the `/administration/` location. This can unfortunately __not__ be changed at the moment.

35
docs/database.md Normal file
View File

@@ -0,0 +1,35 @@
# Database initialization
You also need to initialize the data source with the tables from HopFrame.
## Create a DbContext
1. Create a c# class that inherits from the `HopDbContextBase` and add a data source (In the example Sqlite is used)\
**IMPORTANT:** You need to leave the `base.OnConfiguring(optionsBuilder)` in place so the HopFrame model relations are set correctly.
```csharp
public class DatabaseContext : HopDbContextBase {
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseSqlite("...");
}
}
```
2. Register the `DatabaseContext` as a service
```csharp
builder.Services.AddDbContext<DatabaseContext>();
```
3. Create a database migration
```bash
dotnet ef migrations add Initial
```
4. Apply the migration to the data source
```bash
dotnet ef database update
```

View File

@@ -1,21 +1,71 @@
# Models for HopFrame # HopFrame base models
All models listed below are part of the core HopFrame components and accessible in all installation variations
This page shows all models that HopFrame uses. > **Note:** All properties of the models that are `virtual` are relational properties and don't directly correspond to columns in the database.
## User
```csharp
public class User : IPermissionOwner {
public Guid Id { get; init; }
public string Username { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public DateTime CreatedAt { get; set; }
public virtual List<Permission> Permissions { get; set; }
public virtual List<Token> Tokens { get; set; }
}
```
## Base Models ## PermissionGroup
These are the models used by the various database services. ```csharp
public class PermissionGroup : IPermissionOwner {
public string Name { get; init; }
public bool IsDefaultGroup { get; set; }
public string Description { get; set; }
public DateTime CreatedAt { get; set; }
public virtual List<Permission> Permissions { get; set; }
}
```
![](./Diagrams/Models/img/BaseModels.svg) ## Permission
```csharp
public class Permission {
public long Id { get; init; }
public string PermissionName { get; set; }
public DateTime GrantedAt { get; set; }
public virtual User User { get; set; }
public virtual PermissionGroup Group { get; set; }
}
```
## Token
```csharp
public class Token {
public int Type { get; set; }
public Guid Content { get; set; }
public DateTime CreatedAt { get; set; }
public virtual User Owner { get; set; }
}
```
## API Models ## UserLogin
These are the models used by the REST API and the Blazor API. ```csharp
public class UserLogin {
public string Email { get; set; }
public string Password { get; set; }
}
```
![](./Diagrams/Models/img/ApiModels.svg) ## UserRegister
```csharp
public class UserRegister {
public string Username { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
```
## IPermissionOwner
## Database Models ```csharp
These are the models that correspond to the scheme in the Database public interface IPermissionOwner;
```
![](./Diagrams/Models/img/DatabaseModels.svg)

25
docs/readme.md Normal file
View File

@@ -0,0 +1,25 @@
# HopFrame Documentation
The HopFrame comes in two variations, you can eiter only use the backend with some basic endpoints or with fully fledged blazor pages for managing HopFrame components.
## Shared HopFrame Modules
- [Database](./database.md)
- [Repositories](./repositories.md)
- [Base Models](./models.md)
## HopFrame Web API
- [Installation](./api/installation.md)
- [Endpoints](./api/endpoints.md)
- [Authorization](./api/authorization.md)
- [Models](./api/models.md)
- [LogicResults](./api/logicresults.md)
## HopFrame Blazor library
- [Installation](./blazor/installation.md)
- [Pages](./blazor/pages.md)
- [Authorization](./blazor/authorization.md)
- [Auth Service](./blazor/auth.md)
- [Admin Context](./blazor/admin.md)

75
docs/repositories.md Normal file
View File

@@ -0,0 +1,75 @@
# HopFrame Repositories
The HopFrame provies repositories for the various build in database models as an abstraction around the `HopDbContext` to ensure, that the data is proccessed and saved correctly.
## Overview
The repositories can also be used by simply defining them as a dependency in your service / controller.
### User Repository
```csharp
public interface IUserRepository {
Task<IList<User>> GetUsers();
Task<User> GetUser(Guid userId);
Task<User> GetUserByEmail(string email);
Task<User> GetUserByUsername(string username);
Task<User> AddUser(User user);
Task UpdateUser(User user);
Task DeleteUser(User user);
Task<bool> CheckUserPassword(User user, string password);
Task ChangePassword(User user, string password);
}
```
### Group Repository
```csharp
public interface IGroupRepository {
Task<IList<PermissionGroup>> GetPermissionGroups();
Task<IList<PermissionGroup>> GetDefaultGroups();
Task<IList<PermissionGroup>> GetUserGroups(User user);
Task<PermissionGroup> GetPermissionGroup(string name);
Task EditPermissionGroup(PermissionGroup group);
Task<PermissionGroup> CreatePermissionGroup(PermissionGroup group);
Task DeletePermissionGroup(PermissionGroup group);
}
```
### Permission Repository
```csharp
public interface IPermissionRepository {
Task<bool> HasPermission(IPermissionOwner owner, params string[] permissions);
Task<Permission> AddPermission(IPermissionOwner owner, string permission);
Task RemovePermission(IPermissionOwner owner, string permission);
Task<IList<string>> GetFullPermissions(IPermissionOwner owner);
}
```
### Token Repository
```csharp
public interface ITokenRepository {
Task<Token> GetToken(string content);
Task<Token> CreateToken(int type, User owner);
Task DeleteUserTokens(User owner);
}
```

View File

@@ -1,145 +0,0 @@
# HopFrame Services
This page describes all services provided by the HopFrame.
You can use these services by specifying them as a dependency. All of them are scoped dependencies.
## HopFrame.Security
### ITokenContext
This service provides the information given by the current request
```csharp
public interface ITokenContext {
bool IsAuthenticated { get; }
User User { get; }
Guid AccessToken { get; }
}
```
### IUserService
This service simplifies the data access of the user table in the database.
```csharp
public interface IUserService {
Task<IList<User>> GetUsers();
Task<User> GetUser(Guid userId);
Task<User> GetUserByEmail(string email);
Task<User> GetUserByUsername(string username);
Task<User> AddUser(UserRegister user);
Task UpdateUser(User user);
Task DeleteUser(User user);
Task<bool> CheckUserPassword(User user, string password);
Task ChangePassword(User user, string password);
}
```
### IPermissionService
This service handles all permission and group interactions with the data source.
```csharp
public interface IPermissionService {
Task<bool> HasPermission(string permission, Guid user);
Task<IList<PermissionGroup>> GetPermissionGroups();
Task<PermissionGroup> GetPermissionGroup(string name);
Task EditPermissionGroup(PermissionGroup group);
Task<IList<PermissionGroup>> GetUserPermissionGroups(User user);
Task RemoveGroupFromUser(User user, PermissionGroup group);
Task<PermissionGroup> CreatePermissionGroup(string name, bool isDefault = false, string description = null);
Task DeletePermissionGroup(PermissionGroup group);
Task<Permission> GetPermission(string name, IPermissionOwner owner);
Task AddPermission(IPermissionOwner owner, string permission);
Task RemovePermission(Permission permission);
Task<string[]> GetFullPermissions(string user);
}
```
## HopFrame.Api
### LogicResult
Logic result is an extension of the ActionResult for an ApiController. It provides simple Http status results with either a message or data by specifying the generic type.
```csharp
public class LogicResult : ILogicResult {
public static LogicResult Ok();
public static LogicResult BadRequest();
public static LogicResult BadRequest(string message);
public static LogicResult Forbidden();
public static LogicResult Forbidden(string message);
public static LogicResult NotFound();
public static LogicResult NotFound(string message);
public static LogicResult Conflict();
public static LogicResult Conflict(string message);
public static LogicResult Forward(LogicResult result);
public static LogicResult Forward<T>(ILogicResult<T> result);
public static implicit operator ActionResult(LogicResult v);
}
public class LogicResult<T> : ILogicResult<T> {
public static LogicResult<T> Ok();
public static LogicResult<T> Ok(T result);
...
}
```
### IAuthLogic
This service handles all logic needed to provide the authentication endpoints by using the LogicResults.
```csharp
public interface IAuthLogic {
Task<LogicResult<SingleValueResult<string>>> Login(UserLogin login);
Task<LogicResult<SingleValueResult<string>>> Register(UserRegister register);
Task<LogicResult<SingleValueResult<string>>> Authenticate();
Task<LogicResult> Logout();
Task<LogicResult> Delete(UserPasswordValidation validation);
}
```
## HopFrame.Web
### IAuthService
This service handles all the authentication like login or register. It properly creates all tokens so the user can be identified
```csharp
public interface IAuthService {
Task Register(UserRegister register);
Task<bool> Login(UserLogin login);
Task Logout();
Task<TokenEntry> RefreshLogin();
Task<bool> IsLoggedIn();
}
```

View File

@@ -1,70 +0,0 @@
# HopFrame 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();
```

View File

@@ -8,9 +8,18 @@ public interface IAuthLogic {
Task<LogicResult<SingleValueResult<string>>> Register(UserRegister register); Task<LogicResult<SingleValueResult<string>>> Register(UserRegister register);
/// <summary>
/// Reassures that the user has a valid refresh token and generates a new access token
/// </summary>
/// <returns>The newly generated access token</returns>
Task<LogicResult<SingleValueResult<string>>> Authenticate(); Task<LogicResult<SingleValueResult<string>>> Authenticate();
Task<LogicResult> Logout(); Task<LogicResult> Logout();
/// <summary>
/// Deletes the user account that called the endpoint if the provided password is correct
/// </summary>
/// <param name="validation">The password od the user</param>
/// <returns></returns>
Task<LogicResult> Delete(UserPasswordValidation validation); Task<LogicResult> Delete(UserPasswordValidation validation);
} }

View File

@@ -1,5 +1,10 @@
namespace HopFrame.Api.Models; namespace HopFrame.Api.Models;
/// <summary>
/// Useful for endpoints that only return a single int or string
/// </summary>
/// <param name="value">The value of the result</param>
/// <typeparam name="TValue">The type of the result</typeparam>
public struct SingleValueResult<TValue>(TValue value) { public struct SingleValueResult<TValue>(TValue value) {
public TValue Value { get; set; } = value; public TValue Value { get; set; } = value;

View File

@@ -19,5 +19,5 @@ public interface IPermissionRepository {
Task RemovePermission(IPermissionOwner owner, string permission); Task RemovePermission(IPermissionOwner owner, string permission);
public Task<IList<string>> GetFullPermissions(IPermissionOwner owner); Task<IList<string>> GetFullPermissions(IPermissionOwner owner);
} }

View File

@@ -3,7 +3,7 @@ using HopFrame.Database.Models;
namespace HopFrame.Database.Repositories; namespace HopFrame.Database.Repositories;
public interface ITokenRepository { public interface ITokenRepository {
public Task<Token> GetToken(string content); Task<Token> GetToken(string content);
public Task<Token> CreateToken(int type, User owner); Task<Token> CreateToken(int type, User owner);
public Task DeleteUserTokens(User owner); Task DeleteUserTokens(User owner);
} }

View File

@@ -1,5 +1,3 @@
using System.Text.Json.Serialization;
namespace HopFrame.Web.Admin.Models; namespace HopFrame.Web.Admin.Models;
public sealed class AdminPageProperty { public sealed class AdminPageProperty {

View File

@@ -7,6 +7,9 @@ using Microsoft.AspNetCore.Http;
namespace HopFrame.Web; namespace HopFrame.Web;
/// <summary>
/// Assures that the user stays logged in even if the access token is expired
/// </summary>
public sealed class AuthMiddleware(IAuthService auth, IPermissionRepository perms) : IMiddleware { public sealed class AuthMiddleware(IAuthService auth, IPermissionRepository perms) : IMiddleware {
public async Task InvokeAsync(HttpContext context, RequestDelegate next) { public async Task InvokeAsync(HttpContext context, RequestDelegate next) {
var loggedIn = await auth.IsLoggedIn(); var loggedIn = await auth.IsLoggedIn();
@@ -14,7 +17,7 @@ public sealed class AuthMiddleware(IAuthService auth, IPermissionRepository perm
if (!loggedIn) { if (!loggedIn) {
var token = await auth.RefreshLogin(); var token = await auth.RefreshLogin();
if (token is null) { if (token is null) {
await next.Invoke(context); next?.Invoke(context);
return; return;
} }

View File

@@ -8,7 +8,7 @@ using HopFrame.Web.Repositories;
namespace HopFrame.Web; namespace HopFrame.Web;
public class HopAdminContext : AdminPagesContext { internal class HopAdminContext : AdminPagesContext {
public AdminPage<User> Users { get; set; } public AdminPage<User> Users { get; set; }
public AdminPage<PermissionGroup> Groups { get; set; } public AdminPage<PermissionGroup> Groups { get; set; }

View File

@@ -5,11 +5,14 @@
@using BlazorStrap @using BlazorStrap
@using HopFrame.Web.Pages.Administration.Layout @using HopFrame.Web.Pages.Administration.Layout
@using BlazorStrap.V5 @using BlazorStrap.V5
@using HopFrame.Security
@using HopFrame.Web.Admin.Providers @using HopFrame.Web.Admin.Providers
@using HopFrame.Web.Components @using HopFrame.Web.Components
@using Microsoft.AspNetCore.Components.Web @using Microsoft.AspNetCore.Components.Web
@layout AdminLayout @layout AdminLayout
<AuthorizedView Permission="@AdminPermissions.IsAdmin" RedirectIfUnauthorized="/administration/login" />
<PageTitle>Admin Dashboard</PageTitle> <PageTitle>Admin Dashboard</PageTitle>
<BSContainer> <BSContainer>

View File

@@ -30,13 +30,12 @@
} }
</BSNav> </BSNav>
<span style="margin-left: auto; line-height: 100%; color: white"> <span style="margin-left: auto; line-height: 100%; color: white; margin-right: 10px">
logged in as @Context?.User.Username logged in as @Context?.User.Username
</span> </span>
<BSButton DataId="logout" Size="Size.ExtraSmall" OnClick="Logout" Color="BSColor.Dark"> <BSButton DataId="logout" Size="Size.ExtraSmall" OnClick="Logout" Color="BSColor.Dark" style="display: grid; align-items: center">
<HopIconDisplay Type="HopIconDisplay.HopIcon.Logout"/> <HopIconDisplay Type="HopIconDisplay.HopIcon.Logout"/>
</BSButton> </BSButton>
<BSTooltip Placement="Placement.Bottom" Target="logout" ContentAlwaysRendered="false">logout</BSTooltip>
</Content> </Content>
</BSCollapse> </BSCollapse>
</BSContainer> </BSContainer>