diff --git a/.idea/.idea.HopFrame/.idea/discord.xml b/.idea/.idea.HopFrame/.idea/discord.xml
new file mode 100644
index 0000000..912db82
--- /dev/null
+++ b/.idea/.idea.HopFrame/.idea/discord.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/HopFrame.sln.DotSettings.user b/HopFrame.sln.DotSettings.user
index 86d9f80..eab4e1d 100644
--- a/HopFrame.sln.DotSettings.user
+++ b/HopFrame.sln.DotSettings.user
@@ -70,6 +70,7 @@
+
\ No newline at end of file
diff --git a/src/HopFrame.Database/Repositories/ITokenRepository.cs b/src/HopFrame.Database/Repositories/ITokenRepository.cs
index 5f66769..2c1192c 100644
--- a/src/HopFrame.Database/Repositories/ITokenRepository.cs
+++ b/src/HopFrame.Database/Repositories/ITokenRepository.cs
@@ -6,5 +6,6 @@ public interface ITokenRepository {
Task GetToken(string content);
Task CreateToken(int type, User owner);
Task DeleteUserTokens(User owner);
+ Task DeleteToken(Token token);
Task CreateApiToken(User owner, DateTime expirationDate);
}
\ No newline at end of file
diff --git a/src/HopFrame.Database/Repositories/Implementation/PermissionRepository.cs b/src/HopFrame.Database/Repositories/Implementation/PermissionRepository.cs
index f80b0b8..6d55bc0 100644
--- a/src/HopFrame.Database/Repositories/Implementation/PermissionRepository.cs
+++ b/src/HopFrame.Database/Repositories/Implementation/PermissionRepository.cs
@@ -69,6 +69,10 @@ internal sealed class PermissionRepository(TDbContext context, IGrou
public async Task> GetFullPermissions(IPermissionOwner owner) {
var permissions = new List();
+
+ if (owner is Token token && token.Type != Token.ApiTokenType) {
+ owner = token.Owner;
+ }
if (owner is User user) {
var perms = await context.Permissions
@@ -86,11 +90,11 @@ internal sealed class PermissionRepository(TDbContext context, IGrou
.ToListAsync();
permissions.AddRange(perms.Select(p => p.PermissionName));
- }else if (owner is Token token) {
+ }else if (owner is Token apiToken) {
var perms = await context.Permissions
.Include(p => p.Token)
.Where(p => p.Token != null)
- .Where(p =>p.Token.TokenId == token.TokenId)
+ .Where(p =>p.Token.TokenId == apiToken.TokenId)
.ToListAsync();
permissions.AddRange(perms.Select(p => p.PermissionName));
diff --git a/src/HopFrame.Database/Repositories/Implementation/TokenRepository.cs b/src/HopFrame.Database/Repositories/Implementation/TokenRepository.cs
index 927d080..b44dc43 100644
--- a/src/HopFrame.Database/Repositories/Implementation/TokenRepository.cs
+++ b/src/HopFrame.Database/Repositories/Implementation/TokenRepository.cs
@@ -39,6 +39,11 @@ internal sealed class TokenRepository(TDbContext context) : ITokenRe
await context.SaveChangesAsync();
}
+ public async Task DeleteToken(Token token) {
+ context.Tokens.Remove(token);
+ await context.SaveChangesAsync();
+ }
+
public async Task CreateApiToken(User owner, DateTime expirationDate) {
var token = new Token {
CreatedAt = expirationDate,
diff --git a/src/HopFrame.Security/Authentication/HopFrameAuthentication.cs b/src/HopFrame.Security/Authentication/HopFrameAuthentication.cs
index 9f9af47..88a95c1 100644
--- a/src/HopFrame.Security/Authentication/HopFrameAuthentication.cs
+++ b/src/HopFrame.Security/Authentication/HopFrameAuthentication.cs
@@ -47,15 +47,7 @@ public class HopFrameAuthentication(
new(HopFrameClaimTypes.UserId, tokenEntry.Owner.Id.ToString())
};
- IList permissions;
-
- if (tokenEntry.Type == Token.ApiTokenType) {
- permissions = await perms.GetFullPermissions(tokenEntry);
- }
- else {
- permissions = await perms.GetFullPermissions(tokenEntry.Owner);
- }
-
+ var permissions = await perms.GetFullPermissions(tokenEntry);
claims.AddRange(permissions.Select(perm => new Claim(HopFrameClaimTypes.Permission, perm)));
var principal = new ClaimsPrincipal();
diff --git a/src/HopFrame.Security/Claims/ITokenContext.cs b/src/HopFrame.Security/Claims/ITokenContext.cs
index 6b5a590..6b052bc 100644
--- a/src/HopFrame.Security/Claims/ITokenContext.cs
+++ b/src/HopFrame.Security/Claims/ITokenContext.cs
@@ -21,4 +21,6 @@ public interface ITokenContext {
/// The access token the user provided
///
Token AccessToken { get; }
+
+ IList ContextualPermissions { get; }
}
\ No newline at end of file
diff --git a/src/HopFrame.Security/Claims/TokenContextImplementor.cs b/src/HopFrame.Security/Claims/TokenContextImplementor.cs
index dd50a08..47fce76 100644
--- a/src/HopFrame.Security/Claims/TokenContextImplementor.cs
+++ b/src/HopFrame.Security/Claims/TokenContextImplementor.cs
@@ -4,10 +4,12 @@ using Microsoft.AspNetCore.Http;
namespace HopFrame.Security.Claims;
-internal sealed class TokenContextImplementor(IHttpContextAccessor accessor, IUserRepository users, ITokenRepository tokens) : ITokenContext {
+internal sealed class TokenContextImplementor(IHttpContextAccessor accessor, IUserRepository users, ITokenRepository tokens, IPermissionRepository permissions) : ITokenContext {
public bool IsAuthenticated => !string.IsNullOrEmpty(accessor.HttpContext?.User.GetAccessTokenId());
public User User => users.GetUser(Guid.Parse(accessor.HttpContext?.User.GetUserId() ?? Guid.Empty.ToString())).GetAwaiter().GetResult();
public Token AccessToken => tokens.GetToken(accessor.HttpContext?.User.GetAccessTokenId()).GetAwaiter().GetResult();
+
+ public IList ContextualPermissions => permissions.GetFullPermissions(AccessToken).GetAwaiter().GetResult();
}
\ No newline at end of file
diff --git a/src/HopFrame.Web/AuthMiddleware.cs b/src/HopFrame.Web/AuthMiddleware.cs
index ac5c954..b5fbc93 100644
--- a/src/HopFrame.Web/AuthMiddleware.cs
+++ b/src/HopFrame.Web/AuthMiddleware.cs
@@ -26,7 +26,7 @@ public sealed class AuthMiddleware(IAuthService auth, IPermissionRepository perm
new(HopFrameClaimTypes.UserId, token.Owner.Id.ToString())
};
- var permissions = await perms.GetFullPermissions(token.Owner);
+ var permissions = await perms.GetFullPermissions(token);
claims.AddRange(permissions.Select(perm => new Claim(HopFrameClaimTypes.Permission, perm)));
context.User.AddIdentity(new ClaimsIdentity(claims, HopFrameAuthentication.SchemeName));
diff --git a/src/HopFrame.Web/Components/Administration/AdminPageModal.razor b/src/HopFrame.Web/Components/Administration/AdminPageModal.razor
index 4876323..4e212b5 100644
--- a/src/HopFrame.Web/Components/Administration/AdminPageModal.razor
+++ b/src/HopFrame.Web/Components/Administration/AdminPageModal.razor
@@ -321,7 +321,7 @@
private async void Save() {
if (_isEdit && _currentPage.Permissions.Update is not null) {
- if (!await Permissions.HasPermission(Auth.User, _currentPage.Permissions.Update)) {
+ if (!await Permissions.HasPermission(Auth.AccessToken, _currentPage.Permissions.Update)) {
await Alerts.FireAsync(new SweetAlertOptions {
Title = "Unauthorized!",
Text = "You don't have the required permissions to edit an entry!",
@@ -330,7 +330,7 @@
return;
}
}else if (_currentPage.Permissions.Create is not null) {
- if (!await Permissions.HasPermission(Auth.User, _currentPage.Permissions.Create)) {
+ if (!await Permissions.HasPermission(Auth.AccessToken, _currentPage.Permissions.Create)) {
await Alerts.FireAsync(new SweetAlertOptions {
Title = "Unauthorized!",
Text = "You don't have the required permissions to add an entry!",
diff --git a/src/HopFrame.Web/Pages/Administration/AdminPageList.razor b/src/HopFrame.Web/Pages/Administration/AdminPageList.razor
index d796aeb..1086918 100644
--- a/src/HopFrame.Web/Pages/Administration/AdminPageList.razor
+++ b/src/HopFrame.Web/Pages/Administration/AdminPageList.razor
@@ -140,8 +140,8 @@
throw new ArgumentException($"AdminPage '{_pageData.Title}' does not specify a model repository!'");
_modelProvider = _pageData.LoadModelProvider(Provider);
- _hasEditPermission = _pageData.Permissions.Update is null || await Permissions.HasPermission(Auth.User, _pageData.Permissions.Update);
- _hasDeletePermission = _pageData.Permissions.Delete is null || await Permissions.HasPermission(Auth.User, _pageData.Permissions.Delete);
+ _hasEditPermission = _pageData.Permissions.Update is null || await Permissions.HasPermission(Auth.AccessToken, _pageData.Permissions.Update);
+ _hasDeletePermission = _pageData.Permissions.Delete is null || await Permissions.HasPermission(Auth.AccessToken, _pageData.Permissions.Delete);
await Reload();
}
diff --git a/testing/HopFrame.Testing.Api/Controllers/TestController.cs b/testing/HopFrame.Testing.Api/Controllers/TestController.cs
index fb39666..d097592 100644
--- a/testing/HopFrame.Testing.Api/Controllers/TestController.cs
+++ b/testing/HopFrame.Testing.Api/Controllers/TestController.cs
@@ -1,5 +1,7 @@
using HopFrame.Api.Logic;
+using HopFrame.Api.Models;
using HopFrame.Database.Models;
+using HopFrame.Database.Repositories;
using HopFrame.Security.Authorization;
using HopFrame.Security.Claims;
using HopFrame.Testing.Api.Models;
@@ -10,11 +12,11 @@ namespace HopFrame.Testing.Api.Controllers;
[ApiController]
[Route("test")]
-public class TestController(ITokenContext userContext, DatabaseContext context) : ControllerBase {
+public class TestController(ITokenContext userContext, DatabaseContext context, ITokenRepository tokens, IPermissionRepository permissions) : ControllerBase {
[HttpGet("permissions"), Authorized]
- public ActionResult> Permissions() {
- return new ActionResult>(userContext.User.Permissions);
+ public ActionResult> Permissions() {
+ return new ActionResult>(userContext.ContextualPermissions);
}
[HttpGet("generate")]
@@ -50,5 +52,19 @@ public class TestController(ITokenContext userContext, DatabaseContext context)
public async Task>> GetAddresses() {
return LogicResult>.Ok(await context.Addresses.Include(e => e.Employee).ToListAsync());
}
+
+ [HttpGet("token"), Authorized]
+ public async Task>> GetApiToken() {
+ var token = await tokens.CreateApiToken(userContext.User, DateTime.MaxValue);
+ await permissions.AddPermission(token, "hopframe.admin");
+ await permissions.AddPermission(token, "hopframe.admin.users.read");
+ return LogicResult>.Ok(token.TokenId.ToString());
+ }
+
+ [HttpDelete("token/{tokenId}")]
+ public async Task DeleteToken(string tokenId) {
+ var token = await tokens.GetToken(tokenId);
+ await tokens.DeleteToken(token);
+ }
}
\ No newline at end of file
diff --git a/testing/HopFrame.Testing.Api/Program.cs b/testing/HopFrame.Testing.Api/Program.cs
index b728eb3..948be0d 100644
--- a/testing/HopFrame.Testing.Api/Program.cs
+++ b/testing/HopFrame.Testing.Api/Program.cs
@@ -18,7 +18,7 @@ builder.Services.AddSwaggerGen(c => {
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme {
Description = @"JWT Authorization header using the Bearer scheme. \r\n\r\n
Enter 'Bearer' [space] and then your token in the text input below.",
- Name = "Authorization",
+ Name = "Token",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer"