Release/v2.1.0 #44
14
.idea/.idea.HopFrame/.idea/discord.xml
generated
Normal file
14
.idea/.idea.HopFrame/.idea/discord.xml
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DiscordProjectSettings">
|
||||
<option name="show" value="PROJECT_FILES" />
|
||||
<option name="description" value="" />
|
||||
<option name="applicationTheme" value="default" />
|
||||
<option name="iconsTheme" value="default" />
|
||||
<option name="button1Title" value="" />
|
||||
<option name="button1Url" value="" />
|
||||
<option name="button2Title" value="" />
|
||||
<option name="button2Url" value="" />
|
||||
<option name="customApplicationId" value="" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -70,6 +70,7 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</wpf:ResourceDictionary>
|
||||
@@ -6,5 +6,6 @@ public interface ITokenRepository {
|
||||
Task<Token> GetToken(string content);
|
||||
Task<Token> CreateToken(int type, User owner);
|
||||
Task DeleteUserTokens(User owner);
|
||||
Task DeleteToken(Token token);
|
||||
Task<Token> CreateApiToken(User owner, DateTime expirationDate);
|
||||
}
|
||||
@@ -70,6 +70,10 @@ internal sealed class PermissionRepository<TDbContext>(TDbContext context, IGrou
|
||||
public async Task<IList<string>> GetFullPermissions(IPermissionOwner owner) {
|
||||
var permissions = new List<string>();
|
||||
|
||||
if (owner is Token token && token.Type != Token.ApiTokenType) {
|
||||
owner = token.Owner;
|
||||
}
|
||||
|
||||
if (owner is User user) {
|
||||
var perms = await context.Permissions
|
||||
.Include(p => p.User)
|
||||
@@ -86,11 +90,11 @@ internal sealed class PermissionRepository<TDbContext>(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));
|
||||
|
||||
@@ -39,6 +39,11 @@ internal sealed class TokenRepository<TDbContext>(TDbContext context) : ITokenRe
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteToken(Token token) {
|
||||
context.Tokens.Remove(token);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<Token> CreateApiToken(User owner, DateTime expirationDate) {
|
||||
var token = new Token {
|
||||
CreatedAt = expirationDate,
|
||||
|
||||
@@ -47,15 +47,7 @@ public class HopFrameAuthentication(
|
||||
new(HopFrameClaimTypes.UserId, tokenEntry.Owner.Id.ToString())
|
||||
};
|
||||
|
||||
IList<string> 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();
|
||||
|
||||
@@ -21,4 +21,6 @@ public interface ITokenContext {
|
||||
/// The access token the user provided
|
||||
/// </summary>
|
||||
Token AccessToken { get; }
|
||||
|
||||
IList<string> ContextualPermissions { get; }
|
||||
}
|
||||
@@ -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<string> ContextualPermissions => permissions.GetFullPermissions(AccessToken).GetAwaiter().GetResult();
|
||||
}
|
||||
@@ -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));
|
||||
|
||||
@@ -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!",
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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<IList<Permission>> Permissions() {
|
||||
return new ActionResult<IList<Permission>>(userContext.User.Permissions);
|
||||
public ActionResult<IList<string>> Permissions() {
|
||||
return new ActionResult<IList<string>>(userContext.ContextualPermissions);
|
||||
}
|
||||
|
||||
[HttpGet("generate")]
|
||||
@@ -51,4 +53,18 @@ public class TestController(ITokenContext userContext, DatabaseContext context)
|
||||
return LogicResult<IList<Address>>.Ok(await context.Addresses.Include(e => e.Employee).ToListAsync());
|
||||
}
|
||||
|
||||
[HttpGet("token"), Authorized]
|
||||
public async Task<ActionResult<SingleValueResult<string>>> 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<SingleValueResult<string>>.Ok(token.TokenId.ToString());
|
||||
}
|
||||
|
||||
[HttpDelete("token/{tokenId}")]
|
||||
public async Task DeleteToken(string tokenId) {
|
||||
var token = await tokens.GetToken(tokenId);
|
||||
await tokens.DeleteToken(token);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user