secured api tokens against permission breaches

This commit is contained in:
2024-12-21 17:35:11 +01:00
parent e47d4917df
commit c6aca4baf6
4 changed files with 15 additions and 4 deletions

View File

@@ -62,8 +62,8 @@ tokens.CreateApiToken(user, DateTime.MaxValue);
This creates a new api token that is valid until the provided DateTime has passed. Note that in the database and the token
model the `CreatedAt` property represents the expiration date on an api token. For security reasons the api token by default
has no permissions. This allows you to create tokens that are just permitted to perform a single action. Note that a token
associated to a user can also have more permissions than the user itself so make sure to properly secure the creation process.
has no permissions. This allows you to create tokens that are just permitted to perform a single action. Note that an api token
can **never** have more permissions than the user associated with it.
### Add permissions to an api token

View File

@@ -5,7 +5,7 @@ namespace HopFrame.Database.Repositories;
public interface ITokenRepository {
Task<Token> GetToken(string content);
Task<Token> CreateToken(int type, User owner);
Task DeleteUserTokens(User owner);
Task DeleteUserTokens(User owner, bool includeApiTokens = false);
Task DeleteToken(Token token);
Task<Token> CreateApiToken(User owner, DateTime expirationDate);
}

View File

@@ -5,6 +5,10 @@ namespace HopFrame.Database.Repositories.Implementation;
internal sealed class PermissionRepository<TDbContext>(TDbContext context, IGroupRepository groupRepository) : IPermissionRepository where TDbContext : HopDbContextBase {
public async Task<bool> HasPermission(IPermissionOwner owner, params string[] permissions) {
if (owner is Token { Type: Token.ApiTokenType } token) {
if (!await HasPermission(token.Owner, permissions)) return false;
}
var perms = (await GetFullPermissions(owner)).ToArray();
foreach (var permission in permissions) {
@@ -27,6 +31,8 @@ internal sealed class PermissionRepository<TDbContext>(TDbContext context, IGrou
}else if (owner is Token token) {
if (token.Type != Token.ApiTokenType)
throw new ArgumentException("Only API tokens can have permissions!");
if (!await HasPermission(token.Owner, permission))
throw new ArgumentException("An api token cannot have more permissions than the owner has!");
entry.Token = token;
}

View File

@@ -29,12 +29,17 @@ internal sealed class TokenRepository<TDbContext>(TDbContext context) : ITokenRe
return token;
}
public async Task DeleteUserTokens(User owner) {
public async Task DeleteUserTokens(User owner, bool includeApiTokens = false) {
var tokens = await context.Tokens
.Include(t => t.Owner)
.Where(t => t.Owner.Id == owner.Id)
.ToListAsync();
if (!includeApiTokens)
tokens = tokens
.Where(t => t.Type != Token.ApiTokenType)
.ToList();
context.Tokens.RemoveRange(tokens);
await context.SaveChangesAsync();
}