@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
using HopFrame.Api.Controller;
|
|
||||||
using HopFrame.Database;
|
|
||||||
using HopFrame.Security.Authentication;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
|
|
||||||
namespace HopFrame.Api.Extensions;
|
|
||||||
|
|
||||||
public static class ServiceCollectionExtensions {
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds all HopFrame endpoints and the HopFrame security layer to the WebApplication
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="services">The service provider to add the services to</param>
|
|
||||||
/// <typeparam name="TDbContext">The data source for all HopFrame entities</typeparam>
|
|
||||||
public static void AddHopFrame<TDbContext>(this IServiceCollection services) where TDbContext : HopDbContextBase {
|
|
||||||
services.AddMvcCore().UseSpecificControllers(typeof(SecurityController<TDbContext>));
|
|
||||||
services.AddHopFrameAuthentication<TDbContext>();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
# HopFrame API module
|
|
||||||
This module contains some useful endpoints for user login / register management.
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
# HopFrame Security module
|
|
||||||
this module contains all handlers for the login and register validation. It also checks the user permissions.
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
# HopFrame Web module
|
|
||||||
This module contains useful helpers for Blazor Apps and an Admin Dashboard.
|
|
||||||
16
HopFrame.sln
16
HopFrame.sln
@@ -1,18 +1,16 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HopFrame.Database", "HopFrame.Database\HopFrame.Database.csproj", "{003120AE-F38B-4632-8497-BE4505189627}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HopFrame.Database", "src\HopFrame.Database\HopFrame.Database.csproj", "{003120AE-F38B-4632-8497-BE4505189627}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Testing", "Testing", "{58703056-8DAD-4221-BBE3-42425D2F4929}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestApiTest", "test\RestApiTest\RestApiTest.csproj", "{921159CE-AF75-44C3-A3F9-6B9B1A4E85CF}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RestApiTest", "RestApiTest\RestApiTest.csproj", "{921159CE-AF75-44C3-A3F9-6B9B1A4E85CF}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HopFrame.Security", "src\HopFrame.Security\HopFrame.Security.csproj", "{7F82E1C6-4A42-4337-9E03-2EE6429D004F}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HopFrame.Security", "HopFrame.Security\HopFrame.Security.csproj", "{7F82E1C6-4A42-4337-9E03-2EE6429D004F}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HopFrame.Api", "src\HopFrame.Api\HopFrame.Api.csproj", "{1E821490-AEDC-4F55-B758-52F4FADAB53A}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HopFrame.Api", "HopFrame.Api\HopFrame.Api.csproj", "{1E821490-AEDC-4F55-B758-52F4FADAB53A}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HopFrame.Web", "src\HopFrame.Web\HopFrame.Web.csproj", "{3BE585BC-13A5-4BE4-A806-E9EC2D825956}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HopFrame.Web", "HopFrame.Web\HopFrame.Web.csproj", "{3BE585BC-13A5-4BE4-A806-E9EC2D825956}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrontendTest", "test\FrontendTest\FrontendTest.csproj", "{8F983A37-63CF-48D5-988D-58B78EF8AECD}"
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrontendTest", "FrontendTest\FrontendTest.csproj", "{8F983A37-63CF-48D5-988D-58B78EF8AECD}"
|
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@@ -46,7 +44,5 @@ Global
|
|||||||
{8F983A37-63CF-48D5-988D-58B78EF8AECD}.Release|Any CPU.Build.0 = Release|Any CPU
|
{8F983A37-63CF-48D5-988D-58B78EF8AECD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{921159CE-AF75-44C3-A3F9-6B9B1A4E85CF} = {58703056-8DAD-4221-BBE3-42425D2F4929}
|
|
||||||
{8F983A37-63CF-48D5-988D-58B78EF8AECD} = {58703056-8DAD-4221-BBE3-42425D2F4929}
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
71
README.md
71
README.md
@@ -6,3 +6,74 @@ A simple backend management api for ASP.NET Core Web APIs
|
|||||||
- [x] User authentication
|
- [x] User authentication
|
||||||
- [x] Permission management
|
- [x] Permission management
|
||||||
- [x] Frontend dashboards
|
- [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();
|
||||||
|
```
|
||||||
|
|||||||
@@ -13,12 +13,6 @@ namespace HopFrame.Security {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace HopFrame.Web {
|
|
||||||
class RegisterData {
|
|
||||||
+RepeatedPassword: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace HopFrame.Api {
|
namespace HopFrame.Api {
|
||||||
class SingleValueResult<TValue> {
|
class SingleValueResult<TValue> {
|
||||||
+Value: TValue
|
+Value: TValue
|
||||||
@@ -29,6 +23,4 @@ namespace HopFrame.Api {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UserRegister <|-- RegisterData
|
|
||||||
|
|
||||||
@enduml
|
@enduml
|
||||||
@@ -11,9 +11,6 @@ namespace HopFrame.Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class TokenEntry {
|
class TokenEntry {
|
||||||
{static} +RefreshTokenType: int = 0
|
|
||||||
{static} +AccessTokenType: int = 1
|
|
||||||
|
|
||||||
+Type: int
|
+Type: int
|
||||||
+Token: string
|
+Token: string
|
||||||
+UserId: string
|
+UserId: string
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 12 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
13
docs/README.md
Normal file
13
docs/README.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# 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.
|
||||||
@@ -6,16 +6,16 @@ This page shows all models that HopFrame uses.
|
|||||||
## Base Models
|
## Base Models
|
||||||
These are the models used by the various database services.
|
These are the models used by the various database services.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
## API Models
|
## API Models
|
||||||
These are the models used by the REST API and the Blazor API.
|
These are the models used by the REST API and the Blazor API.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
## Database Models
|
## Database Models
|
||||||
These are the models that correspond to the scheme in the Database
|
These are the models that correspond to the scheme in the Database
|
||||||
|
|
||||||

|

|
||||||
145
docs/services.md
Normal file
145
docs/services.md
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
# 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();
|
||||||
|
}
|
||||||
|
```
|
||||||
70
docs/usage.md
Normal file
70
docs/usage.md
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# 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();
|
||||||
|
```
|
||||||
38
src/HopFrame.Api/Controller/SecurityController.cs
Normal file
38
src/HopFrame.Api/Controller/SecurityController.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using HopFrame.Api.Logic;
|
||||||
|
using HopFrame.Api.Models;
|
||||||
|
using HopFrame.Security.Authorization;
|
||||||
|
using HopFrame.Security.Models;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace HopFrame.Api.Controller;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("authentication")]
|
||||||
|
public class SecurityController(IAuthLogic auth) : ControllerBase {
|
||||||
|
|
||||||
|
[HttpPut("login")]
|
||||||
|
public async Task<ActionResult<SingleValueResult<string>>> Login([FromBody] UserLogin login) {
|
||||||
|
return await auth.Login(login);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("register")]
|
||||||
|
public async Task<ActionResult<SingleValueResult<string>>> Register([FromBody] UserRegister register) {
|
||||||
|
return await auth.Register(register);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("authenticate")]
|
||||||
|
public async Task<ActionResult<SingleValueResult<string>>> Authenticate() {
|
||||||
|
return await auth.Authenticate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("logout"), Authorized]
|
||||||
|
public async Task<ActionResult> Logout() {
|
||||||
|
return await auth.Logout();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("delete"), Authorized]
|
||||||
|
public async Task<ActionResult> Delete([FromBody] UserPasswordValidation validation) {
|
||||||
|
return await auth.Delete(validation);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
36
src/HopFrame.Api/Extensions/ServiceCollectionExtensions.cs
Normal file
36
src/HopFrame.Api/Extensions/ServiceCollectionExtensions.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using HopFrame.Api.Controller;
|
||||||
|
using HopFrame.Api.Logic;
|
||||||
|
using HopFrame.Api.Logic.Implementation;
|
||||||
|
using HopFrame.Database;
|
||||||
|
using HopFrame.Security.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
|
||||||
|
namespace HopFrame.Api.Extensions;
|
||||||
|
|
||||||
|
public static class ServiceCollectionExtensions {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds all HopFrame endpoints and services to the application
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services">The service provider to add the services to</param>
|
||||||
|
/// <typeparam name="TDbContext">The data source for all HopFrame entities</typeparam>
|
||||||
|
public static void AddHopFrame<TDbContext>(this IServiceCollection services) where TDbContext : HopDbContextBase {
|
||||||
|
services.AddMvcCore().UseSpecificControllers(typeof(SecurityController));
|
||||||
|
AddHopFrameNoEndpoints<TDbContext>(services);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds all HopFrame services to the application
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services">The service provider to add the services to</param>
|
||||||
|
/// <typeparam name="TDbContext">The data source for all HopFrame entities</typeparam>
|
||||||
|
public static void AddHopFrameNoEndpoints<TDbContext>(this IServiceCollection services) where TDbContext : HopDbContextBase {
|
||||||
|
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||||
|
services.AddScoped<IAuthLogic, AuthLogic<TDbContext>>();
|
||||||
|
|
||||||
|
services.AddHopFrameAuthentication<TDbContext>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,8 +5,12 @@
|
|||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>disable</Nullable>
|
<Nullable>disable</Nullable>
|
||||||
|
|
||||||
|
<PackageId>HopFrame.Api</PackageId>
|
||||||
|
<Version>1.1.0</Version>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
<IsPackable>true</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
16
src/HopFrame.Api/Logic/IAuthLogic.cs
Normal file
16
src/HopFrame.Api/Logic/IAuthLogic.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using HopFrame.Api.Models;
|
||||||
|
using HopFrame.Security.Models;
|
||||||
|
|
||||||
|
namespace HopFrame.Api.Logic;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -1,30 +1,24 @@
|
|||||||
using HopFrame.Api.Logic;
|
|
||||||
using HopFrame.Api.Models;
|
using HopFrame.Api.Models;
|
||||||
using HopFrame.Database;
|
using HopFrame.Database;
|
||||||
using HopFrame.Database.Models.Entries;
|
using HopFrame.Database.Models.Entries;
|
||||||
using HopFrame.Security.Authentication;
|
using HopFrame.Security.Authentication;
|
||||||
using HopFrame.Security.Authorization;
|
|
||||||
using HopFrame.Security.Claims;
|
using HopFrame.Security.Claims;
|
||||||
using HopFrame.Security.Models;
|
using HopFrame.Security.Models;
|
||||||
using HopFrame.Security.Services;
|
using HopFrame.Security.Services;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace HopFrame.Api.Controller;
|
namespace HopFrame.Api.Logic.Implementation;
|
||||||
|
|
||||||
[ApiController]
|
public class AuthLogic<TDbContext>(TDbContext context, IUserService users, ITokenContext tokenContext, IHttpContextAccessor accessor) : IAuthLogic where TDbContext : HopDbContextBase {
|
||||||
[Route("authentication")]
|
|
||||||
public class SecurityController<TDbContext>(TDbContext context, IUserService users, ITokenContext tokenContext) : ControllerBase where TDbContext : HopDbContextBase {
|
public async Task<LogicResult<SingleValueResult<string>>> Login(UserLogin login) {
|
||||||
|
|
||||||
[HttpPut("login")]
|
|
||||||
public async Task<ActionResult<SingleValueResult<string>>> Login([FromBody] UserLogin login) {
|
|
||||||
var user = await users.GetUserByEmail(login.Email);
|
var user = await users.GetUserByEmail(login.Email);
|
||||||
|
|
||||||
if (user is null)
|
if (user is null)
|
||||||
return LogicResult<SingleValueResult<string>>.NotFound("The provided email address was not found");
|
return LogicResult<SingleValueResult<string>>.NotFound("The provided email address was not found");
|
||||||
|
|
||||||
if (await users.CheckUserPassword(user, login.Password))
|
if (!await users.CheckUserPassword(user, login.Password))
|
||||||
return LogicResult<SingleValueResult<string>>.Forbidden("The provided password is not correct");
|
return LogicResult<SingleValueResult<string>>.Forbidden("The provided password is not correct");
|
||||||
|
|
||||||
var refreshToken = new TokenEntry {
|
var refreshToken = new TokenEntry {
|
||||||
@@ -40,11 +34,16 @@ public class SecurityController<TDbContext>(TDbContext context, IUserService use
|
|||||||
UserId = user.Id.ToString()
|
UserId = user.Id.ToString()
|
||||||
};
|
};
|
||||||
|
|
||||||
HttpContext.Response.Cookies.Append(ITokenContext.RefreshTokenType, refreshToken.Token, new CookieOptions {
|
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.RefreshTokenType, refreshToken.Token, new CookieOptions {
|
||||||
MaxAge = HopFrameAuthentication<TDbContext>.RefreshTokenTime,
|
MaxAge = HopFrameAuthentication<TDbContext>.RefreshTokenTime,
|
||||||
HttpOnly = true,
|
HttpOnly = true,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
|
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Token, new CookieOptions {
|
||||||
|
MaxAge = HopFrameAuthentication<TDbContext>.AccessTokenTime,
|
||||||
|
HttpOnly = true,
|
||||||
|
Secure = true
|
||||||
|
});
|
||||||
|
|
||||||
await context.Tokens.AddRangeAsync(refreshToken, accessToken);
|
await context.Tokens.AddRangeAsync(refreshToken, accessToken);
|
||||||
await context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
@@ -52,8 +51,7 @@ public class SecurityController<TDbContext>(TDbContext context, IUserService use
|
|||||||
return LogicResult<SingleValueResult<string>>.Ok(accessToken.Token);
|
return LogicResult<SingleValueResult<string>>.Ok(accessToken.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("register")]
|
public async Task<LogicResult<SingleValueResult<string>>> Register(UserRegister register) {
|
||||||
public async Task<ActionResult<SingleValueResult<string>>> Register([FromBody] UserRegister register) {
|
|
||||||
if (register.Password.Length < 8)
|
if (register.Password.Length < 8)
|
||||||
return LogicResult<SingleValueResult<string>>.Conflict("Password needs to be at least 8 characters long");
|
return LogicResult<SingleValueResult<string>>.Conflict("Password needs to be at least 8 characters long");
|
||||||
|
|
||||||
@@ -79,12 +77,12 @@ public class SecurityController<TDbContext>(TDbContext context, IUserService use
|
|||||||
await context.Tokens.AddRangeAsync(refreshToken, accessToken);
|
await context.Tokens.AddRangeAsync(refreshToken, accessToken);
|
||||||
await context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
HttpContext.Response.Cookies.Append(ITokenContext.RefreshTokenType, refreshToken.Token, new CookieOptions {
|
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.RefreshTokenType, refreshToken.Token, new CookieOptions {
|
||||||
MaxAge = HopFrameAuthentication<TDbContext>.RefreshTokenTime,
|
MaxAge = HopFrameAuthentication<TDbContext>.RefreshTokenTime,
|
||||||
HttpOnly = true,
|
HttpOnly = true,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
HttpContext.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Token, new CookieOptions {
|
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Token, new CookieOptions {
|
||||||
MaxAge = HopFrameAuthentication<TDbContext>.AccessTokenTime,
|
MaxAge = HopFrameAuthentication<TDbContext>.AccessTokenTime,
|
||||||
HttpOnly = false,
|
HttpOnly = false,
|
||||||
Secure = true
|
Secure = true
|
||||||
@@ -93,9 +91,8 @@ public class SecurityController<TDbContext>(TDbContext context, IUserService use
|
|||||||
return LogicResult<SingleValueResult<string>>.Ok(accessToken.Token);
|
return LogicResult<SingleValueResult<string>>.Ok(accessToken.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("authenticate")]
|
public async Task<LogicResult<SingleValueResult<string>>> Authenticate() {
|
||||||
public async Task<ActionResult<SingleValueResult<string>>> Authenticate() {
|
var refreshToken = accessor.HttpContext?.Request.Cookies[ITokenContext.RefreshTokenType];
|
||||||
var refreshToken = HttpContext.Request.Cookies[ITokenContext.RefreshTokenType];
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(refreshToken))
|
if (string.IsNullOrEmpty(refreshToken))
|
||||||
return LogicResult<SingleValueResult<string>>.Conflict("Refresh token not provided");
|
return LogicResult<SingleValueResult<string>>.Conflict("Refresh token not provided");
|
||||||
@@ -118,7 +115,7 @@ public class SecurityController<TDbContext>(TDbContext context, IUserService use
|
|||||||
await context.Tokens.AddAsync(accessToken);
|
await context.Tokens.AddAsync(accessToken);
|
||||||
await context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
HttpContext.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Token, new CookieOptions {
|
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Token, new CookieOptions {
|
||||||
MaxAge = HopFrameAuthentication<TDbContext>.AccessTokenTime,
|
MaxAge = HopFrameAuthentication<TDbContext>.AccessTokenTime,
|
||||||
HttpOnly = false,
|
HttpOnly = false,
|
||||||
Secure = true
|
Secure = true
|
||||||
@@ -127,10 +124,9 @@ public class SecurityController<TDbContext>(TDbContext context, IUserService use
|
|||||||
return LogicResult<SingleValueResult<string>>.Ok(accessToken.Token);
|
return LogicResult<SingleValueResult<string>>.Ok(accessToken.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("logout"), Authorized]
|
public async Task<LogicResult> Logout() {
|
||||||
public async Task<ActionResult> Logout() {
|
var accessToken = accessor.HttpContext?.User.GetAccessTokenId();
|
||||||
var accessToken = HttpContext.User.GetAccessTokenId();
|
var refreshToken = accessor.HttpContext?.Request.Cookies[ITokenContext.RefreshTokenType];
|
||||||
var refreshToken = HttpContext.Request.Cookies[ITokenContext.RefreshTokenType];
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(accessToken) || string.IsNullOrEmpty(refreshToken))
|
if (string.IsNullOrEmpty(accessToken) || string.IsNullOrEmpty(refreshToken))
|
||||||
return LogicResult.Conflict("access or refresh token not provided");
|
return LogicResult.Conflict("access or refresh token not provided");
|
||||||
@@ -147,22 +143,22 @@ public class SecurityController<TDbContext>(TDbContext context, IUserService use
|
|||||||
context.Tokens.Remove(tokenEntries[1]);
|
context.Tokens.Remove(tokenEntries[1]);
|
||||||
await context.SaveChangesAsync();
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
HttpContext.Response.Cookies.Delete(ITokenContext.RefreshTokenType);
|
accessor.HttpContext?.Response.Cookies.Delete(ITokenContext.RefreshTokenType);
|
||||||
HttpContext.Response.Cookies.Delete(ITokenContext.AccessTokenType);
|
accessor.HttpContext?.Response.Cookies.Delete(ITokenContext.AccessTokenType);
|
||||||
|
|
||||||
return LogicResult.Ok();
|
return LogicResult.Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpDelete("delete"), Authorized]
|
public async Task<LogicResult> Delete(UserPasswordValidation validation) {
|
||||||
public async Task<ActionResult> Delete([FromBody] UserPasswordValidation validation) {
|
|
||||||
var user = tokenContext.User;
|
var user = tokenContext.User;
|
||||||
|
|
||||||
if (await users.CheckUserPassword(user, validation.Password))
|
if (!await users.CheckUserPassword(user, validation.Password))
|
||||||
return LogicResult.Forbidden("The provided password is not correct");
|
return LogicResult.Forbidden("The provided password is not correct");
|
||||||
|
|
||||||
await users.DeleteUser(user);
|
await users.DeleteUser(user);
|
||||||
|
|
||||||
HttpContext.Response.Cookies.Delete(ITokenContext.RefreshTokenType);
|
accessor.HttpContext?.Response.Cookies.Delete(ITokenContext.RefreshTokenType);
|
||||||
|
accessor.HttpContext?.Response.Cookies.Delete(ITokenContext.AccessTokenType);
|
||||||
|
|
||||||
return LogicResult.Ok();
|
return LogicResult.Ok();
|
||||||
}
|
}
|
||||||
100
src/HopFrame.Api/README.md
Normal file
100
src/HopFrame.Api/README.md
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
# 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>();
|
||||||
|
```
|
||||||
|
|
||||||
|
# Endpoints
|
||||||
|
By default, the module provides a controller for handling authentication based requests by the user.
|
||||||
|
You can explore the contoller by the build in swagger site from ASP .NET.
|
||||||
|
|
||||||
|
## Disable the Endpoints
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
builder.Services.AddDbContext<DatabaseContext>();
|
||||||
|
//builder.Services.AddHopFrame<DatabaseContext>();
|
||||||
|
services.AddHopFrameNoEndpoints<TDbContext>();
|
||||||
|
```
|
||||||
|
|
||||||
|
# Services added in this module
|
||||||
|
You can use these services by specifying them as a dependency. All of them are scoped dependencies.
|
||||||
|
|
||||||
|
## 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);
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -5,8 +5,12 @@
|
|||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>disable</Nullable>
|
<Nullable>disable</Nullable>
|
||||||
|
|
||||||
|
<PackageId>HopFrame.Database</PackageId>
|
||||||
|
<Version>1.1.0</Version>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
<IsPackable>true</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace HopFrame.Web;
|
namespace HopFrame.Security;
|
||||||
|
|
||||||
public static class AdminPermissions {
|
public static class AdminPermissions {
|
||||||
public const string IsAdmin = "hopframe.admin";
|
public const string IsAdmin = "hopframe.admin";
|
||||||
@@ -29,6 +29,8 @@ public class HopFrameAuthentication<TDbContext>(
|
|||||||
|
|
||||||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync() {
|
protected override async Task<AuthenticateResult> HandleAuthenticateAsync() {
|
||||||
var accessToken = Request.Cookies[ITokenContext.AccessTokenType];
|
var accessToken = Request.Cookies[ITokenContext.AccessTokenType];
|
||||||
|
if (string.IsNullOrEmpty(accessToken)) accessToken = Request.Headers[SchemeName];
|
||||||
|
if (string.IsNullOrEmpty(accessToken)) accessToken = Request.Headers["Token"];
|
||||||
if (string.IsNullOrEmpty(accessToken)) return AuthenticateResult.Fail("No Access Token provided");
|
if (string.IsNullOrEmpty(accessToken)) return AuthenticateResult.Fail("No Access Token provided");
|
||||||
|
|
||||||
var tokenEntry = await context.Tokens.SingleOrDefaultAsync(token => token.Token == accessToken);
|
var tokenEntry = await context.Tokens.SingleOrDefaultAsync(token => token.Token == accessToken);
|
||||||
@@ -6,12 +6,16 @@ public static class PermissionValidator {
|
|||||||
var permLow = permission.ToLower();
|
var permLow = permission.ToLower();
|
||||||
var permsLow = permissions.Select(perm => perm.ToLower()).ToArray();
|
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) {
|
foreach (var perm in permsLow) {
|
||||||
if (!perm.EndsWith(".*")) continue;
|
if (!perm.EndsWith(".*")) continue;
|
||||||
|
|
||||||
var permissionGroup = perm.Replace(".*", "");
|
var permissionGroup = perm.Substring(0, perm.Length - 1);
|
||||||
if (permLow.StartsWith(permissionGroup)) return true;
|
if (permLow.StartsWith(permissionGroup)) return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6,8 +6,12 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>disable</Nullable>
|
<Nullable>disable</Nullable>
|
||||||
<RootNamespace>HopFrame.Security</RootNamespace>
|
<RootNamespace>HopFrame.Security</RootNamespace>
|
||||||
|
|
||||||
|
<PackageId>HopFrame.Security</PackageId>
|
||||||
|
<Version>1.1.0</Version>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
<IsPackable>true</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
74
src/HopFrame.Security/README.md
Normal file
74
src/HopFrame.Security/README.md
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
# HopFrame Security module
|
||||||
|
this module contains all handlers for the login and register validation. It also checks the user permissions.
|
||||||
|
|
||||||
|
# Services added in this module
|
||||||
|
You can use these services by specifying them as a dependency. All of them are scoped dependencies.
|
||||||
|
|
||||||
|
## 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);
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -2,6 +2,13 @@ using HopFrame.Database.Models;
|
|||||||
|
|
||||||
namespace HopFrame.Security.Services;
|
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 {
|
public interface IPermissionService {
|
||||||
|
|
||||||
Task<bool> HasPermission(string permission, Guid user);
|
Task<bool> HasPermission(string permission, Guid user);
|
||||||
16
src/HopFrame.Web/AdminPermissions.cs
Normal file
16
src/HopFrame.Web/AdminPermissions.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
namespace HopFrame.Web;
|
||||||
|
|
||||||
|
[Obsolete("Use HopFrame.Security.AdminPermissions instead")]
|
||||||
|
public static class AdminPermissions {
|
||||||
|
public const string IsAdmin = Security.AdminPermissions.IsAdmin;
|
||||||
|
|
||||||
|
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 = Security.AdminPermissions.ViewGroups;
|
||||||
|
public const string EditGroup = Security.AdminPermissions.EditGroup;
|
||||||
|
public const string DeleteGroup = Security.AdminPermissions.DeleteGroup;
|
||||||
|
public const string AddGroup = Security.AdminPermissions.AddGroup;
|
||||||
|
}
|
||||||
@@ -167,7 +167,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_isEdit) {
|
if (_isEdit) {
|
||||||
if (!(await Permissions.HasPermission(AdminPermissions.EditGroup, Context.User.Id))) {
|
if (!(await Permissions.HasPermission(Security.AdminPermissions.EditGroup, Context.User.Id))) {
|
||||||
await NoEditPermissions();
|
await NoEditPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -202,7 +202,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_isEdit) {
|
if (_isEdit) {
|
||||||
if (!(await Permissions.HasPermission(AdminPermissions.EditGroup, Context.User.Id))) {
|
if (!(await Permissions.HasPermission(Security.AdminPermissions.EditGroup, Context.User.Id))) {
|
||||||
await NoEditPermissions();
|
await NoEditPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -219,7 +219,7 @@
|
|||||||
|
|
||||||
private async Task AddGroup() {
|
private async Task AddGroup() {
|
||||||
if (_isEdit) {
|
if (_isEdit) {
|
||||||
if (!(await Permissions.HasPermission(AdminPermissions.EditGroup, Context.User.Id))) {
|
if (!(await Permissions.HasPermission(Security.AdminPermissions.EditGroup, Context.User.Id))) {
|
||||||
await NoEditPermissions();
|
await NoEditPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -239,7 +239,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(await Permissions.HasPermission(AdminPermissions.AddGroup, Context.User.Id))) {
|
if (!(await Permissions.HasPermission(Security.AdminPermissions.AddGroup, Context.User.Id))) {
|
||||||
await NoAddPermissions();
|
await NoAddPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -69,7 +69,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task AddUser() {
|
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();
|
await NoAddPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
private string _permissionToAdd;
|
private string _permissionToAdd;
|
||||||
|
|
||||||
public async Task ShowAsync(User user) {
|
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();
|
await NoEditPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -130,7 +130,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task AddGroup() {
|
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();
|
await NoEditPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -158,7 +158,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task RemoveGroup(PermissionGroup group) {
|
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();
|
await NoEditPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -186,7 +186,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task AddPermission() {
|
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();
|
await NoEditPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -213,7 +213,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task RemovePermission(Permission perm) {
|
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();
|
await NoEditPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -241,7 +241,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async void EditUser() {
|
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();
|
await NoEditPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,12 @@
|
|||||||
<Nullable>disable</Nullable>
|
<Nullable>disable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
||||||
|
|
||||||
|
<PackageId>HopFrame.Web</PackageId>
|
||||||
|
<Version>1.1.0</Version>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
<IsPackable>true</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
@using HopFrame.Web.Pages.Administration.Layout
|
@using HopFrame.Web.Pages.Administration.Layout
|
||||||
|
|
||||||
<PageTitle>Groups</PageTitle>
|
<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"/>
|
<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">
|
<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>
|
<BSButton Color="BSColor.Success" IsOutlined="true" type="submit">Search</BSButton>
|
||||||
</form>
|
</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>
|
<BSButton IsSubmit="false" Color="BSColor.Success" Target="add-user" OnClick="() => _groupAddModal.ShowAsync()">Add Group</BSButton>
|
||||||
</AuthorizedView>
|
</AuthorizedView>
|
||||||
</div>
|
</div>
|
||||||
@@ -112,8 +112,8 @@
|
|||||||
protected override async Task OnInitializedAsync() {
|
protected override async Task OnInitializedAsync() {
|
||||||
_groups = await Permissions.GetPermissionGroups();
|
_groups = await Permissions.GetPermissionGroups();
|
||||||
|
|
||||||
_hasEditPrivileges = await Permissions.HasPermission(AdminPermissions.EditGroup, Auth.User.Id);
|
_hasEditPrivileges = await Permissions.HasPermission(Security.AdminPermissions.EditGroup, Auth.User.Id);
|
||||||
_hasDeletePrivileges = await Permissions.HasPermission(AdminPermissions.DeleteGroup, Auth.User.Id);
|
_hasDeletePrivileges = await Permissions.HasPermission(Security.AdminPermissions.DeleteGroup, Auth.User.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Reload() {
|
private async Task Reload() {
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
@using BlazorStrap.V5
|
@using BlazorStrap.V5
|
||||||
@inherits LayoutComponentBase
|
@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">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
||||||
|
|
||||||
@@ -53,13 +53,13 @@
|
|||||||
Name = "Users",
|
Name = "Users",
|
||||||
Url = "administration/users",
|
Url = "administration/users",
|
||||||
Description = "On this page you can manage all user accounts.",
|
Description = "On this page you can manage all user accounts.",
|
||||||
Permission = AdminPermissions.ViewUsers
|
Permission = Security.AdminPermissions.ViewUsers
|
||||||
},
|
},
|
||||||
new () {
|
new () {
|
||||||
Name = "Groups",
|
Name = "Groups",
|
||||||
Url = "administration/groups",
|
Url = "administration/groups",
|
||||||
Description = "On this page you can view, create, edit and delete permission groups.",
|
Description = "On this page you can view, create, edit and delete permission groups.",
|
||||||
Permission = AdminPermissions.ViewGroups
|
Permission = Security.AdminPermissions.ViewGroups
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
@using HopFrame.Web.Components.Administration
|
@using HopFrame.Web.Components.Administration
|
||||||
|
|
||||||
<PageTitle>Users</PageTitle>
|
<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"/>
|
<UserAddModal @ref="_userAddModal" ReloadPage="Reload"/>
|
||||||
<UserEditModal @ref="_userEditModal" 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">
|
<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>
|
<BSButton Color="BSColor.Success" IsOutlined="true" type="submit">Search</BSButton>
|
||||||
</form>
|
</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>
|
<BSButton IsSubmit="false" Color="BSColor.Success" Target="add-user" OnClick="() => _userAddModal.ShowAsync()">Add User</BSButton>
|
||||||
</AuthorizedView>
|
</AuthorizedView>
|
||||||
</div>
|
</div>
|
||||||
@@ -123,8 +123,8 @@
|
|||||||
_userGroups.Add(user.Id, groups.LastOrDefault());
|
_userGroups.Add(user.Id, groups.LastOrDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
_hasEditPrivileges = await PermissionsService.HasPermission(AdminPermissions.EditUser, Auth.User.Id);
|
_hasEditPrivileges = await PermissionsService.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id);
|
||||||
_hasDeletePrivileges = await PermissionsService.HasPermission(AdminPermissions.DeleteUser, Auth.User.Id);
|
_hasDeletePrivileges = await PermissionsService.HasPermission(Security.AdminPermissions.DeleteUser, Auth.User.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Reload() {
|
private async Task Reload() {
|
||||||
60
src/HopFrame.Web/README.md
Normal file
60
src/HopFrame.Web/README.md
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
# 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();
|
||||||
|
```
|
||||||
|
|
||||||
|
# Services added in this module
|
||||||
|
You can use these services by specifying them as a dependency. All of them are scoped dependencies.
|
||||||
|
|
||||||
|
## 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();
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -10,7 +10,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
namespace HopFrame.Web;
|
namespace HopFrame.Web;
|
||||||
|
|
||||||
public static class ServiceCollectionExtensions {
|
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.AddHttpClient();
|
||||||
services.AddScoped<IAuthService, AuthService<TDbContext>>();
|
services.AddScoped<IAuthService, AuthService<TDbContext>>();
|
||||||
services.AddTransient<AuthMiddleware>();
|
services.AddTransient<AuthMiddleware>();
|
||||||
@@ -18,8 +18,6 @@ public static class ServiceCollectionExtensions {
|
|||||||
// Component library's
|
// Component library's
|
||||||
services.AddSweetAlert2();
|
services.AddSweetAlert2();
|
||||||
services.AddBlazorStrap();
|
services.AddBlazorStrap();
|
||||||
|
|
||||||
//TODO: Use https://blazorstrap.io/V5/V5
|
|
||||||
|
|
||||||
services.AddHopFrameAuthentication<TDbContext>();
|
services.AddHopFrameAuthentication<TDbContext>();
|
||||||
|
|
||||||
@@ -7,6 +7,6 @@ public class DatabaseContext : HopDbContextBase {
|
|||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
|
||||||
base.OnConfiguring(optionsBuilder);
|
base.OnConfiguring(optionsBuilder);
|
||||||
|
|
||||||
optionsBuilder.UseSqlite("Data Source=C:\\Users\\Remote\\Documents\\Projekte\\HopFrame\\RestApiTest\\bin\\Debug\\net8.0\\test.db;Mode=ReadWrite;");
|
optionsBuilder.UseSqlite("Data Source=C:\\Users\\Remote\\Documents\\Projekte\\HopFrame\\test\\RestApiTest\\bin\\Debug\\net8.0\\test.db;Mode=ReadWrite;");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,10 +6,6 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\HopFrame.Web\HopFrame.Web.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.7" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.7" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -19,4 +15,8 @@
|
|||||||
<_ContentIncludedByDefault Remove="wwwroot\bootstrap\bootstrap.min.css.map" />
|
<_ContentIncludedByDefault Remove="wwwroot\bootstrap\bootstrap.min.css.map" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\HopFrame.Web\HopFrame.Web.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -5,7 +5,7 @@ using HopFrame.Web;
|
|||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
builder.Services.AddDbContext<DatabaseContext>();
|
builder.Services.AddDbContext<DatabaseContext>();
|
||||||
builder.Services.AddHopFrameServices<DatabaseContext>();
|
builder.Services.AddHopFrame<DatabaseContext>();
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
builder.Services.AddRazorComponents()
|
builder.Services.AddRazorComponents()
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user