diff --git a/.idea/.idea.HopFrame/.idea/dataSources.xml b/.idea/.idea.HopFrame/.idea/dataSources.xml
index f9bef4b..81347d0 100644
--- a/.idea/.idea.HopFrame/.idea/dataSources.xml
+++ b/.idea/.idea.HopFrame/.idea/dataSources.xml
@@ -5,7 +5,7 @@
sqlite.xerial
true
org.sqlite.JDBC
- jdbc:sqlite:C:\Users\Remote\Documents\Projekte\HopFrame\DatabaseTest\test.db
+ jdbc:sqlite:$PROJECT_DIR$/DatabaseTest/bin/Debug/net8.0/test.db
diff --git a/DatabaseTest/Controllers/TestController.cs b/DatabaseTest/Controllers/TestController.cs
index 1b78deb..4e74da0 100644
--- a/DatabaseTest/Controllers/TestController.cs
+++ b/DatabaseTest/Controllers/TestController.cs
@@ -1,3 +1,4 @@
+using HopFrame.Database.Models;
using HopFrame.Security.Authorization;
using HopFrame.Security.Claims;
using Microsoft.AspNetCore.Mvc;
@@ -9,8 +10,8 @@ namespace DatabaseTest.Controllers;
public class TestController(ITokenContext userContext) : ControllerBase {
[HttpGet("permissions"), Authorized]
- public ActionResult> Permissions() {
- return new ActionResult>(userContext.User.Permissions);
+ public ActionResult> Permissions() {
+ return new ActionResult>(userContext.User.Permissions);
}
}
\ No newline at end of file
diff --git a/HopFrame.Api/Controller/SecurityController.cs b/HopFrame.Api/Controller/SecurityController.cs
index f9bcd9d..8405b95 100644
--- a/HopFrame.Api/Controller/SecurityController.cs
+++ b/HopFrame.Api/Controller/SecurityController.cs
@@ -4,9 +4,12 @@ using HopFrame.Api.Logic;
using HopFrame.Api.Models;
using HopFrame.Database;
using HopFrame.Database.Models.Entries;
+using HopFrame.Security;
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;
@@ -15,32 +18,32 @@ namespace HopFrame.Api.Controller;
[ApiController]
[Route("authentication")]
-public class SecurityController(TDbContext context) : ControllerBase where TDbContext : HopDbContextBase {
+public class SecurityController(TDbContext context, IUserService users, ITokenContext tokenContext) : ControllerBase where TDbContext : HopDbContextBase {
private const string RefreshTokenType = "HopFrame.Security.RefreshToken";
[HttpPut("login")]
public async Task>> Login([FromBody] UserLogin login) {
- var user = await context.Users.SingleOrDefaultAsync(user => user.Email == login.Email);
+ var user = await users.GetUserByEmail(login.Email);
if (user is null)
return LogicResult>.NotFound("The provided email address was not found");
var hashedPassword = EncryptionManager.Hash(login.Password, Encoding.Default.GetBytes(user.CreatedAt.ToString(CultureInfo.InvariantCulture)));
- if (hashedPassword != user.Password)
+ if (hashedPassword != await users.GetUserPassword(user))
return LogicResult>.Forbidden("The provided password is not correct");
var refreshToken = new TokenEntry {
CreatedAt = DateTime.Now,
Token = Guid.NewGuid().ToString(),
Type = TokenEntry.RefreshTokenType,
- UserId = user.Id
+ UserId = user.Id.ToString()
};
var accessToken = new TokenEntry {
CreatedAt = DateTime.Now,
Token = Guid.NewGuid().ToString(),
Type = TokenEntry.AccessTokenType,
- UserId = user.Id
+ UserId = user.Id.ToString()
};
HttpContext.Response.Cookies.Append(RefreshTokenType, refreshToken.Token, new CookieOptions {
@@ -59,42 +62,24 @@ public class SecurityController(TDbContext context) : ControllerBase
public async Task>> Register([FromBody] UserRegister register) {
if (register.Password.Length < 8)
return LogicResult>.Conflict("Password needs to be at least 8 characters long");
-
- if (await context.Users.AnyAsync(user => user.Username == register.Username || user.Email == register.Email))
+
+ var allUsers = await users.GetUsers();
+ if (allUsers.Any(user => user.Username == register.Username || user.Email == register.Email))
return LogicResult>.Conflict("Username or Email is already registered");
- var user = new UserEntry {
- CreatedAt = DateTime.Now,
- Email = register.Email,
- Username = register.Username,
- Id = Guid.NewGuid().ToString()
- };
- user.Password = EncryptionManager.Hash(register.Password, Encoding.Default.GetBytes(user.CreatedAt.ToString(CultureInfo.InvariantCulture)));
-
- await context.Users.AddAsync(user);
-
- var defaultGroups = await context.Groups
- .Where(group => group.Default)
- .Select(group => "group." + group.Name)
- .ToListAsync();
-
- await context.Permissions.AddRangeAsync(defaultGroups.Select(group => new PermissionEntry {
- GrantedAt = DateTime.Now,
- PermissionText = group,
- UserId = user.Id
- }));
+ var user = await users.AddUser(register);
var refreshToken = new TokenEntry {
CreatedAt = DateTime.Now,
Token = Guid.NewGuid().ToString(),
Type = TokenEntry.RefreshTokenType,
- UserId = user.Id
+ UserId = user.Id.ToString()
};
var accessToken = new TokenEntry {
CreatedAt = DateTime.Now,
Token = Guid.NewGuid().ToString(),
Type = TokenEntry.AccessTokenType,
- UserId = user.Id
+ UserId = user.Id.ToString()
};
HttpContext.Response.Cookies.Append(RefreshTokenType, refreshToken.Token, new CookieOptions {
@@ -163,26 +148,14 @@ public class SecurityController(TDbContext context) : ControllerBase
}
[HttpDelete("delete"), Authorized]
- public async Task Delete([FromBody] UserLogin login) {
- var token = HttpContext.User.GetAccessTokenId();
- var userId = (await context.Tokens.SingleOrDefaultAsync(t => t.Token == token && t.Type == TokenEntry.AccessTokenType))?.UserId;
-
- if (string.IsNullOrEmpty(userId))
- return LogicResult.NotFound("Access token does not match any user");
-
- var user = await context.Users.SingleAsync(user => user.Id == userId);
+ public async Task Delete([FromBody] UserPasswordValidation validation) {
+ var user = tokenContext.User;
- var password = EncryptionManager.Hash(login.Password, Encoding.Default.GetBytes(user.CreatedAt.ToString(CultureInfo.InvariantCulture)));
- if (user.Password != password)
+ var password = EncryptionManager.Hash(validation.Password, Encoding.Default.GetBytes(user.CreatedAt.ToString(CultureInfo.InvariantCulture)));
+ if (await users.GetUserPassword(user) != password)
return LogicResult.Forbidden("The provided password is not correct");
- var tokens = await context.Tokens.Where(t => t.UserId == userId).ToArrayAsync();
- var permissions = await context.Permissions.Where(perm => perm.UserId == userId).ToArrayAsync();
-
- context.Tokens.RemoveRange(tokens);
- context.Permissions.RemoveRange(permissions);
- context.Users.Remove(user);
- await context.SaveChangesAsync();
+ await users.DeleteUser(user);
HttpContext.Response.Cookies.Delete(RefreshTokenType);
diff --git a/HopFrame.Api/Models/UserPasswordValidation.cs b/HopFrame.Api/Models/UserPasswordValidation.cs
new file mode 100644
index 0000000..1b274cb
--- /dev/null
+++ b/HopFrame.Api/Models/UserPasswordValidation.cs
@@ -0,0 +1,5 @@
+namespace HopFrame.Api.Models;
+
+public class UserPasswordValidation {
+ public string Password { get; set; }
+}
\ No newline at end of file
diff --git a/HopFrame.Database/HopDbContextBase.cs b/HopFrame.Database/HopDbContextBase.cs
index 944ac54..2ae1fe6 100644
--- a/HopFrame.Database/HopDbContextBase.cs
+++ b/HopFrame.Database/HopDbContextBase.cs
@@ -6,11 +6,7 @@ namespace HopFrame.Database;
///
/// This class includes the basic database structure in order for HopFrame to work
///
-public class HopDbContextBase : DbContext {
-
- public HopDbContextBase() {}
-
- public HopDbContextBase(DbContextOptions options) : base(options) {}
+public abstract class HopDbContextBase : DbContext {
public virtual DbSet Users { get; set; }
public virtual DbSet Permissions { get; set; }
@@ -25,4 +21,12 @@ public class HopDbContextBase : DbContext {
modelBuilder.Entity();
modelBuilder.Entity();
}
+
+ ///
+ /// Gets executed when a user is deleted through the IUserService from the
+ /// HopFrame.Security package. You can override this method to also delete
+ /// related user specific entries in the database
+ ///
+ ///
+ public virtual void OnUserDelete(UserEntry user) {}
}
\ No newline at end of file
diff --git a/HopFrame.Database/Models/ModelExtensions.cs b/HopFrame.Database/Models/ModelExtensions.cs
index e632367..4600afd 100644
--- a/HopFrame.Database/Models/ModelExtensions.cs
+++ b/HopFrame.Database/Models/ModelExtensions.cs
@@ -20,10 +20,37 @@ public static class ModelExtensions {
user.Permissions = contextBase.Permissions
.Where(perm => perm.UserId == entry.Id)
- .Select(perm => perm.PermissionText)
+ .Select(perm => perm.ToPermissionModel())
.ToList();
return user;
}
+
+ public static Permission ToPermissionModel(this PermissionEntry entry) {
+ Guid.TryParse(entry.UserId, out var userId);
+
+ return new Permission {
+ Owner = userId,
+ PermissionName = entry.PermissionText,
+ GrantedAt = entry.GrantedAt,
+ Id = entry.RecordId
+ };
+ }
+
+ public static PermissionGroup ToPermissionGroup(this GroupEntry entry, HopDbContextBase contextBase) {
+ var group = new PermissionGroup {
+ Name = entry.Name,
+ IsDefaultGroup = entry.Default,
+ Description = entry.Description,
+ CreatedAt = entry.CreatedAt
+ };
+
+ group.Permissions = contextBase.Permissions
+ .Where(perm => perm.UserId == group.Name)
+ .Select(perm => perm.ToPermissionModel())
+ .ToList();
+
+ return group;
+ }
}
\ No newline at end of file
diff --git a/HopFrame.Database/Models/Permission.cs b/HopFrame.Database/Models/Permission.cs
new file mode 100644
index 0000000..e6fbe14
--- /dev/null
+++ b/HopFrame.Database/Models/Permission.cs
@@ -0,0 +1,10 @@
+namespace HopFrame.Database.Models;
+
+public sealed class Permission {
+ public long Id { get; init; }
+ public string PermissionName { get; set; }
+ public Guid Owner { get; set; }
+ public DateTime GrantedAt { get; set; }
+}
+
+public interface IPermissionOwner {}
diff --git a/HopFrame.Database/Models/PermissionGroup.cs b/HopFrame.Database/Models/PermissionGroup.cs
new file mode 100644
index 0000000..6d151b1
--- /dev/null
+++ b/HopFrame.Database/Models/PermissionGroup.cs
@@ -0,0 +1,9 @@
+namespace HopFrame.Database.Models;
+
+public sealed class PermissionGroup : IPermissionOwner {
+ public string Name { get; init; }
+ public bool IsDefaultGroup { get; set; }
+ public string Description { get; set; }
+ public DateTime CreatedAt { get; set; }
+ public IList Permissions { get; set; }
+}
\ No newline at end of file
diff --git a/HopFrame.Database/Models/User.cs b/HopFrame.Database/Models/User.cs
index cbedd0c..e97d720 100644
--- a/HopFrame.Database/Models/User.cs
+++ b/HopFrame.Database/Models/User.cs
@@ -1,9 +1,9 @@
namespace HopFrame.Database.Models;
-public class User {
- public Guid Id { get; set; }
+public sealed class User : IPermissionOwner {
+ public Guid Id { get; init; }
public string Username { get; set; }
public string Email { get; set; }
public DateTime CreatedAt { get; set; }
- public IList Permissions { get; set; }
+ public IList Permissions { get; set; }
}
\ No newline at end of file
diff --git a/HopFrame.Security/Authentication/HopFrameAuthentication.cs b/HopFrame.Security/Authentication/HopFrameAuthentication.cs
index e0244ac..5fc7e3b 100644
--- a/HopFrame.Security/Authentication/HopFrameAuthentication.cs
+++ b/HopFrame.Security/Authentication/HopFrameAuthentication.cs
@@ -2,6 +2,7 @@ using System.Security.Claims;
using System.Text.Encodings.Web;
using HopFrame.Database;
using HopFrame.Security.Claims;
+using HopFrame.Security.Services;
using Microsoft.AspNetCore.Authentication;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
@@ -17,7 +18,8 @@ public class HopFrameAuthentication(
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
- TDbContext context)
+ TDbContext context,
+ IPermissionService perms)
: AuthenticationHandler(options, logger, encoder, clock)
where TDbContext : HopDbContextBase {
@@ -42,22 +44,7 @@ public class HopFrameAuthentication(
new(HopFrameClaimTypes.UserId, tokenEntry.UserId)
};
- var permissions = await context.Permissions
- .Where(perm => perm.UserId == tokenEntry.UserId)
- .Select(perm => perm.PermissionText)
- .ToListAsync();
-
- var groups = permissions
- .Where(perm => perm.StartsWith("group."))
- .ToList();
-
- var groupPerms = await context.Permissions
- .Where(perm => groups.Contains(perm.UserId))
- .Select(perm => perm.PermissionText)
- .ToListAsync();
-
- permissions.AddRange(groupPerms);
-
+ var permissions = await perms.GetFullPermissions(tokenEntry.UserId);
claims.AddRange(permissions.Select(perm => new Claim(HopFrameClaimTypes.Permission, perm)));
var principal = new ClaimsPrincipal();
diff --git a/HopFrame.Security/Authentication/HopFrameAuthenticationExtensions.cs b/HopFrame.Security/Authentication/HopFrameAuthenticationExtensions.cs
index fed708c..e6348ed 100644
--- a/HopFrame.Security/Authentication/HopFrameAuthenticationExtensions.cs
+++ b/HopFrame.Security/Authentication/HopFrameAuthenticationExtensions.cs
@@ -1,6 +1,7 @@
using HopFrame.Database;
using HopFrame.Security.Claims;
using HopFrame.Security.Services;
+using HopFrame.Security.Services.Implementation;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
@@ -20,6 +21,8 @@ public static class HopFrameAuthenticationExtensions {
service.TryAddSingleton();
service.AddScoped>();
service.AddScoped>();
+ service.AddScoped>();
+
return service.AddAuthentication(HopFrameAuthentication.SchemeName).AddScheme>(HopFrameAuthentication.SchemeName, _ => {});
}
diff --git a/HopFrame.Security/Authorization/AuthorizedAttribute.cs b/HopFrame.Security/Authorization/AuthorizedAttribute.cs
index ad81973..436881d 100644
--- a/HopFrame.Security/Authorization/AuthorizedAttribute.cs
+++ b/HopFrame.Security/Authorization/AuthorizedAttribute.cs
@@ -6,6 +6,11 @@ public class AuthorizedAttribute : TypeFilterAttribute {
///
/// If this decorator is present, the endpoint is only accessible if the user provided a valid access token (is logged in)
+ /// permission system:
+ /// - "*" -> all rights
+ /// - "group.[name]" -> group member
+ /// - "[namespace].[name]" -> single permission
+ /// - "[namespace].*" -> all permissions in the namespace
///
/// specifies the permissions the user needs to have in order to access this endpoint
public AuthorizedAttribute(params string[] permissions) : base(typeof(AuthorizedFilter)) {
diff --git a/HopFrame.Api/EncryptionManager.cs b/HopFrame.Security/EncryptionManager.cs
similarity index 96%
rename from HopFrame.Api/EncryptionManager.cs
rename to HopFrame.Security/EncryptionManager.cs
index 592ad5d..8f5037b 100644
--- a/HopFrame.Api/EncryptionManager.cs
+++ b/HopFrame.Security/EncryptionManager.cs
@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
-namespace HopFrame.Api;
+namespace HopFrame.Security;
public static class EncryptionManager {
diff --git a/HopFrame.Api/Models/UserRegister.cs b/HopFrame.Security/Models/UserRegister.cs
similarity index 80%
rename from HopFrame.Api/Models/UserRegister.cs
rename to HopFrame.Security/Models/UserRegister.cs
index aacafdc..3f560c1 100644
--- a/HopFrame.Api/Models/UserRegister.cs
+++ b/HopFrame.Security/Models/UserRegister.cs
@@ -1,4 +1,4 @@
-namespace HopFrame.Api.Models;
+namespace HopFrame.Security.Models;
public struct UserRegister {
public string Username { get; set; }
diff --git a/HopFrame.Security/Services/IPermissionService.cs b/HopFrame.Security/Services/IPermissionService.cs
index 646ba22..1564aea 100644
--- a/HopFrame.Security/Services/IPermissionService.cs
+++ b/HopFrame.Security/Services/IPermissionService.cs
@@ -1,43 +1,31 @@
+using HopFrame.Database.Models;
+
namespace HopFrame.Security.Services;
public interface IPermissionService {
-
- ///
- /// Checks for the user to have the specified permission
- /// Permission system:
- /// - "*" -> all rights
- /// - "group.[name]" -> group member
- /// - "[namespace].[name]" -> single permission
- /// - "[namespace].*" -> all permissions in the namespace
- ///
- /// The permission the user needs
- /// rather the user has the permission or not
- Task HasPermission(string permission);
-
- ///
- /// Checks if the user has all the specified permissions
- ///
- /// list of the permissions
- /// rather the user has all the permissions or not
- Task HasPermissions(params string[] permissions);
-
- ///
- /// Checks if the user has any of the specified permissions
- ///
- /// list of the permissions
- /// rather the user has any permission or not
- Task HasAnyPermission(params string[] permissions);
+
+ Task HasPermission(string permission, Guid user);
+
+ Task GetPermissionGroup(string name);
+
+ Task CreatePermissionGroup(string name, bool isDefault = false, string description = null);
+
+ Task DeletePermissionGroup(PermissionGroup group);
///
- /// Checks for the user to have the specified permission
- /// Permission system:
+ /// permission system:
/// - "*" -> all rights
/// - "group.[name]" -> group member
/// - "[namespace].[name]" -> single permission
/// - "[namespace].*" -> all permissions in the namespace
///
- /// The permission the user needs
- /// The user who gets checked
- /// rather the user has the permission or not
- Task HasPermission(string permission, Guid user);
+ ///
+ ///
+ ///
+ Task AddPermission(IPermissionOwner owner, string permission);
+
+ Task DeletePermission(Permission permission);
+
+ internal Task GetFullPermissions(string user);
+
}
\ No newline at end of file
diff --git a/HopFrame.Security/Services/IUserService.cs b/HopFrame.Security/Services/IUserService.cs
new file mode 100644
index 0000000..786d83e
--- /dev/null
+++ b/HopFrame.Security/Services/IUserService.cs
@@ -0,0 +1,27 @@
+using HopFrame.Database.Models;
+using HopFrame.Security.Models;
+
+namespace HopFrame.Security.Services;
+
+public interface IUserService {
+ Task> GetUsers();
+
+ Task GetUser(Guid userId);
+
+ Task GetUserByEmail(string email);
+
+ Task GetUserByUsername(string username);
+
+ Task AddUser(UserRegister user);
+
+ ///
+ /// IMPORTANT:
+ /// This function does not add or remove any permissions to the user.
+ /// For that please use
+ ///
+ Task UpdateUser(User user);
+
+ Task DeleteUser(User user);
+
+ Task GetUserPassword(User user);
+}
\ No newline at end of file
diff --git a/HopFrame.Security/Services/Implementation/PermissionService.cs b/HopFrame.Security/Services/Implementation/PermissionService.cs
new file mode 100644
index 0000000..50dc3a2
--- /dev/null
+++ b/HopFrame.Security/Services/Implementation/PermissionService.cs
@@ -0,0 +1,105 @@
+using HopFrame.Database;
+using HopFrame.Database.Models;
+using HopFrame.Database.Models.Entries;
+using HopFrame.Security.Authorization;
+using HopFrame.Security.Claims;
+using Microsoft.EntityFrameworkCore;
+
+namespace HopFrame.Security.Services.Implementation;
+
+internal sealed class PermissionService(TDbContext context, ITokenContext current) : IPermissionService where TDbContext : HopDbContextBase {
+ public async Task HasPermission(string permission) {
+ return await HasPermission(permission, current.User.Id);
+ }
+
+ public async Task HasPermissions(params string[] permissions) {
+ var user = current.User.Id.ToString();
+ var perms = await GetFullPermissions(user);
+
+ foreach (var permission in permissions) {
+ if (!PermissionValidator.IncludesPermission(permission, perms)) return false;
+ }
+
+ return true;
+ }
+
+ public async Task HasAnyPermission(params string[] permissions) {
+ var user = current.User.Id.ToString();
+ var perms = await GetFullPermissions(user);
+
+ foreach (var permission in permissions) {
+ if (PermissionValidator.IncludesPermission(permission, perms)) return true;
+ }
+
+ return false;
+ }
+
+ public async Task HasPermission(string permission, Guid user) {
+ var permissions = await GetFullPermissions(user.ToString());
+
+ return PermissionValidator.IncludesPermission(permission, permissions);
+ }
+
+ public Task GetPermissionGroup(string name) {
+ return context.Groups
+ .Where(group => group.Name == name)
+ .Select(group => group.ToPermissionGroup(context))
+ .SingleOrDefaultAsync();
+ }
+
+ public async Task CreatePermissionGroup(string name, bool isDefault = false, string description = null) {
+ var group = new GroupEntry {
+ Name = name,
+ Description = description,
+ Default = isDefault,
+ CreatedAt = DateTime.Now
+ };
+
+ await context.Groups.AddAsync(group);
+ await context.SaveChangesAsync();
+ }
+
+ public async Task DeletePermissionGroup(PermissionGroup group) {
+ var entry = await context.Groups.SingleOrDefaultAsync(entry => entry.Name == group.Name);
+ context.Groups.Remove(entry);
+ await context.SaveChangesAsync();
+ }
+
+ public async Task AddPermission(IPermissionOwner owner, string permission) {
+ var userId = owner is User user ? user.Id.ToString() : (owner as PermissionGroup)?.Name;
+
+ await context.Permissions.AddAsync(new PermissionEntry {
+ UserId = userId,
+ PermissionText = permission,
+ GrantedAt = DateTime.Now
+ });
+ await context.SaveChangesAsync();
+ }
+
+ public async Task DeletePermission(Permission permission) {
+ var entry = await context.Permissions.SingleOrDefaultAsync(entry => entry.RecordId == permission.Id);
+ context.Permissions.Remove(entry);
+ await context.SaveChangesAsync();
+ }
+
+ public async Task GetFullPermissions(string user) {
+ var permissions = await context.Permissions
+ .Where(perm => perm.UserId == user)
+ .Select(perm => perm.PermissionText)
+ .ToListAsync();
+
+ var groups = permissions
+ .Where(perm => perm.StartsWith("group."))
+ .ToList();
+
+ var groupPerms = new List();
+ foreach (var group in groups) {
+ var perms = await GetFullPermissions(group);
+ groupPerms.AddRange(perms);
+ }
+
+ permissions.AddRange(groupPerms);
+
+ return permissions.ToArray();
+ }
+}
\ No newline at end of file
diff --git a/HopFrame.Security/Services/Implementation/UserService.cs b/HopFrame.Security/Services/Implementation/UserService.cs
new file mode 100644
index 0000000..7d549d1
--- /dev/null
+++ b/HopFrame.Security/Services/Implementation/UserService.cs
@@ -0,0 +1,110 @@
+using System.Globalization;
+using System.Text;
+using HopFrame.Database;
+using HopFrame.Database.Models;
+using HopFrame.Database.Models.Entries;
+using HopFrame.Security.Models;
+using Microsoft.EntityFrameworkCore;
+
+namespace HopFrame.Security.Services.Implementation;
+
+internal sealed class UserService(TDbContext context) : IUserService where TDbContext : HopDbContextBase {
+ public async Task> GetUsers() {
+ return await context.Users
+ .Select(user => user.ToUserModel(context))
+ .ToListAsync();
+ }
+
+ public Task GetUser(Guid userId) {
+ var id = userId.ToString();
+
+ return context.Users
+ .Where(user => user.Id == id)
+ .Select(user => user.ToUserModel(context))
+ .SingleOrDefaultAsync();
+ }
+
+ public Task GetUserByEmail(string email) {
+ return context.Users
+ .Where(user => user.Email == email)
+ .Select(user => user.ToUserModel(context))
+ .SingleOrDefaultAsync();
+ }
+
+ public Task GetUserByUsername(string username) {
+ return context.Users
+ .Where(user => user.Username == username)
+ .Select(user => user.ToUserModel(context))
+ .SingleOrDefaultAsync();
+ }
+
+ public async Task AddUser(UserRegister user) {
+ var entry = new UserEntry {
+ Id = Guid.NewGuid().ToString(),
+ Email = user.Email,
+ Username = user.Username,
+ CreatedAt = DateTime.Now
+ };
+ entry.Password = EncryptionManager.Hash(user.Password, Encoding.Default.GetBytes(entry.CreatedAt.ToString(CultureInfo.InvariantCulture)));
+
+ await context.Users.AddAsync(entry);
+
+ var defaultGroups = await context.Groups
+ .Where(group => group.Default)
+ .Select(group => "group." + group.Name)
+ .ToListAsync();
+
+ await context.Permissions.AddRangeAsync(defaultGroups.Select(group => new PermissionEntry {
+ GrantedAt = DateTime.Now,
+ PermissionText = group,
+ UserId = entry.Id
+ }));
+
+ await context.SaveChangesAsync();
+ return entry.ToUserModel(context);
+ }
+
+ public async Task UpdateUser(User user) {
+ var id = user.Id.ToString();
+ var entry = await context.Users
+ .SingleOrDefaultAsync(entry => entry.Id == id);
+ if (entry is null) return;
+
+ entry.Email = user.Email;
+ entry.Username = user.Username;
+
+ await context.SaveChangesAsync();
+ }
+
+ public async Task DeleteUser(User user) {
+ var id = user.Id.ToString();
+ var entry = await context.Users
+ .SingleOrDefaultAsync(entry => entry.Id == id);
+
+ if (entry is null) return;
+
+ context.Users.Remove(entry);
+
+ var userTokens = await context.Tokens
+ .Where(token => token.UserId == id)
+ .ToArrayAsync();
+ context.Tokens.RemoveRange(userTokens);
+
+ var userPermissions = await context.Permissions
+ .Where(perm => perm.UserId == id)
+ .ToArrayAsync();
+ context.Permissions.RemoveRange(userPermissions);
+
+ context.OnUserDelete(entry);
+
+ await context.SaveChangesAsync();
+ }
+
+ public Task GetUserPassword(User user) {
+ var id = user.Id.ToString();
+ return context.Users
+ .Where(entry => entry.Id == id)
+ .Select(entry => entry.Password)
+ .SingleOrDefaultAsync();
+ }
+}
\ No newline at end of file
diff --git a/HopFrame.Security/Services/PermissionService.cs b/HopFrame.Security/Services/PermissionService.cs
deleted file mode 100644
index daf4f81..0000000
--- a/HopFrame.Security/Services/PermissionService.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-using HopFrame.Database;
-using HopFrame.Security.Authorization;
-using HopFrame.Security.Claims;
-using Microsoft.EntityFrameworkCore;
-
-namespace HopFrame.Security.Services;
-
-internal class PermissionService(TDbContext context, ITokenContext current) : IPermissionService where TDbContext : HopDbContextBase {
- public async Task HasPermission(string permission) {
- return await HasPermission(permission, current.User.Id);
- }
-
- public async Task HasPermissions(params string[] permissions) {
- var user = current.User.Id.ToString();
- var perms = await GetFullPermissions(user);
-
- foreach (var permission in permissions) {
- if (!PermissionValidator.IncludesPermission(permission, perms)) return false;
- }
-
- return true;
- }
-
- public async Task HasAnyPermission(params string[] permissions) {
- var user = current.User.Id.ToString();
- var perms = await GetFullPermissions(user);
-
- foreach (var permission in permissions) {
- if (PermissionValidator.IncludesPermission(permission, perms)) return true;
- }
-
- return false;
- }
-
- public async Task HasPermission(string permission, Guid user) {
- var permissions = await GetFullPermissions(user.ToString());
-
- return PermissionValidator.IncludesPermission(permission, permissions);
- }
-
- private async Task GetFullPermissions(string user) {
- var permissions = await context.Permissions
- .Where(perm => perm.UserId == user)
- .Select(perm => perm.PermissionText)
- .ToListAsync();
-
- var groups = permissions
- .Where(perm => perm.StartsWith("group."))
- .ToList();
-
- var groupPerms = await context.Permissions
- .Where(perm => groups.Contains(user))
- .Select(perm => perm.PermissionText)
- .ToListAsync();
-
- permissions.AddRange(groupPerms);
-
- return permissions.ToArray();
- }
-}
\ No newline at end of file