diff --git a/FrontendTest/appsettings.Development.json b/FrontendTest/appsettings.Development.json
deleted file mode 100644
index 0c208ae..0000000
--- a/FrontendTest/appsettings.Development.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "Logging": {
- "LogLevel": {
- "Default": "Information",
- "Microsoft.AspNetCore": "Warning"
- }
- }
-}
diff --git a/HopFrame.Api/Extensions/ServiceCollectionExtensions.cs b/HopFrame.Api/Extensions/ServiceCollectionExtensions.cs
deleted file mode 100644
index 568580e..0000000
--- a/HopFrame.Api/Extensions/ServiceCollectionExtensions.cs
+++ /dev/null
@@ -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 {
-
- ///
- /// Adds all HopFrame endpoints and the HopFrame security layer to the WebApplication
- ///
- /// The service provider to add the services to
- /// The data source for all HopFrame entities
- public static void AddHopFrame(this IServiceCollection services) where TDbContext : HopDbContextBase {
- services.AddMvcCore().UseSpecificControllers(typeof(SecurityController));
- services.AddHopFrameAuthentication();
- }
-
-}
diff --git a/HopFrame.Api/README.md b/HopFrame.Api/README.md
deleted file mode 100644
index 2a42044..0000000
--- a/HopFrame.Api/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# HopFrame API module
-This module contains some useful endpoints for user login / register management.
diff --git a/HopFrame.Security/README.md b/HopFrame.Security/README.md
deleted file mode 100644
index fec1066..0000000
--- a/HopFrame.Security/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# HopFrame Security module
-this module contains all handlers for the login and register validation. It also checks the user permissions.
diff --git a/HopFrame.Web/README.md b/HopFrame.Web/README.md
deleted file mode 100644
index ec39f17..0000000
--- a/HopFrame.Web/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# HopFrame Web module
-This module contains useful helpers for Blazor Apps and an Admin Dashboard.
diff --git a/HopFrame.sln b/HopFrame.sln
index 453dbcf..f9217e8 100644
--- a/HopFrame.sln
+++ b/HopFrame.sln
@@ -1,18 +1,16 @@
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
-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
-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
-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
-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
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HopFrame.Web", "HopFrame.Web\HopFrame.Web.csproj", "{3BE585BC-13A5-4BE4-A806-E9EC2D825956}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrontendTest", "FrontendTest\FrontendTest.csproj", "{8F983A37-63CF-48D5-988D-58B78EF8AECD}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FrontendTest", "test\FrontendTest\FrontendTest.csproj", "{8F983A37-63CF-48D5-988D-58B78EF8AECD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -46,7 +44,5 @@ Global
{8F983A37-63CF-48D5-988D-58B78EF8AECD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
- {921159CE-AF75-44C3-A3F9-6B9B1A4E85CF} = {58703056-8DAD-4221-BBE3-42425D2F4929}
- {8F983A37-63CF-48D5-988D-58B78EF8AECD} = {58703056-8DAD-4221-BBE3-42425D2F4929}
EndGlobalSection
EndGlobal
diff --git a/README.md b/README.md
index 84f0ab2..b7ed1b7 100644
--- a/README.md
+++ b/README.md
@@ -6,3 +6,74 @@ A simple backend management api for ASP.NET Core Web APIs
- [x] User authentication
- [x] Permission management
- [x] Frontend dashboards
+
+# Usage
+There are two different versions of HopFrame, either the Web API version or the full Blazor web version.
+
+## Ho to use the Web API version
+
+1. Add the HopFrame.Api library to your project:
+
+ ```
+ dotnet add package HopFrame.Api
+ ```
+
+2. Create a DbContext that inherits the ``HopDbContext`` and add a data source
+
+ ```csharp
+ public class DatabaseContext : HopDbContextBase {
+ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
+ base.OnConfiguring(optionsBuilder);
+
+ optionsBuilder.UseSqlite("...");
+ }
+ }
+ ```
+
+3. Add the DbContext and HopFrame to your services
+
+ ```csharp
+ builder.Services.AddDbContext();
+ builder.Services.AddHopFrame();
+ ```
+
+## How to use the Blazor API
+
+1. Add the HopFrame.Web library to your project
+
+ ```
+ dotnet add package HopFrame.Web
+ ```
+
+2. Create a DbContext that inherits the ``HopDbContext`` and add a data source
+
+ ```csharp
+ public class DatabaseContext : HopDbContextBase {
+ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
+ base.OnConfiguring(optionsBuilder);
+
+ optionsBuilder.UseSqlite("...");
+ }
+ }
+ ```
+
+3. Add the DbContext and HopFrame to your services
+
+ ```csharp
+ builder.Services.AddDbContext();
+ builder.Services.AddHopFrame();
+ ```
+
+4. Add the authentication middleware to your app
+
+ ```csharp
+ app.UseMiddleware();
+ ```
+
+5. Add the HopFrame pages to your Razor components
+
+ ```csharp
+ app.MapRazorComponents()
+ .AddHopFrameAdminPages()
+ .AddInteractiveServerRenderMode();
+ ```
diff --git a/docs/Diagrams/Models/ApiModels.puml b/docs/Diagrams/Models/ApiModels.puml
index 2019855..bb5d25a 100644
--- a/docs/Diagrams/Models/ApiModels.puml
+++ b/docs/Diagrams/Models/ApiModels.puml
@@ -13,12 +13,6 @@ namespace HopFrame.Security {
}
}
-namespace HopFrame.Web {
- class RegisterData {
- +RepeatedPassword: string
- }
-}
-
namespace HopFrame.Api {
class SingleValueResult {
+Value: TValue
@@ -29,6 +23,4 @@ namespace HopFrame.Api {
}
}
-UserRegister <|-- RegisterData
-
@enduml
\ No newline at end of file
diff --git a/docs/Diagrams/Models/DatabaseModels.puml b/docs/Diagrams/Models/DatabaseModels.puml
index fc3a7b9..2e47b5b 100644
--- a/docs/Diagrams/Models/DatabaseModels.puml
+++ b/docs/Diagrams/Models/DatabaseModels.puml
@@ -11,9 +11,6 @@ namespace HopFrame.Database {
}
class TokenEntry {
- {static} +RefreshTokenType: int = 0
- {static} +AccessTokenType: int = 1
-
+Type: int
+Token: string
+UserId: string
diff --git a/docs/Diagrams/Models/img/ApiModels.svg b/docs/Diagrams/Models/img/ApiModels.svg
index 82ff3c0..76a19b4 100644
--- a/docs/Diagrams/Models/img/ApiModels.svg
+++ b/docs/Diagrams/Models/img/ApiModels.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/Diagrams/Models/img/DatabaseModels.svg b/docs/Diagrams/Models/img/DatabaseModels.svg
index 06aa882..fedbd56 100644
--- a/docs/Diagrams/Models/img/DatabaseModels.svg
+++ b/docs/Diagrams/Models/img/DatabaseModels.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..eb4fa47
--- /dev/null
+++ b/docs/README.md
@@ -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.
diff --git a/docs/Models/README.md b/docs/models.md
similarity index 70%
rename from docs/Models/README.md
rename to docs/models.md
index 03273b6..6af77a4 100644
--- a/docs/Models/README.md
+++ b/docs/models.md
@@ -6,16 +6,16 @@ This page shows all models that HopFrame uses.
## Base Models
These are the models used by the various database services.
-
+
## API Models
These are the models used by the REST API and the Blazor API.
-
+
## Database Models
These are the models that correspond to the scheme in the Database
-
+
diff --git a/docs/services.md b/docs/services.md
new file mode 100644
index 0000000..81a7312
--- /dev/null
+++ b/docs/services.md
@@ -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> GetUsers();
+
+ Task GetUser(Guid userId);
+
+ Task GetUserByEmail(string email);
+
+ Task GetUserByUsername(string username);
+
+ Task AddUser(UserRegister user);
+
+ Task UpdateUser(User user);
+
+ Task DeleteUser(User user);
+
+ Task 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 HasPermission(string permission, Guid user);
+
+ Task> GetPermissionGroups();
+
+ Task GetPermissionGroup(string name);
+
+ Task EditPermissionGroup(PermissionGroup group);
+
+ Task> GetUserPermissionGroups(User user);
+
+ Task RemoveGroupFromUser(User user, PermissionGroup group);
+
+ Task CreatePermissionGroup(string name, bool isDefault = false, string description = null);
+
+ Task DeletePermissionGroup(PermissionGroup group);
+
+ Task GetPermission(string name, IPermissionOwner owner);
+
+ Task AddPermission(IPermissionOwner owner, string permission);
+
+ Task RemovePermission(Permission permission);
+
+ Task 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(ILogicResult result);
+
+ public static implicit operator ActionResult(LogicResult v);
+}
+
+public class LogicResult : ILogicResult {
+ public static LogicResult Ok();
+
+ public static LogicResult Ok(T result);
+
+ ...
+}
+```
+
+### IAuthLogic
+This service handles all logic needed to provide the authentication endpoints by using the LogicResults.
+
+```csharp
+public interface IAuthLogic {
+ Task>> Login(UserLogin login);
+
+ Task>> Register(UserRegister register);
+
+ Task>> Authenticate();
+
+ Task Logout();
+
+ Task 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 Login(UserLogin login);
+ Task Logout();
+
+ Task RefreshLogin();
+ Task IsLoggedIn();
+}
+```
diff --git a/docs/usage.md b/docs/usage.md
new file mode 100644
index 0000000..2023531
--- /dev/null
+++ b/docs/usage.md
@@ -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();
+ builder.Services.AddHopFrame();
+ ```
+
+## How to use the Blazor API
+
+1. Add the HopFrame.Web library to your project
+
+ ```
+ dotnet add package HopFrame.Web
+ ```
+
+2. Create a DbContext that inherits the ``HopDbContext`` and add a data source
+
+ ```csharp
+ public class DatabaseContext : HopDbContextBase {
+ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
+ base.OnConfiguring(optionsBuilder);
+
+ optionsBuilder.UseSqlite("...");
+ }
+ }
+ ```
+
+3. Add the DbContext and HopFrame to your services
+
+ ```csharp
+ builder.Services.AddDbContext();
+ builder.Services.AddHopFrame();
+ ```
+
+4. Add the authentication middleware to your app
+
+ ```csharp
+ app.UseMiddleware();
+ ```
+
+5. Add the HopFrame pages to your Razor components
+
+ ```csharp
+ app.MapRazorComponents()
+ .AddHopFrameAdminPages()
+ .AddInteractiveServerRenderMode();
+ ```
diff --git a/src/HopFrame.Api/Controller/SecurityController.cs b/src/HopFrame.Api/Controller/SecurityController.cs
new file mode 100644
index 0000000..fcbed7f
--- /dev/null
+++ b/src/HopFrame.Api/Controller/SecurityController.cs
@@ -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>> Login([FromBody] UserLogin login) {
+ return await auth.Login(login);
+ }
+
+ [HttpPost("register")]
+ public async Task>> Register([FromBody] UserRegister register) {
+ return await auth.Register(register);
+ }
+
+ [HttpGet("authenticate")]
+ public async Task>> Authenticate() {
+ return await auth.Authenticate();
+ }
+
+ [HttpDelete("logout"), Authorized]
+ public async Task Logout() {
+ return await auth.Logout();
+ }
+
+ [HttpDelete("delete"), Authorized]
+ public async Task Delete([FromBody] UserPasswordValidation validation) {
+ return await auth.Delete(validation);
+ }
+
+}
\ No newline at end of file
diff --git a/HopFrame.Api/Extensions/MvcExtensions.cs b/src/HopFrame.Api/Extensions/MvcExtensions.cs
similarity index 100%
rename from HopFrame.Api/Extensions/MvcExtensions.cs
rename to src/HopFrame.Api/Extensions/MvcExtensions.cs
diff --git a/src/HopFrame.Api/Extensions/ServiceCollectionExtensions.cs b/src/HopFrame.Api/Extensions/ServiceCollectionExtensions.cs
new file mode 100644
index 0000000..29feb38
--- /dev/null
+++ b/src/HopFrame.Api/Extensions/ServiceCollectionExtensions.cs
@@ -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 {
+
+ ///
+ /// Adds all HopFrame endpoints and services to the application
+ ///
+ /// The service provider to add the services to
+ /// The data source for all HopFrame entities
+ public static void AddHopFrame(this IServiceCollection services) where TDbContext : HopDbContextBase {
+ services.AddMvcCore().UseSpecificControllers(typeof(SecurityController));
+ AddHopFrameNoEndpoints(services);
+ }
+
+ ///
+ /// Adds all HopFrame services to the application
+ ///
+ /// The service provider to add the services to
+ /// The data source for all HopFrame entities
+ public static void AddHopFrameNoEndpoints(this IServiceCollection services) where TDbContext : HopDbContextBase {
+ services.TryAddSingleton();
+ services.AddScoped>();
+
+ services.AddHopFrameAuthentication();
+ }
+
+}
diff --git a/HopFrame.Api/HopFrame.Api.csproj b/src/HopFrame.Api/HopFrame.Api.csproj
similarity index 84%
rename from HopFrame.Api/HopFrame.Api.csproj
rename to src/HopFrame.Api/HopFrame.Api.csproj
index fa215ac..614e37a 100644
--- a/HopFrame.Api/HopFrame.Api.csproj
+++ b/src/HopFrame.Api/HopFrame.Api.csproj
@@ -5,8 +5,12 @@
latest
enable
disable
+
+ HopFrame.Api
+ 1.1.0
README.md
MIT
+ true
diff --git a/src/HopFrame.Api/Logic/IAuthLogic.cs b/src/HopFrame.Api/Logic/IAuthLogic.cs
new file mode 100644
index 0000000..7dc5b78
--- /dev/null
+++ b/src/HopFrame.Api/Logic/IAuthLogic.cs
@@ -0,0 +1,16 @@
+using HopFrame.Api.Models;
+using HopFrame.Security.Models;
+
+namespace HopFrame.Api.Logic;
+
+public interface IAuthLogic {
+ Task>> Login(UserLogin login);
+
+ Task>> Register(UserRegister register);
+
+ Task>> Authenticate();
+
+ Task Logout();
+
+ Task Delete(UserPasswordValidation validation);
+}
\ No newline at end of file
diff --git a/HopFrame.Api/Logic/ILogicResult.cs b/src/HopFrame.Api/Logic/ILogicResult.cs
similarity index 100%
rename from HopFrame.Api/Logic/ILogicResult.cs
rename to src/HopFrame.Api/Logic/ILogicResult.cs
diff --git a/HopFrame.Api/Controller/SecurityController.cs b/src/HopFrame.Api/Logic/Implementation/AuthLogic.cs
similarity index 70%
rename from HopFrame.Api/Controller/SecurityController.cs
rename to src/HopFrame.Api/Logic/Implementation/AuthLogic.cs
index 022db42..405c2cf 100644
--- a/HopFrame.Api/Controller/SecurityController.cs
+++ b/src/HopFrame.Api/Logic/Implementation/AuthLogic.cs
@@ -1,30 +1,24 @@
-using HopFrame.Api.Logic;
using HopFrame.Api.Models;
using HopFrame.Database;
using HopFrame.Database.Models.Entries;
using HopFrame.Security.Authentication;
-using HopFrame.Security.Authorization;
using HopFrame.Security.Claims;
using HopFrame.Security.Models;
using HopFrame.Security.Services;
using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
-namespace HopFrame.Api.Controller;
+namespace HopFrame.Api.Logic.Implementation;
-[ApiController]
-[Route("authentication")]
-public class SecurityController(TDbContext context, IUserService users, ITokenContext tokenContext) : ControllerBase where TDbContext : HopDbContextBase {
-
- [HttpPut("login")]
- public async Task>> Login([FromBody] UserLogin login) {
+public class AuthLogic(TDbContext context, IUserService users, ITokenContext tokenContext, IHttpContextAccessor accessor) : IAuthLogic where TDbContext : HopDbContextBase {
+
+ public async Task>> Login(UserLogin login) {
var user = await users.GetUserByEmail(login.Email);
if (user is null)
return LogicResult>.NotFound("The provided email address was not found");
- if (await users.CheckUserPassword(user, login.Password))
+ if (!await users.CheckUserPassword(user, login.Password))
return LogicResult>.Forbidden("The provided password is not correct");
var refreshToken = new TokenEntry {
@@ -40,11 +34,16 @@ public class SecurityController(TDbContext context, IUserService use
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.RefreshTokenTime,
HttpOnly = true,
Secure = true
});
+ accessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Token, new CookieOptions {
+ MaxAge = HopFrameAuthentication.AccessTokenTime,
+ HttpOnly = true,
+ Secure = true
+ });
await context.Tokens.AddRangeAsync(refreshToken, accessToken);
await context.SaveChangesAsync();
@@ -52,8 +51,7 @@ public class SecurityController(TDbContext context, IUserService use
return LogicResult>.Ok(accessToken.Token);
}
- [HttpPost("register")]
- public async Task>> Register([FromBody] UserRegister register) {
+ public async Task>> Register(UserRegister register) {
if (register.Password.Length < 8)
return LogicResult>.Conflict("Password needs to be at least 8 characters long");
@@ -79,12 +77,12 @@ public class SecurityController(TDbContext context, IUserService use
await context.Tokens.AddRangeAsync(refreshToken, accessToken);
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.RefreshTokenTime,
HttpOnly = 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.AccessTokenTime,
HttpOnly = false,
Secure = true
@@ -93,9 +91,8 @@ public class SecurityController(TDbContext context, IUserService use
return LogicResult>.Ok(accessToken.Token);
}
- [HttpGet("authenticate")]
- public async Task>> Authenticate() {
- var refreshToken = HttpContext.Request.Cookies[ITokenContext.RefreshTokenType];
+ public async Task>> Authenticate() {
+ var refreshToken = accessor.HttpContext?.Request.Cookies[ITokenContext.RefreshTokenType];
if (string.IsNullOrEmpty(refreshToken))
return LogicResult>.Conflict("Refresh token not provided");
@@ -118,7 +115,7 @@ public class SecurityController(TDbContext context, IUserService use
await context.Tokens.AddAsync(accessToken);
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.AccessTokenTime,
HttpOnly = false,
Secure = true
@@ -127,10 +124,9 @@ public class SecurityController(TDbContext context, IUserService use
return LogicResult>.Ok(accessToken.Token);
}
- [HttpDelete("logout"), Authorized]
- public async Task Logout() {
- var accessToken = HttpContext.User.GetAccessTokenId();
- var refreshToken = HttpContext.Request.Cookies[ITokenContext.RefreshTokenType];
+ public async Task Logout() {
+ var accessToken = accessor.HttpContext?.User.GetAccessTokenId();
+ var refreshToken = accessor.HttpContext?.Request.Cookies[ITokenContext.RefreshTokenType];
if (string.IsNullOrEmpty(accessToken) || string.IsNullOrEmpty(refreshToken))
return LogicResult.Conflict("access or refresh token not provided");
@@ -147,22 +143,22 @@ public class SecurityController(TDbContext context, IUserService use
context.Tokens.Remove(tokenEntries[1]);
await context.SaveChangesAsync();
- HttpContext.Response.Cookies.Delete(ITokenContext.RefreshTokenType);
- HttpContext.Response.Cookies.Delete(ITokenContext.AccessTokenType);
+ accessor.HttpContext?.Response.Cookies.Delete(ITokenContext.RefreshTokenType);
+ accessor.HttpContext?.Response.Cookies.Delete(ITokenContext.AccessTokenType);
return LogicResult.Ok();
}
- [HttpDelete("delete"), Authorized]
- public async Task Delete([FromBody] UserPasswordValidation validation) {
+ public async Task Delete(UserPasswordValidation validation) {
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");
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();
}
diff --git a/HopFrame.Api/Logic/LogicResult.cs b/src/HopFrame.Api/Logic/LogicResult.cs
similarity index 100%
rename from HopFrame.Api/Logic/LogicResult.cs
rename to src/HopFrame.Api/Logic/LogicResult.cs
diff --git a/HopFrame.Api/Models/SingleValueResult.cs b/src/HopFrame.Api/Models/SingleValueResult.cs
similarity index 100%
rename from HopFrame.Api/Models/SingleValueResult.cs
rename to src/HopFrame.Api/Models/SingleValueResult.cs
diff --git a/HopFrame.Api/Models/UserPasswordValidation.cs b/src/HopFrame.Api/Models/UserPasswordValidation.cs
similarity index 100%
rename from HopFrame.Api/Models/UserPasswordValidation.cs
rename to src/HopFrame.Api/Models/UserPasswordValidation.cs
diff --git a/src/HopFrame.Api/README.md b/src/HopFrame.Api/README.md
new file mode 100644
index 0000000..67b946b
--- /dev/null
+++ b/src/HopFrame.Api/README.md
@@ -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();
+ builder.Services.AddHopFrame();
+ ```
+
+# 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();
+//builder.Services.AddHopFrame();
+services.AddHopFrameNoEndpoints();
+```
+
+# 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(ILogicResult result);
+
+ public static implicit operator ActionResult(LogicResult v);
+}
+
+public class LogicResult : ILogicResult {
+ public static LogicResult Ok();
+
+ public static LogicResult Ok(T result);
+
+ ...
+}
+```
+
+## IAuthLogic
+This service handles all logic needed to provide the authentication endpoints by using the LogicResults.
+
+```csharp
+public interface IAuthLogic {
+ Task>> Login(UserLogin login);
+
+ Task>> Register(UserRegister register);
+
+ Task>> Authenticate();
+
+ Task Logout();
+
+ Task Delete(UserPasswordValidation validation);
+}
+```
\ No newline at end of file
diff --git a/HopFrame.Database/HopDbContextBase.cs b/src/HopFrame.Database/HopDbContextBase.cs
similarity index 100%
rename from HopFrame.Database/HopDbContextBase.cs
rename to src/HopFrame.Database/HopDbContextBase.cs
diff --git a/HopFrame.Database/HopFrame.Database.csproj b/src/HopFrame.Database/HopFrame.Database.csproj
similarity index 83%
rename from HopFrame.Database/HopFrame.Database.csproj
rename to src/HopFrame.Database/HopFrame.Database.csproj
index 870f818..d88b757 100644
--- a/HopFrame.Database/HopFrame.Database.csproj
+++ b/src/HopFrame.Database/HopFrame.Database.csproj
@@ -5,8 +5,12 @@
latest
enable
disable
+
+ HopFrame.Database
+ 1.1.0
README.md
MIT
+ true
diff --git a/HopFrame.Database/Models/Entries/GroupEntry.cs b/src/HopFrame.Database/Models/Entries/GroupEntry.cs
similarity index 100%
rename from HopFrame.Database/Models/Entries/GroupEntry.cs
rename to src/HopFrame.Database/Models/Entries/GroupEntry.cs
diff --git a/HopFrame.Database/Models/Entries/PermissionEntry.cs b/src/HopFrame.Database/Models/Entries/PermissionEntry.cs
similarity index 100%
rename from HopFrame.Database/Models/Entries/PermissionEntry.cs
rename to src/HopFrame.Database/Models/Entries/PermissionEntry.cs
diff --git a/HopFrame.Database/Models/Entries/TokenEntry.cs b/src/HopFrame.Database/Models/Entries/TokenEntry.cs
similarity index 100%
rename from HopFrame.Database/Models/Entries/TokenEntry.cs
rename to src/HopFrame.Database/Models/Entries/TokenEntry.cs
diff --git a/HopFrame.Database/Models/Entries/UserEntry.cs b/src/HopFrame.Database/Models/Entries/UserEntry.cs
similarity index 100%
rename from HopFrame.Database/Models/Entries/UserEntry.cs
rename to src/HopFrame.Database/Models/Entries/UserEntry.cs
diff --git a/HopFrame.Database/Models/ModelExtensions.cs b/src/HopFrame.Database/Models/ModelExtensions.cs
similarity index 100%
rename from HopFrame.Database/Models/ModelExtensions.cs
rename to src/HopFrame.Database/Models/ModelExtensions.cs
diff --git a/HopFrame.Database/Models/Permission.cs b/src/HopFrame.Database/Models/Permission.cs
similarity index 100%
rename from HopFrame.Database/Models/Permission.cs
rename to src/HopFrame.Database/Models/Permission.cs
diff --git a/HopFrame.Database/Models/PermissionGroup.cs b/src/HopFrame.Database/Models/PermissionGroup.cs
similarity index 100%
rename from HopFrame.Database/Models/PermissionGroup.cs
rename to src/HopFrame.Database/Models/PermissionGroup.cs
diff --git a/HopFrame.Database/Models/User.cs b/src/HopFrame.Database/Models/User.cs
similarity index 100%
rename from HopFrame.Database/Models/User.cs
rename to src/HopFrame.Database/Models/User.cs
diff --git a/HopFrame.Database/README.md b/src/HopFrame.Database/README.md
similarity index 100%
rename from HopFrame.Database/README.md
rename to src/HopFrame.Database/README.md
diff --git a/HopFrame.Web/AdminPermissions.cs b/src/HopFrame.Security/AdminPermissions.cs
similarity index 95%
rename from HopFrame.Web/AdminPermissions.cs
rename to src/HopFrame.Security/AdminPermissions.cs
index 499cdb0..7f45afc 100644
--- a/HopFrame.Web/AdminPermissions.cs
+++ b/src/HopFrame.Security/AdminPermissions.cs
@@ -1,4 +1,4 @@
-namespace HopFrame.Web;
+namespace HopFrame.Security;
public static class AdminPermissions {
public const string IsAdmin = "hopframe.admin";
diff --git a/HopFrame.Security/Authentication/HopFrameAuthentication.cs b/src/HopFrame.Security/Authentication/HopFrameAuthentication.cs
similarity index 93%
rename from HopFrame.Security/Authentication/HopFrameAuthentication.cs
rename to src/HopFrame.Security/Authentication/HopFrameAuthentication.cs
index e688096..8232d4c 100644
--- a/HopFrame.Security/Authentication/HopFrameAuthentication.cs
+++ b/src/HopFrame.Security/Authentication/HopFrameAuthentication.cs
@@ -29,6 +29,8 @@ public class HopFrameAuthentication(
protected override async Task HandleAuthenticateAsync() {
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");
var tokenEntry = await context.Tokens.SingleOrDefaultAsync(token => token.Token == accessToken);
diff --git a/HopFrame.Security/Authentication/HopFrameAuthenticationExtensions.cs b/src/HopFrame.Security/Authentication/HopFrameAuthenticationExtensions.cs
similarity index 100%
rename from HopFrame.Security/Authentication/HopFrameAuthenticationExtensions.cs
rename to src/HopFrame.Security/Authentication/HopFrameAuthenticationExtensions.cs
diff --git a/HopFrame.Security/Authorization/AuthorizedAttribute.cs b/src/HopFrame.Security/Authorization/AuthorizedAttribute.cs
similarity index 100%
rename from HopFrame.Security/Authorization/AuthorizedAttribute.cs
rename to src/HopFrame.Security/Authorization/AuthorizedAttribute.cs
diff --git a/HopFrame.Security/Authorization/AuthorizedFilter.cs b/src/HopFrame.Security/Authorization/AuthorizedFilter.cs
similarity index 100%
rename from HopFrame.Security/Authorization/AuthorizedFilter.cs
rename to src/HopFrame.Security/Authorization/AuthorizedFilter.cs
diff --git a/HopFrame.Security/Authorization/PermissionValidator.cs b/src/HopFrame.Security/Authorization/PermissionValidator.cs
similarity index 61%
rename from HopFrame.Security/Authorization/PermissionValidator.cs
rename to src/HopFrame.Security/Authorization/PermissionValidator.cs
index d4280b9..d3a844b 100644
--- a/HopFrame.Security/Authorization/PermissionValidator.cs
+++ b/src/HopFrame.Security/Authorization/PermissionValidator.cs
@@ -6,12 +6,16 @@ public static class PermissionValidator {
var permLow = permission.ToLower();
var permsLow = permissions.Select(perm => perm.ToLower()).ToArray();
- if (permsLow.Any(perm => perm == permLow || perm == "*")) return true;
+ if (permsLow.Any(perm =>
+ perm == permLow ||
+ (perm.Length > permLow.Length && perm.StartsWith(permLow) && perm.ToCharArray()[permLow.Length] == '.') ||
+ perm == "*"))
+ return true;
foreach (var perm in permsLow) {
if (!perm.EndsWith(".*")) continue;
- var permissionGroup = perm.Replace(".*", "");
+ var permissionGroup = perm.Substring(0, perm.Length - 1);
if (permLow.StartsWith(permissionGroup)) return true;
}
diff --git a/HopFrame.Security/Claims/HopFrameClaimTypes.cs b/src/HopFrame.Security/Claims/HopFrameClaimTypes.cs
similarity index 100%
rename from HopFrame.Security/Claims/HopFrameClaimTypes.cs
rename to src/HopFrame.Security/Claims/HopFrameClaimTypes.cs
diff --git a/HopFrame.Security/Claims/ITokenContext.cs b/src/HopFrame.Security/Claims/ITokenContext.cs
similarity index 100%
rename from HopFrame.Security/Claims/ITokenContext.cs
rename to src/HopFrame.Security/Claims/ITokenContext.cs
diff --git a/HopFrame.Security/Claims/TokenContextImplementor.cs b/src/HopFrame.Security/Claims/TokenContextImplementor.cs
similarity index 100%
rename from HopFrame.Security/Claims/TokenContextImplementor.cs
rename to src/HopFrame.Security/Claims/TokenContextImplementor.cs
diff --git a/HopFrame.Security/EncryptionManager.cs b/src/HopFrame.Security/EncryptionManager.cs
similarity index 100%
rename from HopFrame.Security/EncryptionManager.cs
rename to src/HopFrame.Security/EncryptionManager.cs
diff --git a/HopFrame.Security/HopFrame.Security.csproj b/src/HopFrame.Security/HopFrame.Security.csproj
similarity index 87%
rename from HopFrame.Security/HopFrame.Security.csproj
rename to src/HopFrame.Security/HopFrame.Security.csproj
index 8437d1d..b8a814b 100644
--- a/HopFrame.Security/HopFrame.Security.csproj
+++ b/src/HopFrame.Security/HopFrame.Security.csproj
@@ -6,8 +6,12 @@
enable
disable
HopFrame.Security
+
+ HopFrame.Security
+ 1.1.0
README.md
MIT
+ true
diff --git a/HopFrame.Security/Models/UserLogin.cs b/src/HopFrame.Security/Models/UserLogin.cs
similarity index 100%
rename from HopFrame.Security/Models/UserLogin.cs
rename to src/HopFrame.Security/Models/UserLogin.cs
diff --git a/HopFrame.Security/Models/UserRegister.cs b/src/HopFrame.Security/Models/UserRegister.cs
similarity index 100%
rename from HopFrame.Security/Models/UserRegister.cs
rename to src/HopFrame.Security/Models/UserRegister.cs
diff --git a/src/HopFrame.Security/README.md b/src/HopFrame.Security/README.md
new file mode 100644
index 0000000..cc4b5d0
--- /dev/null
+++ b/src/HopFrame.Security/README.md
@@ -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> GetUsers();
+
+ Task GetUser(Guid userId);
+
+ Task GetUserByEmail(string email);
+
+ Task GetUserByUsername(string username);
+
+ Task AddUser(UserRegister user);
+
+ Task UpdateUser(User user);
+
+ Task DeleteUser(User user);
+
+ Task 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 HasPermission(string permission, Guid user);
+
+ Task> GetPermissionGroups();
+
+ Task GetPermissionGroup(string name);
+
+ Task EditPermissionGroup(PermissionGroup group);
+
+ Task> GetUserPermissionGroups(User user);
+
+ Task RemoveGroupFromUser(User user, PermissionGroup group);
+
+ Task CreatePermissionGroup(string name, bool isDefault = false, string description = null);
+
+ Task DeletePermissionGroup(PermissionGroup group);
+
+ Task GetPermission(string name, IPermissionOwner owner);
+
+ Task AddPermission(IPermissionOwner owner, string permission);
+
+ Task RemovePermission(Permission permission);
+
+ Task GetFullPermissions(string user);
+}
+```
diff --git a/HopFrame.Security/Services/IPermissionService.cs b/src/HopFrame.Security/Services/IPermissionService.cs
similarity index 84%
rename from HopFrame.Security/Services/IPermissionService.cs
rename to src/HopFrame.Security/Services/IPermissionService.cs
index 2071ce9..38a9000 100644
--- a/HopFrame.Security/Services/IPermissionService.cs
+++ b/src/HopFrame.Security/Services/IPermissionService.cs
@@ -2,6 +2,13 @@ using HopFrame.Database.Models;
namespace HopFrame.Security.Services;
+///
+/// permission system:
+/// - "*" -> all rights
+/// - "group.[name]" -> group member
+/// - "[namespace].[name]" -> single permission
+/// - "[namespace].*" -> all permissions in the namespace
+///
public interface IPermissionService {
Task HasPermission(string permission, Guid user);
diff --git a/HopFrame.Security/Services/IUserService.cs b/src/HopFrame.Security/Services/IUserService.cs
similarity index 100%
rename from HopFrame.Security/Services/IUserService.cs
rename to src/HopFrame.Security/Services/IUserService.cs
diff --git a/HopFrame.Security/Services/Implementation/PermissionService.cs b/src/HopFrame.Security/Services/Implementation/PermissionService.cs
similarity index 100%
rename from HopFrame.Security/Services/Implementation/PermissionService.cs
rename to src/HopFrame.Security/Services/Implementation/PermissionService.cs
diff --git a/HopFrame.Security/Services/Implementation/UserService.cs b/src/HopFrame.Security/Services/Implementation/UserService.cs
similarity index 100%
rename from HopFrame.Security/Services/Implementation/UserService.cs
rename to src/HopFrame.Security/Services/Implementation/UserService.cs
diff --git a/src/HopFrame.Web/AdminPermissions.cs b/src/HopFrame.Web/AdminPermissions.cs
new file mode 100644
index 0000000..e1146ca
--- /dev/null
+++ b/src/HopFrame.Web/AdminPermissions.cs
@@ -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;
+}
diff --git a/HopFrame.Web/AuthMiddleware.cs b/src/HopFrame.Web/AuthMiddleware.cs
similarity index 100%
rename from HopFrame.Web/AuthMiddleware.cs
rename to src/HopFrame.Web/AuthMiddleware.cs
diff --git a/HopFrame.Web/Components/Administration/GroupAddModal.razor b/src/HopFrame.Web/Components/Administration/GroupAddModal.razor
similarity index 96%
rename from HopFrame.Web/Components/Administration/GroupAddModal.razor
rename to src/HopFrame.Web/Components/Administration/GroupAddModal.razor
index 95c710b..d1e2723 100644
--- a/HopFrame.Web/Components/Administration/GroupAddModal.razor
+++ b/src/HopFrame.Web/Components/Administration/GroupAddModal.razor
@@ -167,7 +167,7 @@
}
if (_isEdit) {
- if (!(await Permissions.HasPermission(AdminPermissions.EditGroup, Context.User.Id))) {
+ if (!(await Permissions.HasPermission(Security.AdminPermissions.EditGroup, Context.User.Id))) {
await NoEditPermissions();
return;
}
@@ -202,7 +202,7 @@
}
if (_isEdit) {
- if (!(await Permissions.HasPermission(AdminPermissions.EditGroup, Context.User.Id))) {
+ if (!(await Permissions.HasPermission(Security.AdminPermissions.EditGroup, Context.User.Id))) {
await NoEditPermissions();
return;
}
@@ -219,7 +219,7 @@
private async Task AddGroup() {
if (_isEdit) {
- if (!(await Permissions.HasPermission(AdminPermissions.EditGroup, Context.User.Id))) {
+ if (!(await Permissions.HasPermission(Security.AdminPermissions.EditGroup, Context.User.Id))) {
await NoEditPermissions();
return;
}
@@ -239,7 +239,7 @@
return;
}
- if (!(await Permissions.HasPermission(AdminPermissions.AddGroup, Context.User.Id))) {
+ if (!(await Permissions.HasPermission(Security.AdminPermissions.AddGroup, Context.User.Id))) {
await NoAddPermissions();
return;
}
diff --git a/HopFrame.Web/Components/Administration/HopIconDisplay.razor b/src/HopFrame.Web/Components/Administration/HopIconDisplay.razor
similarity index 100%
rename from HopFrame.Web/Components/Administration/HopIconDisplay.razor
rename to src/HopFrame.Web/Components/Administration/HopIconDisplay.razor
diff --git a/HopFrame.Web/Components/Administration/UserAddModal.razor b/src/HopFrame.Web/Components/Administration/UserAddModal.razor
similarity index 97%
rename from HopFrame.Web/Components/Administration/UserAddModal.razor
rename to src/HopFrame.Web/Components/Administration/UserAddModal.razor
index 2a743af..96ff62b 100644
--- a/HopFrame.Web/Components/Administration/UserAddModal.razor
+++ b/src/HopFrame.Web/Components/Administration/UserAddModal.razor
@@ -69,7 +69,7 @@
}
private async Task AddUser() {
- if (!(await Permissions.HasPermission(AdminPermissions.AddUser, Auth.User.Id))) {
+ if (!(await Permissions.HasPermission(Security.AdminPermissions.AddUser, Auth.User.Id))) {
await NoAddPermissions();
return;
}
diff --git a/HopFrame.Web/Components/Administration/UserEditModal.razor b/src/HopFrame.Web/Components/Administration/UserEditModal.razor
similarity index 94%
rename from HopFrame.Web/Components/Administration/UserEditModal.razor
rename to src/HopFrame.Web/Components/Administration/UserEditModal.razor
index 77f82f6..930dba6 100644
--- a/HopFrame.Web/Components/Administration/UserEditModal.razor
+++ b/src/HopFrame.Web/Components/Administration/UserEditModal.razor
@@ -118,7 +118,7 @@
private string _permissionToAdd;
public async Task ShowAsync(User user) {
- if (!(await Permissions.HasPermission(AdminPermissions.EditUser, Auth.User.Id))) {
+ if (!(await Permissions.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id))) {
await NoEditPermissions();
return;
}
@@ -130,7 +130,7 @@
}
private async Task AddGroup() {
- if (!(await Permissions.HasPermission(AdminPermissions.EditUser, Auth.User.Id))) {
+ if (!(await Permissions.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id))) {
await NoEditPermissions();
return;
}
@@ -158,7 +158,7 @@
}
private async Task RemoveGroup(PermissionGroup group) {
- if (!(await Permissions.HasPermission(AdminPermissions.EditUser, Auth.User.Id))) {
+ if (!(await Permissions.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id))) {
await NoEditPermissions();
return;
}
@@ -186,7 +186,7 @@
}
private async Task AddPermission() {
- if (!(await Permissions.HasPermission(AdminPermissions.EditUser, Auth.User.Id))) {
+ if (!(await Permissions.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id))) {
await NoEditPermissions();
return;
}
@@ -213,7 +213,7 @@
}
private async Task RemovePermission(Permission perm) {
- if (!(await Permissions.HasPermission(AdminPermissions.EditUser, Auth.User.Id))) {
+ if (!(await Permissions.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id))) {
await NoEditPermissions();
return;
}
@@ -241,7 +241,7 @@
}
private async void EditUser() {
- if (!(await Permissions.HasPermission(AdminPermissions.EditUser, Auth.User.Id))) {
+ if (!(await Permissions.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id))) {
await NoEditPermissions();
return;
}
diff --git a/HopFrame.Web/Components/AuthorizedView.razor b/src/HopFrame.Web/Components/AuthorizedView.razor
similarity index 100%
rename from HopFrame.Web/Components/AuthorizedView.razor
rename to src/HopFrame.Web/Components/AuthorizedView.razor
diff --git a/HopFrame.Web/HopFrame.Web.csproj b/src/HopFrame.Web/HopFrame.Web.csproj
similarity index 91%
rename from HopFrame.Web/HopFrame.Web.csproj
rename to src/HopFrame.Web/HopFrame.Web.csproj
index 88fe5c7..ab3c31a 100644
--- a/HopFrame.Web/HopFrame.Web.csproj
+++ b/src/HopFrame.Web/HopFrame.Web.csproj
@@ -5,8 +5,12 @@
disable
enable
true
+
+ HopFrame.Web
+ 1.1.0
README.md
MIT
+ true
diff --git a/HopFrame.Web/Model/NavigationItem.cs b/src/HopFrame.Web/Model/NavigationItem.cs
similarity index 100%
rename from HopFrame.Web/Model/NavigationItem.cs
rename to src/HopFrame.Web/Model/NavigationItem.cs
diff --git a/HopFrame.Web/Model/PermissionGroupAdd.cs b/src/HopFrame.Web/Model/PermissionGroupAdd.cs
similarity index 100%
rename from HopFrame.Web/Model/PermissionGroupAdd.cs
rename to src/HopFrame.Web/Model/PermissionGroupAdd.cs
diff --git a/HopFrame.Web/Model/RegisterData.cs b/src/HopFrame.Web/Model/RegisterData.cs
similarity index 100%
rename from HopFrame.Web/Model/RegisterData.cs
rename to src/HopFrame.Web/Model/RegisterData.cs
diff --git a/HopFrame.Web/Model/UserAdd.cs b/src/HopFrame.Web/Model/UserAdd.cs
similarity index 100%
rename from HopFrame.Web/Model/UserAdd.cs
rename to src/HopFrame.Web/Model/UserAdd.cs
diff --git a/HopFrame.Web/Pages/Administration/AdminDashboard.razor b/src/HopFrame.Web/Pages/Administration/AdminDashboard.razor
similarity index 100%
rename from HopFrame.Web/Pages/Administration/AdminDashboard.razor
rename to src/HopFrame.Web/Pages/Administration/AdminDashboard.razor
diff --git a/HopFrame.Web/Pages/Administration/AdminLogin.razor b/src/HopFrame.Web/Pages/Administration/AdminLogin.razor
similarity index 100%
rename from HopFrame.Web/Pages/Administration/AdminLogin.razor
rename to src/HopFrame.Web/Pages/Administration/AdminLogin.razor
diff --git a/HopFrame.Web/Pages/Administration/AdminLogin.razor.css b/src/HopFrame.Web/Pages/Administration/AdminLogin.razor.css
similarity index 100%
rename from HopFrame.Web/Pages/Administration/AdminLogin.razor.css
rename to src/HopFrame.Web/Pages/Administration/AdminLogin.razor.css
diff --git a/HopFrame.Web/Pages/Administration/GroupsPage.razor b/src/HopFrame.Web/Pages/Administration/GroupsPage.razor
similarity index 93%
rename from HopFrame.Web/Pages/Administration/GroupsPage.razor
rename to src/HopFrame.Web/Pages/Administration/GroupsPage.razor
index 1308db9..9040d63 100644
--- a/HopFrame.Web/Pages/Administration/GroupsPage.razor
+++ b/src/HopFrame.Web/Pages/Administration/GroupsPage.razor
@@ -16,7 +16,7 @@
@using HopFrame.Web.Pages.Administration.Layout
Groups
-
+
@@ -32,7 +32,7 @@
Search
-
+
Add Group
@@ -112,8 +112,8 @@
protected override async Task OnInitializedAsync() {
_groups = await Permissions.GetPermissionGroups();
- _hasEditPrivileges = await Permissions.HasPermission(AdminPermissions.EditGroup, Auth.User.Id);
- _hasDeletePrivileges = await Permissions.HasPermission(AdminPermissions.DeleteGroup, Auth.User.Id);
+ _hasEditPrivileges = await Permissions.HasPermission(Security.AdminPermissions.EditGroup, Auth.User.Id);
+ _hasDeletePrivileges = await Permissions.HasPermission(Security.AdminPermissions.DeleteGroup, Auth.User.Id);
}
private async Task Reload() {
diff --git a/HopFrame.Web/Pages/Administration/GroupsPage.razor.css b/src/HopFrame.Web/Pages/Administration/GroupsPage.razor.css
similarity index 100%
rename from HopFrame.Web/Pages/Administration/GroupsPage.razor.css
rename to src/HopFrame.Web/Pages/Administration/GroupsPage.razor.css
diff --git a/HopFrame.Web/Pages/Administration/Layout/AdminLayout.razor b/src/HopFrame.Web/Pages/Administration/Layout/AdminLayout.razor
similarity index 86%
rename from HopFrame.Web/Pages/Administration/Layout/AdminLayout.razor
rename to src/HopFrame.Web/Pages/Administration/Layout/AdminLayout.razor
index e3a4611..0e92e3e 100644
--- a/HopFrame.Web/Pages/Administration/Layout/AdminLayout.razor
+++ b/src/HopFrame.Web/Pages/Administration/Layout/AdminLayout.razor
@@ -2,7 +2,7 @@
@using BlazorStrap.V5
@inherits LayoutComponentBase
-
+
diff --git a/HopFrame.Web/Pages/Administration/Layout/AdminMenu.razor b/src/HopFrame.Web/Pages/Administration/Layout/AdminMenu.razor
similarity index 95%
rename from HopFrame.Web/Pages/Administration/Layout/AdminMenu.razor
rename to src/HopFrame.Web/Pages/Administration/Layout/AdminMenu.razor
index 275afd2..233cf52 100644
--- a/HopFrame.Web/Pages/Administration/Layout/AdminMenu.razor
+++ b/src/HopFrame.Web/Pages/Administration/Layout/AdminMenu.razor
@@ -53,13 +53,13 @@
Name = "Users",
Url = "administration/users",
Description = "On this page you can manage all user accounts.",
- Permission = AdminPermissions.ViewUsers
+ Permission = Security.AdminPermissions.ViewUsers
},
new () {
Name = "Groups",
Url = "administration/groups",
Description = "On this page you can view, create, edit and delete permission groups.",
- Permission = AdminPermissions.ViewGroups
+ Permission = Security.AdminPermissions.ViewGroups
}
};
diff --git a/HopFrame.Web/Pages/Administration/Layout/EmptyLayout.razor b/src/HopFrame.Web/Pages/Administration/Layout/EmptyLayout.razor
similarity index 100%
rename from HopFrame.Web/Pages/Administration/Layout/EmptyLayout.razor
rename to src/HopFrame.Web/Pages/Administration/Layout/EmptyLayout.razor
diff --git a/HopFrame.Web/Pages/Administration/UsersPage.razor b/src/HopFrame.Web/Pages/Administration/UsersPage.razor
similarity index 96%
rename from HopFrame.Web/Pages/Administration/UsersPage.razor
rename to src/HopFrame.Web/Pages/Administration/UsersPage.razor
index 812cdf7..0bd7e96 100644
--- a/HopFrame.Web/Pages/Administration/UsersPage.razor
+++ b/src/HopFrame.Web/Pages/Administration/UsersPage.razor
@@ -16,7 +16,7 @@
@using HopFrame.Web.Components.Administration
Users
-
+
@@ -33,7 +33,7 @@
Search
-
+
Add User
@@ -123,8 +123,8 @@
_userGroups.Add(user.Id, groups.LastOrDefault());
}
- _hasEditPrivileges = await PermissionsService.HasPermission(AdminPermissions.EditUser, Auth.User.Id);
- _hasDeletePrivileges = await PermissionsService.HasPermission(AdminPermissions.DeleteUser, Auth.User.Id);
+ _hasEditPrivileges = await PermissionsService.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id);
+ _hasDeletePrivileges = await PermissionsService.HasPermission(Security.AdminPermissions.DeleteUser, Auth.User.Id);
}
private async Task Reload() {
diff --git a/HopFrame.Web/Pages/Administration/UsersPage.razor.css b/src/HopFrame.Web/Pages/Administration/UsersPage.razor.css
similarity index 100%
rename from HopFrame.Web/Pages/Administration/UsersPage.razor.css
rename to src/HopFrame.Web/Pages/Administration/UsersPage.razor.css
diff --git a/src/HopFrame.Web/README.md b/src/HopFrame.Web/README.md
new file mode 100644
index 0000000..28b55c5
--- /dev/null
+++ b/src/HopFrame.Web/README.md
@@ -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();
+ builder.Services.AddHopFrame();
+ ```
+
+4. Add the authentication middleware to your app
+
+ ```csharp
+ app.UseMiddleware();
+ ```
+
+5. Add the HopFrame pages to your Razor components
+
+ ```csharp
+ app.MapRazorComponents()
+ .AddHopFrameAdminPages()
+ .AddInteractiveServerRenderMode();
+ ```
+
+# 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 Login(UserLogin login);
+ Task Logout();
+
+ Task RefreshLogin();
+ Task IsLoggedIn();
+}
+```
diff --git a/HopFrame.Web/ServiceCollectionExtensions.cs b/src/HopFrame.Web/ServiceCollectionExtensions.cs
similarity index 84%
rename from HopFrame.Web/ServiceCollectionExtensions.cs
rename to src/HopFrame.Web/ServiceCollectionExtensions.cs
index 8e3c453..b6770d7 100644
--- a/HopFrame.Web/ServiceCollectionExtensions.cs
+++ b/src/HopFrame.Web/ServiceCollectionExtensions.cs
@@ -10,7 +10,7 @@ using Microsoft.Extensions.DependencyInjection;
namespace HopFrame.Web;
public static class ServiceCollectionExtensions {
- public static IServiceCollection AddHopFrameServices(this IServiceCollection services) where TDbContext : HopDbContextBase {
+ public static IServiceCollection AddHopFrame(this IServiceCollection services) where TDbContext : HopDbContextBase {
services.AddHttpClient();
services.AddScoped>();
services.AddTransient();
@@ -18,8 +18,6 @@ public static class ServiceCollectionExtensions {
// Component library's
services.AddSweetAlert2();
services.AddBlazorStrap();
-
- //TODO: Use https://blazorstrap.io/V5/V5
services.AddHopFrameAuthentication();
diff --git a/HopFrame.Web/Services/IAuthService.cs b/src/HopFrame.Web/Services/IAuthService.cs
similarity index 100%
rename from HopFrame.Web/Services/IAuthService.cs
rename to src/HopFrame.Web/Services/IAuthService.cs
diff --git a/HopFrame.Web/Services/Implementation/AuthService.cs b/src/HopFrame.Web/Services/Implementation/AuthService.cs
similarity index 100%
rename from HopFrame.Web/Services/Implementation/AuthService.cs
rename to src/HopFrame.Web/Services/Implementation/AuthService.cs
diff --git a/FrontendTest/.gitignore b/test/FrontendTest/.gitignore
similarity index 100%
rename from FrontendTest/.gitignore
rename to test/FrontendTest/.gitignore
diff --git a/FrontendTest/Components/App.razor b/test/FrontendTest/Components/App.razor
similarity index 100%
rename from FrontendTest/Components/App.razor
rename to test/FrontendTest/Components/App.razor
diff --git a/FrontendTest/Components/Layout/MainLayout.razor b/test/FrontendTest/Components/Layout/MainLayout.razor
similarity index 100%
rename from FrontendTest/Components/Layout/MainLayout.razor
rename to test/FrontendTest/Components/Layout/MainLayout.razor
diff --git a/FrontendTest/Components/Layout/MainLayout.razor.css b/test/FrontendTest/Components/Layout/MainLayout.razor.css
similarity index 100%
rename from FrontendTest/Components/Layout/MainLayout.razor.css
rename to test/FrontendTest/Components/Layout/MainLayout.razor.css
diff --git a/FrontendTest/Components/Layout/NavMenu.razor b/test/FrontendTest/Components/Layout/NavMenu.razor
similarity index 100%
rename from FrontendTest/Components/Layout/NavMenu.razor
rename to test/FrontendTest/Components/Layout/NavMenu.razor
diff --git a/FrontendTest/Components/Layout/NavMenu.razor.css b/test/FrontendTest/Components/Layout/NavMenu.razor.css
similarity index 100%
rename from FrontendTest/Components/Layout/NavMenu.razor.css
rename to test/FrontendTest/Components/Layout/NavMenu.razor.css
diff --git a/FrontendTest/Components/Pages/Counter.razor b/test/FrontendTest/Components/Pages/Counter.razor
similarity index 100%
rename from FrontendTest/Components/Pages/Counter.razor
rename to test/FrontendTest/Components/Pages/Counter.razor
diff --git a/FrontendTest/Components/Pages/Error.razor b/test/FrontendTest/Components/Pages/Error.razor
similarity index 100%
rename from FrontendTest/Components/Pages/Error.razor
rename to test/FrontendTest/Components/Pages/Error.razor
diff --git a/FrontendTest/Components/Pages/Home.razor b/test/FrontendTest/Components/Pages/Home.razor
similarity index 100%
rename from FrontendTest/Components/Pages/Home.razor
rename to test/FrontendTest/Components/Pages/Home.razor
diff --git a/FrontendTest/Components/Pages/Weather.razor b/test/FrontendTest/Components/Pages/Weather.razor
similarity index 100%
rename from FrontendTest/Components/Pages/Weather.razor
rename to test/FrontendTest/Components/Pages/Weather.razor
diff --git a/FrontendTest/Components/Routes.razor b/test/FrontendTest/Components/Routes.razor
similarity index 100%
rename from FrontendTest/Components/Routes.razor
rename to test/FrontendTest/Components/Routes.razor
diff --git a/FrontendTest/Components/_Imports.razor b/test/FrontendTest/Components/_Imports.razor
similarity index 100%
rename from FrontendTest/Components/_Imports.razor
rename to test/FrontendTest/Components/_Imports.razor
diff --git a/FrontendTest/DatabaseContext.cs b/test/FrontendTest/DatabaseContext.cs
similarity index 77%
rename from FrontendTest/DatabaseContext.cs
rename to test/FrontendTest/DatabaseContext.cs
index 20945c9..5da7d59 100644
--- a/FrontendTest/DatabaseContext.cs
+++ b/test/FrontendTest/DatabaseContext.cs
@@ -7,6 +7,6 @@ public class DatabaseContext : HopDbContextBase {
protected override void OnConfiguring(DbContextOptionsBuilder 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;");
}
}
\ No newline at end of file
diff --git a/FrontendTest/FrontendTest.csproj b/test/FrontendTest/FrontendTest.csproj
similarity index 87%
rename from FrontendTest/FrontendTest.csproj
rename to test/FrontendTest/FrontendTest.csproj
index 79d6740..312aa4b 100644
--- a/FrontendTest/FrontendTest.csproj
+++ b/test/FrontendTest/FrontendTest.csproj
@@ -6,10 +6,6 @@
enable
-
-
-
-
@@ -19,4 +15,8 @@
<_ContentIncludedByDefault Remove="wwwroot\bootstrap\bootstrap.min.css.map" />
+
+
+
+
diff --git a/FrontendTest/Program.cs b/test/FrontendTest/Program.cs
similarity index 93%
rename from FrontendTest/Program.cs
rename to test/FrontendTest/Program.cs
index 20102b4..af54f28 100644
--- a/FrontendTest/Program.cs
+++ b/test/FrontendTest/Program.cs
@@ -5,7 +5,7 @@ using HopFrame.Web;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext();
-builder.Services.AddHopFrameServices();
+builder.Services.AddHopFrame();
// Add services to the container.
builder.Services.AddRazorComponents()
diff --git a/FrontendTest/Properties/launchSettings.json b/test/FrontendTest/Properties/launchSettings.json
similarity index 100%
rename from FrontendTest/Properties/launchSettings.json
rename to test/FrontendTest/Properties/launchSettings.json
diff --git a/FrontendTest/appsettings.json b/test/FrontendTest/appsettings.json
similarity index 100%
rename from FrontendTest/appsettings.json
rename to test/FrontendTest/appsettings.json
diff --git a/FrontendTest/wwwroot/app.css b/test/FrontendTest/wwwroot/app.css
similarity index 100%
rename from FrontendTest/wwwroot/app.css
rename to test/FrontendTest/wwwroot/app.css
diff --git a/FrontendTest/wwwroot/favicon.png b/test/FrontendTest/wwwroot/favicon.png
similarity index 100%
rename from FrontendTest/wwwroot/favicon.png
rename to test/FrontendTest/wwwroot/favicon.png
diff --git a/RestApiTest/.gitignore b/test/RestApiTest/.gitignore
similarity index 100%
rename from RestApiTest/.gitignore
rename to test/RestApiTest/.gitignore
diff --git a/RestApiTest/Controllers/TestController.cs b/test/RestApiTest/Controllers/TestController.cs
similarity index 100%
rename from RestApiTest/Controllers/TestController.cs
rename to test/RestApiTest/Controllers/TestController.cs
diff --git a/RestApiTest/DatabaseContext.cs b/test/RestApiTest/DatabaseContext.cs
similarity index 77%
rename from RestApiTest/DatabaseContext.cs
rename to test/RestApiTest/DatabaseContext.cs
index fed4058..3133353 100644
--- a/RestApiTest/DatabaseContext.cs
+++ b/test/RestApiTest/DatabaseContext.cs
@@ -7,6 +7,6 @@ public class DatabaseContext : HopDbContextBase {
protected override void OnConfiguring(DbContextOptionsBuilder 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;");
}
}
\ No newline at end of file
diff --git a/RestApiTest/Program.cs b/test/RestApiTest/Program.cs
similarity index 100%
rename from RestApiTest/Program.cs
rename to test/RestApiTest/Program.cs
diff --git a/RestApiTest/Properties/launchSettings.json b/test/RestApiTest/Properties/launchSettings.json
similarity index 100%
rename from RestApiTest/Properties/launchSettings.json
rename to test/RestApiTest/Properties/launchSettings.json
diff --git a/RestApiTest/RestApiTest.csproj b/test/RestApiTest/RestApiTest.csproj
similarity index 77%
rename from RestApiTest/RestApiTest.csproj
rename to test/RestApiTest/RestApiTest.csproj
index 936e228..2549297 100644
--- a/RestApiTest/RestApiTest.csproj
+++ b/test/RestApiTest/RestApiTest.csproj
@@ -17,9 +17,7 @@
-
-
-
+
diff --git a/RestApiTest/appsettings.json b/test/RestApiTest/appsettings.json
similarity index 100%
rename from RestApiTest/appsettings.json
rename to test/RestApiTest/appsettings.json