Merge branch 'feature/config' into 'dev'
Resolve "Configuratable token times" See merge request leon.hoppe/hopframe!2
This commit was merged in pull request #40.
This commit is contained in:
51
docs/authentication.md
Normal file
51
docs/authentication.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# HopFrame Authentication
|
||||||
|
|
||||||
|
HopFrame uses a token system with a short term access token and a long term refresh token for authenticating users.
|
||||||
|
These tokens are usually provided to the endpoints of the API / Blazor Pages through Cookies:
|
||||||
|
|
||||||
|
| Cookie key | Cookie value sample | Description |
|
||||||
|
|--------------------------------|----------------------------------------|-----------------------------|
|
||||||
|
| HopFrame.Security.RefreshToken | `42047983-914d-418b-841a-4382614231be` | The long term refresh token |
|
||||||
|
| HopFrame.Security.AccessToken | `d39c9432-0831-42df-8844-5e2b70f03eda` | The short term access token |
|
||||||
|
|
||||||
|
The advantage of these cookies is that they are automatically set by the backend and delete themselves, when they are
|
||||||
|
no longer valid.
|
||||||
|
|
||||||
|
The access token can also be delivered through a header called `HopFrame.Authentication` or `Token`.
|
||||||
|
It can also be delivered through a query parameter called `token`. This simplifies requests for images for example
|
||||||
|
because you can directly specify the url in the img tag in html.
|
||||||
|
|
||||||
|
## Authentication configuration
|
||||||
|
|
||||||
|
You can also configure the time span that the tokens are valid using the `appsettings.json` or environment variables
|
||||||
|
by configuring your configuration to load these.
|
||||||
|
>**Hint**: Configuring your application to use environment variables works by simply adding
|
||||||
|
> `builder.Configuration.AddEnvironmentVariables();` to your startup configuration before you add the
|
||||||
|
> custom configurations / HopFrame services.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
You can specify `Seconds`, `Minutes`, `Hours` and `Days` for either of the two token types.
|
||||||
|
These get combined to a single time span.
|
||||||
|
|
||||||
|
#### Configuration example
|
||||||
|
```json
|
||||||
|
"HopFrame": {
|
||||||
|
"Authentication": {
|
||||||
|
"AccessToken": {
|
||||||
|
"Minutes": 30
|
||||||
|
},
|
||||||
|
"RefreshToken": {
|
||||||
|
"Days": 10,
|
||||||
|
"Hours": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Environment variables example
|
||||||
|
```dotenv
|
||||||
|
HOPFRAME__AUTHENTICATION__ACCESSTOKEN__MINUTES=30
|
||||||
|
HOPFRAME__AUTHENTICATION__REFRESHTOKEN__DAYS=10
|
||||||
|
HOPFRAME__AUTHENTICATION__REFRESHTOKEN__HOURS=5
|
||||||
|
```
|
||||||
80
docs/permissions.md
Normal file
80
docs/permissions.md
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# HopFrame Permissions
|
||||||
|
|
||||||
|
Permissions in the HopFrame are simple and effective to use.
|
||||||
|
As discussed in the [repositories](./repositories.md) documentation, you can manage user / group permissions
|
||||||
|
via the `IPermissionRepository` service.
|
||||||
|
|
||||||
|
## How do permissions work in the HopFrame
|
||||||
|
|
||||||
|
Permissions are defined using the . (dot) syntax. This enables you to nest permissions in namespaces.
|
||||||
|
You can also give a user or a group the permission to every permission in a namespace by using the * (star) syntax.
|
||||||
|
|
||||||
|
| Permission | Example | Description |
|
||||||
|
|----------------------|-------------------------------|-------------------------------------------------------|
|
||||||
|
| `*` | `*` | all permissions |
|
||||||
|
| `[namespace].[name]` | `hopframe.admin.users.create` | single permission |
|
||||||
|
| `[namespace].*` | `hopframe.admin.*` | all permissions in that namespace (works recursively) |
|
||||||
|
|
||||||
|
### Reserved namespaces
|
||||||
|
|
||||||
|
| Namespace | Example | Description |
|
||||||
|
|-----------|---------------|------------------------------------------|
|
||||||
|
| `group` | `group.admin` | The user needs to be in a specific group |
|
||||||
|
|
||||||
|
### Permission Groups
|
||||||
|
|
||||||
|
You can manage them through the `IGroupRepository` as described in the [repositories](./repositories.md) documentation.
|
||||||
|
You add permissions just like you would to a user with the `IPermissionRepository`.
|
||||||
|
You can assign a user to a group by assigning the group permission to the user:
|
||||||
|
```csharp
|
||||||
|
permissionRepository.AddPermission(user, "group.admin");
|
||||||
|
```
|
||||||
|
|
||||||
|
## Predefined Permissions
|
||||||
|
|
||||||
|
| Permission | Description |
|
||||||
|
|--------------------------------|-------------------------------|
|
||||||
|
| `hopframe.admin` | Access to the admin dashboard |
|
||||||
|
| `hopframe.admin.users.read` | View all users |
|
||||||
|
| `hopframe.admin.users.update` | Edit a user |
|
||||||
|
| `hopframe.admin.users.delete` | Delete a user |
|
||||||
|
| `hopframe.admin.users.create` | Add a group |
|
||||||
|
| `hopframe.admin.groups.read` | View all groups |
|
||||||
|
| `hopframe.admin.groups.update` | Edit a group |
|
||||||
|
| `hopframe.admin.groups.delete` | Delete a group |
|
||||||
|
| `hopframe.admin.groups.create` | Add a group |
|
||||||
|
|
||||||
|
### Configuring HopFrame permissions
|
||||||
|
|
||||||
|
You can also configure the predefined permissions using the `appsettings.json` or environment variables
|
||||||
|
by configuring your configuration to load these.
|
||||||
|
>**Hint**: Configuring your application to use environment variables works by simply adding
|
||||||
|
> `builder.Configuration.AddEnvironmentVariables();` to your startup configuration before you add the
|
||||||
|
> custom configurations / HopFrame services.
|
||||||
|
|
||||||
|
You can specify `Dashboard` for the dashboard permission and for `Users` and `Groups` you can specify
|
||||||
|
`Create`, `Read`, `Update` and `Delete` permissions.
|
||||||
|
|
||||||
|
#### Configuration example
|
||||||
|
```json
|
||||||
|
"HopFrame": {
|
||||||
|
"Permissions": {
|
||||||
|
"Dashboard": "myapp.dashboard.view",
|
||||||
|
"Users": {
|
||||||
|
"Read": "myapp.read.users"
|
||||||
|
},
|
||||||
|
"Groups": {
|
||||||
|
"Create": "myapp.create.groups",
|
||||||
|
"Update": "myapp.update.groups"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Environment variables example
|
||||||
|
```dotenv
|
||||||
|
HOPFRAME__PERMISSIONS__DASHBOARD="myapp.dashboard.view"
|
||||||
|
HOPFRAME__PERMISSIONS__USERS__READ="myapp.read.users"
|
||||||
|
HOPFRAME__PERMISSIONS__GROUPS__CREATE="myapp.create.groups"
|
||||||
|
HOPFRAME__PERMISSIONS__GROUPS__UPDATE="myapp.update.groups"
|
||||||
|
```
|
||||||
@@ -7,6 +7,8 @@ The HopFrame comes in two variations, you can eiter only use the backend with so
|
|||||||
- [Database](./database.md)
|
- [Database](./database.md)
|
||||||
- [Repositories](./repositories.md)
|
- [Repositories](./repositories.md)
|
||||||
- [Base Models](./models.md)
|
- [Base Models](./models.md)
|
||||||
|
- [Authentication](./authentication.md)
|
||||||
|
- [Permissions](./permissions.md)
|
||||||
|
|
||||||
## HopFrame Web API
|
## HopFrame Web API
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using HopFrame.Api.Logic.Implementation;
|
|||||||
using HopFrame.Database;
|
using HopFrame.Database;
|
||||||
using HopFrame.Security.Authentication;
|
using HopFrame.Security.Authentication;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
|
||||||
@@ -15,23 +16,25 @@ public static class ServiceCollectionExtensions {
|
|||||||
/// Adds all HopFrame endpoints and services to the application
|
/// Adds all HopFrame endpoints and services to the application
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="services">The service provider to add the services to</param>
|
/// <param name="services">The service provider to add the services to</param>
|
||||||
|
/// <param name="configuration">The configuration used to configure HopFrame authentication</param>
|
||||||
/// <typeparam name="TDbContext">The data source for all HopFrame entities</typeparam>
|
/// <typeparam name="TDbContext">The data source for all HopFrame entities</typeparam>
|
||||||
public static void AddHopFrame<TDbContext>(this IServiceCollection services) where TDbContext : HopDbContextBase {
|
public static void AddHopFrame<TDbContext>(this IServiceCollection services, ConfigurationManager configuration) where TDbContext : HopDbContextBase {
|
||||||
services.AddMvcCore().UseSpecificControllers(typeof(SecurityController));
|
services.AddMvcCore().UseSpecificControllers(typeof(SecurityController));
|
||||||
AddHopFrameNoEndpoints<TDbContext>(services);
|
AddHopFrameNoEndpoints<TDbContext>(services, configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds all HopFrame services to the application
|
/// Adds all HopFrame services to the application
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="services">The service provider to add the services to</param>
|
/// <param name="services">The service provider to add the services to</param>
|
||||||
|
/// <param name="configuration">The configuration used to configure HopFrame authentication</param>
|
||||||
/// <typeparam name="TDbContext">The data source for all HopFrame entities</typeparam>
|
/// <typeparam name="TDbContext">The data source for all HopFrame entities</typeparam>
|
||||||
public static void AddHopFrameNoEndpoints<TDbContext>(this IServiceCollection services) where TDbContext : HopDbContextBase {
|
public static void AddHopFrameNoEndpoints<TDbContext>(this IServiceCollection services, ConfigurationManager configuration) where TDbContext : HopDbContextBase {
|
||||||
services.AddHopFrameRepositories<TDbContext>();
|
services.AddHopFrameRepositories<TDbContext>();
|
||||||
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||||
services.AddScoped<IAuthLogic, AuthLogic>();
|
services.AddScoped<IAuthLogic, AuthLogic>();
|
||||||
|
|
||||||
services.AddHopFrameAuthentication();
|
services.AddHopFrameAuthentication(configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ using HopFrame.Security.Authentication;
|
|||||||
using HopFrame.Security.Claims;
|
using HopFrame.Security.Claims;
|
||||||
using HopFrame.Security.Models;
|
using HopFrame.Security.Models;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace HopFrame.Api.Logic.Implementation;
|
namespace HopFrame.Api.Logic.Implementation;
|
||||||
|
|
||||||
internal class AuthLogic(IUserRepository users, ITokenRepository tokens, ITokenContext tokenContext, IHttpContextAccessor accessor) : IAuthLogic {
|
internal class AuthLogic(IUserRepository users, ITokenRepository tokens, ITokenContext tokenContext, IHttpContextAccessor accessor, IOptions<HopFrameAuthenticationOptions> options) : IAuthLogic {
|
||||||
|
|
||||||
public async Task<LogicResult<SingleValueResult<string>>> Login(UserLogin login) {
|
public async Task<LogicResult<SingleValueResult<string>>> Login(UserLogin login) {
|
||||||
var user = await users.GetUserByEmail(login.Email);
|
var user = await users.GetUserByEmail(login.Email);
|
||||||
@@ -23,12 +24,12 @@ internal class AuthLogic(IUserRepository users, ITokenRepository tokens, ITokenC
|
|||||||
var accessToken = await tokens.CreateToken(Token.AccessTokenType, user);
|
var accessToken = await tokens.CreateToken(Token.AccessTokenType, user);
|
||||||
|
|
||||||
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.RefreshTokenType, refreshToken.Content.ToString(), new CookieOptions {
|
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.RefreshTokenType, refreshToken.Content.ToString(), new CookieOptions {
|
||||||
MaxAge = HopFrameAuthentication.RefreshTokenTime,
|
MaxAge = options.Value.RefreshTokenTime,
|
||||||
HttpOnly = true,
|
HttpOnly = true,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Content.ToString(), new CookieOptions {
|
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Content.ToString(), new CookieOptions {
|
||||||
MaxAge = HopFrameAuthentication.AccessTokenTime,
|
MaxAge = options.Value.AccessTokenTime,
|
||||||
HttpOnly = true,
|
HttpOnly = true,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
@@ -54,12 +55,12 @@ internal class AuthLogic(IUserRepository users, ITokenRepository tokens, ITokenC
|
|||||||
var accessToken = await tokens.CreateToken(Token.AccessTokenType, user);
|
var accessToken = await tokens.CreateToken(Token.AccessTokenType, user);
|
||||||
|
|
||||||
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.RefreshTokenType, refreshToken.Content.ToString(), new CookieOptions {
|
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.RefreshTokenType, refreshToken.Content.ToString(), new CookieOptions {
|
||||||
MaxAge = HopFrameAuthentication.RefreshTokenTime,
|
MaxAge = options.Value.RefreshTokenTime,
|
||||||
HttpOnly = true,
|
HttpOnly = true,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Content.ToString(), new CookieOptions {
|
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Content.ToString(), new CookieOptions {
|
||||||
MaxAge = HopFrameAuthentication.AccessTokenTime,
|
MaxAge = options.Value.AccessTokenTime,
|
||||||
HttpOnly = false,
|
HttpOnly = false,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
@@ -81,13 +82,13 @@ internal class AuthLogic(IUserRepository users, ITokenRepository tokens, ITokenC
|
|||||||
if (token.Type != Token.RefreshTokenType)
|
if (token.Type != Token.RefreshTokenType)
|
||||||
return LogicResult<SingleValueResult<string>>.Conflict("The provided token is not a refresh token");
|
return LogicResult<SingleValueResult<string>>.Conflict("The provided token is not a refresh token");
|
||||||
|
|
||||||
if (token.CreatedAt + HopFrameAuthentication.RefreshTokenTime < DateTime.Now)
|
if (token.CreatedAt + options.Value.RefreshTokenTime < DateTime.Now)
|
||||||
return LogicResult<SingleValueResult<string>>.Forbidden("Refresh token is expired");
|
return LogicResult<SingleValueResult<string>>.Forbidden("Refresh token is expired");
|
||||||
|
|
||||||
var accessToken = await tokens.CreateToken(Token.AccessTokenType, token.Owner);
|
var accessToken = await tokens.CreateToken(Token.AccessTokenType, token.Owner);
|
||||||
|
|
||||||
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Content.ToString(), new CookieOptions {
|
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Content.ToString(), new CookieOptions {
|
||||||
MaxAge = HopFrameAuthentication.AccessTokenTime,
|
MaxAge = options.Value.AccessTokenTime,
|
||||||
HttpOnly = false,
|
HttpOnly = false,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
namespace HopFrame.Security;
|
|
||||||
|
|
||||||
public static class AdminPermissions {
|
|
||||||
public const string IsAdmin = "hopframe.admin";
|
|
||||||
|
|
||||||
public const string ViewUsers = "hopframe.admin.users.view";
|
|
||||||
public const string EditUser = "hopframe.admin.users.edit";
|
|
||||||
public const string DeleteUser = "hopframe.admin.users.delete";
|
|
||||||
public const string AddUser = "hopframe.admin.users.add";
|
|
||||||
|
|
||||||
public const string ViewGroups = "hopframe.admin.groups.view";
|
|
||||||
public const string EditGroup = "hopframe.admin.groups.edit";
|
|
||||||
public const string DeleteGroup = "hopframe.admin.groups.delete";
|
|
||||||
public const string AddGroup = "hopframe.admin.groups.add";
|
|
||||||
}
|
|
||||||
@@ -17,23 +17,23 @@ public class HopFrameAuthentication(
|
|||||||
UrlEncoder encoder,
|
UrlEncoder encoder,
|
||||||
ISystemClock clock,
|
ISystemClock clock,
|
||||||
ITokenRepository tokens,
|
ITokenRepository tokens,
|
||||||
IPermissionRepository perms)
|
IPermissionRepository perms,
|
||||||
|
IOptions<HopFrameAuthenticationOptions> tokenOptions)
|
||||||
: AuthenticationHandler<AuthenticationSchemeOptions>(options, logger, encoder, clock) {
|
: AuthenticationHandler<AuthenticationSchemeOptions>(options, logger, encoder, clock) {
|
||||||
|
|
||||||
public const string SchemeName = "HopCore.Authentication";
|
public const string SchemeName = "HopFrame.Authentication";
|
||||||
public static readonly TimeSpan AccessTokenTime = new(0, 0, 5, 0);
|
|
||||||
public static readonly TimeSpan RefreshTokenTime = new(30, 0, 0, 0);
|
|
||||||
|
|
||||||
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[SchemeName];
|
||||||
if (string.IsNullOrEmpty(accessToken)) accessToken = Request.Headers["Token"];
|
if (string.IsNullOrEmpty(accessToken)) accessToken = Request.Headers["Token"];
|
||||||
|
if (string.IsNullOrEmpty(accessToken)) accessToken = Request.Query["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 tokens.GetToken(accessToken);
|
var tokenEntry = await tokens.GetToken(accessToken);
|
||||||
|
|
||||||
if (tokenEntry is null) return AuthenticateResult.Fail("The provided Access Token does not exist");
|
if (tokenEntry is null) return AuthenticateResult.Fail("The provided Access Token does not exist");
|
||||||
if (tokenEntry.CreatedAt + AccessTokenTime < DateTime.Now) return AuthenticateResult.Fail("The provided Access Token is expired");
|
if (tokenEntry.CreatedAt + tokenOptions.Value.AccessTokenTime < DateTime.Now) return AuthenticateResult.Fail("The provided Access Token is expired");
|
||||||
|
|
||||||
if (tokenEntry.Owner is null)
|
if (tokenEntry.Owner is null)
|
||||||
return AuthenticateResult.Fail("The provided Access Token does not match any user");
|
return AuthenticateResult.Fail("The provided Access Token does not match any user");
|
||||||
|
|||||||
@@ -1,23 +1,28 @@
|
|||||||
|
using HopFrame.Security.Authorization;
|
||||||
using HopFrame.Security.Claims;
|
using HopFrame.Security.Claims;
|
||||||
|
using HopFrame.Security.Options;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
|
||||||
namespace HopFrame.Security.Authentication;
|
namespace HopFrame.Security.Authentication;
|
||||||
|
|
||||||
public static class HopFrameAuthenticationExtensions {
|
public static class HopFrameAuthenticationExtensions {
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Configures the WebApplication to use the authentication and authorization of the HopFrame API
|
/// Configures the WebApplication to use the authentication and authorization of the HopFrame API
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="service">The service provider to add the services to</param>
|
/// <param name="service">The service provider to add the services to</param>
|
||||||
/// <typeparam name="TDbContext">The database object that saves all entities that are important for the security api</typeparam>
|
/// <param name="configuration">The configuration used to configure HopFrame authentication</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IServiceCollection AddHopFrameAuthentication(this IServiceCollection service) {
|
public static IServiceCollection AddHopFrameAuthentication(this IServiceCollection service, ConfigurationManager configuration) {
|
||||||
service.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
service.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||||
service.AddScoped<ITokenContext, TokenContextImplementor>();
|
service.AddScoped<ITokenContext, TokenContextImplementor>();
|
||||||
|
|
||||||
|
service.AddOptionsFromConfiguration<HopFrameAuthenticationOptions>(configuration);
|
||||||
|
service.AddOptionsFromConfiguration<AdminPermissionOptions>(configuration);
|
||||||
|
|
||||||
service.AddAuthentication(HopFrameAuthentication.SchemeName).AddScheme<AuthenticationSchemeOptions, HopFrameAuthentication>(HopFrameAuthentication.SchemeName, _ => {});
|
service.AddAuthentication(HopFrameAuthentication.SchemeName).AddScheme<AuthenticationSchemeOptions, HopFrameAuthentication>(HopFrameAuthentication.SchemeName, _ => {});
|
||||||
service.AddAuthorization();
|
service.AddAuthorization();
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using HopFrame.Security.Options;
|
||||||
|
|
||||||
|
namespace HopFrame.Security.Authentication;
|
||||||
|
|
||||||
|
public class HopFrameAuthenticationOptions : OptionsFromConfiguration {
|
||||||
|
public override string Position { get; } = "HopFrame:Authentication";
|
||||||
|
|
||||||
|
public TimeSpan AccessTokenTime => AccessToken is null ? new(0, 0, 5, 0) : new(AccessToken.Days, AccessToken.Hours, AccessToken.Minutes, AccessToken.Seconds);
|
||||||
|
public TimeSpan RefreshTokenTime => RefreshToken is null ? new(30, 0, 0, 0) : new(RefreshToken.Days, RefreshToken.Hours, RefreshToken.Minutes, RefreshToken.Seconds);
|
||||||
|
|
||||||
|
public TokenTime AccessToken { get; set; }
|
||||||
|
public TokenTime RefreshToken { get; set; }
|
||||||
|
|
||||||
|
public class TokenTime {
|
||||||
|
public int Days { get; set; }
|
||||||
|
public int Hours { get; set; }
|
||||||
|
public int Minutes { get; set; }
|
||||||
|
public int Seconds { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using HopFrame.Security.Options;
|
||||||
|
|
||||||
|
namespace HopFrame.Security.Authorization;
|
||||||
|
|
||||||
|
public class AdminPermissionOptions : OptionsFromConfiguration {
|
||||||
|
public override string Position { get; } = "HopFrame:Permissions";
|
||||||
|
|
||||||
|
public string Dashboard { get; set; } = "hopframe.admin";
|
||||||
|
|
||||||
|
public CrudPermission Users { get; set; } = new() {
|
||||||
|
Read = "hopframe.admin.users.read",
|
||||||
|
Update = "hopframe.admin.users.update",
|
||||||
|
Delete = "hopframe.admin.users.delete",
|
||||||
|
Create = "hopframe.admin.users.create"
|
||||||
|
};
|
||||||
|
|
||||||
|
public CrudPermission Groups { get; set; } = new() {
|
||||||
|
Read = "hopframe.admin.groups.read",
|
||||||
|
Update = "hopframe.admin.groups.update",
|
||||||
|
Delete = "hopframe.admin.groups.delete",
|
||||||
|
Create = "hopframe.admin.groups.create"
|
||||||
|
};
|
||||||
|
|
||||||
|
public class CrudPermission {
|
||||||
|
public string Create { get; set; }
|
||||||
|
public string Read { get; set; }
|
||||||
|
public string Update { get; set; }
|
||||||
|
public string Delete { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
namespace HopFrame.Security.Options;
|
||||||
|
|
||||||
|
public abstract class OptionsFromConfiguration {
|
||||||
|
public abstract string Position { get; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace HopFrame.Security.Options;
|
||||||
|
|
||||||
|
public static class OptionsFromConfigurationExtensions {
|
||||||
|
public static void AddOptionsFromConfiguration<T>(this IServiceCollection services, IConfiguration configuration) where T : OptionsFromConfiguration {
|
||||||
|
T optionsInstance = (T)Activator.CreateInstance(typeof(T));
|
||||||
|
string position = optionsInstance?.Position;
|
||||||
|
if (position is null) {
|
||||||
|
throw new ArgumentException($"""Configuration "{typeof(T).Name}" has no position configured!""");
|
||||||
|
}
|
||||||
|
|
||||||
|
services.Configure((Action<T>)(options => {
|
||||||
|
IConfigurationSection section = configuration.GetSection(position);
|
||||||
|
section.Bind(options);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,6 @@ public sealed class AdminPermissionsAttribute(string view = null, string create
|
|||||||
Create = create,
|
Create = create,
|
||||||
Update = update,
|
Update = update,
|
||||||
Delete = delete,
|
Delete = delete,
|
||||||
View = view
|
Read = view
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public interface IAdminPageGenerator<TModel> {
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="permission">the specified permission</param>
|
/// <param name="permission">the specified permission</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
IAdminPageGenerator<TModel> ViewPermission(string permission);
|
IAdminPageGenerator<TModel> ReadPermission(string permission);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the permission needed to create a new Entry
|
/// Sets the permission needed to create a new Entry
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ internal sealed class AdminPageGenerator<TModel> : IAdminPageGenerator<TModel>,
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAdminPageGenerator<TModel> ViewPermission(string permission) {
|
public IAdminPageGenerator<TModel> ReadPermission(string permission) {
|
||||||
Page.Permissions.View = permission;
|
Page.Permissions.Read = permission;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +165,7 @@ internal sealed class AdminPageGenerator<TModel> : IAdminPageGenerator<TModel>,
|
|||||||
var attribute = attributes.Single(a => a is AdminPermissionsAttribute) as AdminPermissionsAttribute;
|
var attribute = attributes.Single(a => a is AdminPermissionsAttribute) as AdminPermissionsAttribute;
|
||||||
CreatePermission(attribute?.Permissions.Create);
|
CreatePermission(attribute?.Permissions.Create);
|
||||||
UpdatePermission(attribute?.Permissions.Update);
|
UpdatePermission(attribute?.Permissions.Update);
|
||||||
ViewPermission(attribute?.Permissions.View);
|
ReadPermission(attribute?.Permissions.Read);
|
||||||
DeletePermission(attribute?.Permissions.Delete);
|
DeletePermission(attribute?.Permissions.Delete);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
namespace HopFrame.Web.Admin.Models;
|
namespace HopFrame.Web.Admin.Models;
|
||||||
|
|
||||||
public sealed class AdminPagePermissions {
|
public sealed class AdminPagePermissions {
|
||||||
public string View { get; set; }
|
public string Read { get; set; }
|
||||||
public string Create { get; set; }
|
public string Create { get; set; }
|
||||||
public string Update { get; set; }
|
public string Update { get; set; }
|
||||||
public string Delete { get; set; }
|
public string Delete { get; set; }
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using HopFrame.Database.Models;
|
using HopFrame.Database.Models;
|
||||||
using HopFrame.Security;
|
using HopFrame.Security;
|
||||||
|
using HopFrame.Security.Authorization;
|
||||||
using HopFrame.Web.Admin;
|
using HopFrame.Web.Admin;
|
||||||
using HopFrame.Web.Admin.Attributes;
|
using HopFrame.Web.Admin.Attributes;
|
||||||
using HopFrame.Web.Admin.Generators;
|
using HopFrame.Web.Admin.Generators;
|
||||||
using HopFrame.Web.Admin.Models;
|
using HopFrame.Web.Admin.Models;
|
||||||
using HopFrame.Web.Provider;
|
using HopFrame.Web.Provider;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace HopFrame.Web;
|
namespace HopFrame.Web;
|
||||||
|
|
||||||
internal class HopAdminContext : AdminPagesContext {
|
internal class HopAdminContext(IOptions<AdminPermissionOptions> options) : AdminPagesContext {
|
||||||
|
|
||||||
[AdminPageUrl("users")]
|
[AdminPageUrl("users")]
|
||||||
public AdminPage<User> Users { get; set; }
|
public AdminPage<User> Users { get; set; }
|
||||||
@@ -21,10 +23,10 @@ internal class HopAdminContext : AdminPagesContext {
|
|||||||
generator.Page<User>()
|
generator.Page<User>()
|
||||||
.Description("On this page you can manage all user accounts.")
|
.Description("On this page you can manage all user accounts.")
|
||||||
.ConfigureProvider<UserProvider>()
|
.ConfigureProvider<UserProvider>()
|
||||||
.ViewPermission(AdminPermissions.ViewUsers)
|
.ReadPermission(options.Value.Users.Read)
|
||||||
.CreatePermission(AdminPermissions.AddUser)
|
.CreatePermission(options.Value.Users.Create)
|
||||||
.UpdatePermission(AdminPermissions.EditUser)
|
.UpdatePermission(options.Value.Users.Update)
|
||||||
.DeletePermission(AdminPermissions.DeleteUser);
|
.DeletePermission(options.Value.Users.Delete);
|
||||||
|
|
||||||
generator.Page<User>().Property(u => u.Password)
|
generator.Page<User>().Property(u => u.Password)
|
||||||
.DisplayInListing(false)
|
.DisplayInListing(false)
|
||||||
@@ -64,10 +66,10 @@ internal class HopAdminContext : AdminPagesContext {
|
|||||||
generator.Page<PermissionGroup>()
|
generator.Page<PermissionGroup>()
|
||||||
.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.")
|
||||||
.ConfigureProvider<GroupProvider>()
|
.ConfigureProvider<GroupProvider>()
|
||||||
.ViewPermission(AdminPermissions.ViewGroups)
|
.ReadPermission(options.Value.Groups.Read)
|
||||||
.CreatePermission(AdminPermissions.AddGroup)
|
.CreatePermission(options.Value.Groups.Create)
|
||||||
.UpdatePermission(AdminPermissions.EditGroup)
|
.UpdatePermission(options.Value.Groups.Update)
|
||||||
.DeletePermission(AdminPermissions.DeleteGroup)
|
.DeletePermission(options.Value.Groups.Delete)
|
||||||
.ListingProperty(g => g.Name);
|
.ListingProperty(g => g.Name);
|
||||||
|
|
||||||
generator.Page<PermissionGroup>().Property(g => g.Name)
|
generator.Page<PermissionGroup>().Property(g => g.Name)
|
||||||
|
|||||||
@@ -5,25 +5,26 @@
|
|||||||
@using BlazorStrap
|
@using BlazorStrap
|
||||||
@using HopFrame.Web.Pages.Administration.Layout
|
@using HopFrame.Web.Pages.Administration.Layout
|
||||||
@using BlazorStrap.V5
|
@using BlazorStrap.V5
|
||||||
@using HopFrame.Security
|
@using HopFrame.Security.Authorization
|
||||||
@using HopFrame.Web.Admin.Providers
|
@using HopFrame.Web.Admin.Providers
|
||||||
@using HopFrame.Web.Components
|
@using HopFrame.Web.Components
|
||||||
@using Microsoft.AspNetCore.Components.Web
|
@using Microsoft.AspNetCore.Components.Web
|
||||||
|
@using Microsoft.Extensions.Options
|
||||||
@layout AdminLayout
|
@layout AdminLayout
|
||||||
|
|
||||||
<AuthorizedView Permission="@AdminPermissions.IsAdmin" RedirectIfUnauthorized="/administration/login" />
|
<AuthorizedView Permission="@Options.Value.Dashboard" RedirectIfUnauthorized="/administration/login" />
|
||||||
|
|
||||||
<PageTitle>Admin Dashboard</PageTitle>
|
<PageTitle>Admin Dashboard</PageTitle>
|
||||||
|
|
||||||
<BSContainer>
|
<BSContainer>
|
||||||
<BSRow Justify="Justify.Center">
|
<BSRow Justify="Justify.Center">
|
||||||
@foreach (var adminPage in Pages.LoadRegisteredAdminPages()) {
|
@foreach (var adminPage in Pages.LoadRegisteredAdminPages()) {
|
||||||
<AuthorizedView Permission="@adminPage.Permissions.View">
|
<AuthorizedView Permission="@adminPage.Permissions.Read">
|
||||||
<BSCol Column="4" style="margin-bottom: 10px">
|
<BSCol Column="4" style="margin-bottom: 10px">
|
||||||
<BSCard CardType="CardType.Card" Color="BSColor.Dark" style="min-height: 200px; min-width: 200px">
|
<BSCard CardType="CardType.Card" Color="BSColor.Dark" style="min-height: 200px; min-width: 200px">
|
||||||
<BSCard CardType="CardType.Body" style="display: flex; flex-direction: column">
|
<BSCard CardType="CardType.Body" style="display: flex; flex-direction: column">
|
||||||
<BSCard CardType="CardType.Title">@adminPage.Title</BSCard>
|
<BSCard CardType="CardType.Title">@adminPage.Title</BSCard>
|
||||||
<BSCard CardType="CardType.Subtitle"><span style="color: gray">@adminPage.Permissions.View</span></BSCard>
|
<BSCard CardType="CardType.Subtitle"><span style="color: gray">@adminPage.Permissions.Read</span></BSCard>
|
||||||
<BSCard CardType="CardType.Text">@adminPage.Description</BSCard>
|
<BSCard CardType="CardType.Text">@adminPage.Description</BSCard>
|
||||||
<BSButton IsOutlined="true" MarginTop="Margins.Auto" style="width: max-content; align-self: center" OnClick="() => NavigateTo(adminPage.Url)" Color="BSColor.Light">Open</BSButton>
|
<BSButton IsOutlined="true" MarginTop="Margins.Auto" style="width: max-content; align-self: center" OnClick="() => NavigateTo(adminPage.Url)" Color="BSColor.Light">Open</BSButton>
|
||||||
</BSCard>
|
</BSCard>
|
||||||
@@ -36,6 +37,7 @@
|
|||||||
|
|
||||||
@inject NavigationManager Navigator
|
@inject NavigationManager Navigator
|
||||||
@inject IAdminPagesProvider Pages
|
@inject IAdminPagesProvider Pages
|
||||||
|
@inject IOptions<AdminPermissionOptions> Options
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
@using HopFrame.Web.Components
|
@using HopFrame.Web.Components
|
||||||
|
|
||||||
<PageTitle>@_pageData.Title</PageTitle>
|
<PageTitle>@_pageData.Title</PageTitle>
|
||||||
<AuthorizedView Permission="@_pageData.Permissions.View" RedirectIfUnauthorized="@GenerateRedirectString()" />
|
<AuthorizedView Permission="@_pageData.Permissions.Read" RedirectIfUnauthorized="@GenerateRedirectString()" />
|
||||||
|
|
||||||
<AdminPageModal ReloadDelegate="Reload" @ref="_modal"/>
|
<AdminPageModal ReloadDelegate="Reload" @ref="_modal"/>
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
<BSNavItem IsActive="IsDashboardActive()" OnClick="NavigateToDashboard">Dashboard</BSNavItem>
|
<BSNavItem IsActive="IsDashboardActive()" OnClick="NavigateToDashboard">Dashboard</BSNavItem>
|
||||||
|
|
||||||
@foreach (var adminPage in Pages.LoadRegisteredAdminPages()) {
|
@foreach (var adminPage in Pages.LoadRegisteredAdminPages()) {
|
||||||
<AuthorizedView Permission="@adminPage.Permissions.View">
|
<AuthorizedView Permission="@adminPage.Permissions.Read">
|
||||||
<BSNavItem IsActive="IsNavItemActive(adminPage.Url)" OnClick="() => Navigate(adminPage.Url)">@adminPage.Title</BSNavItem>
|
<BSNavItem IsActive="IsNavItemActive(adminPage.Url)" OnClick="() => Navigate(adminPage.Url)">@adminPage.Title</BSNavItem>
|
||||||
</AuthorizedView>
|
</AuthorizedView>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,13 @@ using HopFrame.Web.Admin;
|
|||||||
using HopFrame.Web.Services;
|
using HopFrame.Web.Services;
|
||||||
using HopFrame.Web.Services.Implementation;
|
using HopFrame.Web.Services.Implementation;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace HopFrame.Web;
|
namespace HopFrame.Web;
|
||||||
|
|
||||||
public static class ServiceCollectionExtensions {
|
public static class ServiceCollectionExtensions {
|
||||||
public static IServiceCollection AddHopFrame<TDbContext>(this IServiceCollection services) where TDbContext : HopDbContextBase {
|
public static IServiceCollection AddHopFrame<TDbContext>(this IServiceCollection services, ConfigurationManager configuration) where TDbContext : HopDbContextBase {
|
||||||
services.AddHttpClient();
|
services.AddHttpClient();
|
||||||
services.AddHopFrameRepositories<TDbContext>();
|
services.AddHopFrameRepositories<TDbContext>();
|
||||||
services.AddScoped<IAuthService, AuthService>();
|
services.AddScoped<IAuthService, AuthService>();
|
||||||
@@ -22,7 +23,7 @@ public static class ServiceCollectionExtensions {
|
|||||||
services.AddSweetAlert2();
|
services.AddSweetAlert2();
|
||||||
services.AddBlazorStrap();
|
services.AddBlazorStrap();
|
||||||
|
|
||||||
services.AddHopFrameAuthentication();
|
services.AddHopFrameAuthentication(configuration);
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using HopFrame.Security.Authentication;
|
|||||||
using HopFrame.Security.Claims;
|
using HopFrame.Security.Claims;
|
||||||
using HopFrame.Security.Models;
|
using HopFrame.Security.Models;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace HopFrame.Web.Services.Implementation;
|
namespace HopFrame.Web.Services.Implementation;
|
||||||
|
|
||||||
@@ -11,7 +12,8 @@ internal class AuthService(
|
|||||||
IUserRepository userService,
|
IUserRepository userService,
|
||||||
IHttpContextAccessor httpAccessor,
|
IHttpContextAccessor httpAccessor,
|
||||||
ITokenRepository tokens,
|
ITokenRepository tokens,
|
||||||
ITokenContext context)
|
ITokenContext context,
|
||||||
|
IOptions<HopFrameAuthenticationOptions> options)
|
||||||
: IAuthService {
|
: IAuthService {
|
||||||
|
|
||||||
public async Task Register(UserRegister register) {
|
public async Task Register(UserRegister register) {
|
||||||
@@ -27,12 +29,12 @@ internal class AuthService(
|
|||||||
var accessToken = await tokens.CreateToken(Token.AccessTokenType, user);
|
var accessToken = await tokens.CreateToken(Token.AccessTokenType, user);
|
||||||
|
|
||||||
httpAccessor.HttpContext?.Response.Cookies.Append(ITokenContext.RefreshTokenType, refreshToken.Content.ToString(), new CookieOptions {
|
httpAccessor.HttpContext?.Response.Cookies.Append(ITokenContext.RefreshTokenType, refreshToken.Content.ToString(), new CookieOptions {
|
||||||
MaxAge = HopFrameAuthentication.RefreshTokenTime,
|
MaxAge = options.Value.RefreshTokenTime,
|
||||||
HttpOnly = true,
|
HttpOnly = true,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
httpAccessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Content.ToString(), new CookieOptions {
|
httpAccessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Content.ToString(), new CookieOptions {
|
||||||
MaxAge = HopFrameAuthentication.AccessTokenTime,
|
MaxAge = options.Value.AccessTokenTime,
|
||||||
HttpOnly = false,
|
HttpOnly = false,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
@@ -48,12 +50,12 @@ internal class AuthService(
|
|||||||
var accessToken = await tokens.CreateToken(Token.AccessTokenType, user);
|
var accessToken = await tokens.CreateToken(Token.AccessTokenType, user);
|
||||||
|
|
||||||
httpAccessor.HttpContext?.Response.Cookies.Append(ITokenContext.RefreshTokenType, refreshToken.Content.ToString(), new CookieOptions {
|
httpAccessor.HttpContext?.Response.Cookies.Append(ITokenContext.RefreshTokenType, refreshToken.Content.ToString(), new CookieOptions {
|
||||||
MaxAge = HopFrameAuthentication.RefreshTokenTime,
|
MaxAge = options.Value.RefreshTokenTime,
|
||||||
HttpOnly = true,
|
HttpOnly = true,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
httpAccessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Content.ToString(), new CookieOptions {
|
httpAccessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Content.ToString(), new CookieOptions {
|
||||||
MaxAge = HopFrameAuthentication.AccessTokenTime,
|
MaxAge = options.Value.AccessTokenTime,
|
||||||
HttpOnly = false,
|
HttpOnly = false,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
@@ -77,12 +79,12 @@ internal class AuthService(
|
|||||||
|
|
||||||
if (token is null || token.Type != Token.RefreshTokenType) return null;
|
if (token is null || token.Type != Token.RefreshTokenType) return null;
|
||||||
|
|
||||||
if (token.CreatedAt + HopFrameAuthentication.RefreshTokenTime < DateTime.Now) return null;
|
if (token.CreatedAt + options.Value.RefreshTokenTime < DateTime.Now) return null;
|
||||||
|
|
||||||
var accessToken = await tokens.CreateToken(Token.AccessTokenType, token.Owner);
|
var accessToken = await tokens.CreateToken(Token.AccessTokenType, token.Owner);
|
||||||
|
|
||||||
httpAccessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Content.ToString(), new CookieOptions {
|
httpAccessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Content.ToString(), new CookieOptions {
|
||||||
MaxAge = HopFrameAuthentication.AccessTokenTime,
|
MaxAge = options.Value.AccessTokenTime,
|
||||||
HttpOnly = false,
|
HttpOnly = false,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
@@ -95,7 +97,7 @@ internal class AuthService(
|
|||||||
|
|
||||||
if (accessToken is null) return false;
|
if (accessToken is null) return false;
|
||||||
if (accessToken.Type != Token.AccessTokenType) return false;
|
if (accessToken.Type != Token.AccessTokenType) return false;
|
||||||
if (accessToken.CreatedAt + HopFrameAuthentication.AccessTokenTime < DateTime.Now) return false;
|
if (accessToken.CreatedAt + options.Value.AccessTokenTime < DateTime.Now) return false;
|
||||||
if (accessToken.Owner is null) return false;
|
if (accessToken.Owner is null) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ var builder = WebApplication.CreateBuilder(args);
|
|||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
|
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
builder.Services.AddHopFrame<DatabaseContext>();
|
builder.Services.AddHopFrame<DatabaseContext>(builder.Configuration);
|
||||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
builder.Services.AddSwaggerGen();
|
builder.Services.AddSwaggerGen();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ using HopFrame.Web.Admin;
|
|||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
builder.Services.AddDbContext<DatabaseContext>();
|
builder.Services.AddDbContext<DatabaseContext>();
|
||||||
builder.Services.AddHopFrame<DatabaseContext>();
|
builder.Services.AddHopFrame<DatabaseContext>(builder.Configuration);
|
||||||
builder.Services.AddAdminContext<AdminContext>();
|
builder.Services.AddAdminContext<AdminContext>();
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using HopFrame.Security.Authentication;
|
|||||||
using HopFrame.Security.Claims;
|
using HopFrame.Security.Claims;
|
||||||
using HopFrame.Security.Models;
|
using HopFrame.Security.Models;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|
||||||
namespace HopFrame.Tests.Api;
|
namespace HopFrame.Tests.Api;
|
||||||
@@ -75,7 +76,7 @@ public class AuthLogicTests {
|
|||||||
.Setup(c => c.User)
|
.Setup(c => c.User)
|
||||||
.Returns(CreateDummyUser());
|
.Returns(CreateDummyUser());
|
||||||
|
|
||||||
return (new AuthLogic(users.Object, tokens.Object, context.Object, accessor), accessor.HttpContext);
|
return (new AuthLogic(users.Object, tokens.Object, context.Object, accessor, new OptionsWrapper<HopFrameAuthenticationOptions>(new HopFrameAuthenticationOptions())), accessor.HttpContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
private User CreateDummyUser() => new() {
|
private User CreateDummyUser() => new() {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public class AuthenticationTests {
|
|||||||
.Setup(x => x.GetFullPermissions(It.IsAny<User>()))
|
.Setup(x => x.GetFullPermissions(It.IsAny<User>()))
|
||||||
.ReturnsAsync(new List<string>());
|
.ReturnsAsync(new List<string>());
|
||||||
|
|
||||||
var auth = new HopFrameAuthentication(options.Object, logger.Object, encoder.Object, clock.Object, tokens.Object, perms.Object);
|
var auth = new HopFrameAuthentication(options.Object, logger.Object, encoder.Object, clock.Object, tokens.Object, perms.Object, new OptionsWrapper<HopFrameAuthenticationOptions>(new HopFrameAuthenticationOptions()));
|
||||||
var context = new DefaultHttpContext();
|
var context = new DefaultHttpContext();
|
||||||
if (provideCorrectToken)
|
if (provideCorrectToken)
|
||||||
context.HttpContext.Request.Headers.Append(HopFrameAuthentication.SchemeName, correctToken.Content.ToString());
|
context.HttpContext.Request.Headers.Append(HopFrameAuthentication.SchemeName, correctToken.Content.ToString());
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
using HopFrame.Database.Models;
|
using HopFrame.Database.Models;
|
||||||
using HopFrame.Database.Repositories;
|
using HopFrame.Database.Repositories;
|
||||||
|
using HopFrame.Security.Authentication;
|
||||||
using HopFrame.Security.Claims;
|
using HopFrame.Security.Claims;
|
||||||
using HopFrame.Security.Models;
|
using HopFrame.Security.Models;
|
||||||
using HopFrame.Tests.Web.Extensions;
|
using HopFrame.Tests.Web.Extensions;
|
||||||
using HopFrame.Web.Services;
|
using HopFrame.Web.Services;
|
||||||
using HopFrame.Web.Services.Implementation;
|
using HopFrame.Web.Services.Implementation;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|
||||||
namespace HopFrame.Tests.Web;
|
namespace HopFrame.Tests.Web;
|
||||||
@@ -66,7 +68,7 @@ public class AuthServiceTests {
|
|||||||
.Setup(c => c.AccessToken)
|
.Setup(c => c.AccessToken)
|
||||||
.Returns(providedAccessToken);
|
.Returns(providedAccessToken);
|
||||||
|
|
||||||
return (new AuthService(users.Object, accessor, tokens.Object, context.Object), accessor.HttpContext);
|
return (new AuthService(users.Object, accessor, tokens.Object, context.Object, new OptionsWrapper<HopFrameAuthenticationOptions>(new HopFrameAuthenticationOptions())), accessor.HttpContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
private User CreateDummyUser() => new() {
|
private User CreateDummyUser() => new() {
|
||||||
|
|||||||
Reference in New Issue
Block a user