Rebuild data storage system so that database dependencies get taken into account
This commit is contained in:
24
src/HopFrame.Database/EncryptionManager.cs
Normal file
24
src/HopFrame.Database/EncryptionManager.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
|
||||
|
||||
namespace HopFrame.Database;
|
||||
|
||||
public static class EncryptionManager {
|
||||
|
||||
/// <summary>
|
||||
/// Encrypts the given string with the specified hash method
|
||||
/// </summary>
|
||||
/// <param name="input">The raw string that should be hashed</param>
|
||||
/// <param name="salt">The "password" for the hash</param>
|
||||
/// <param name="method">The preferred hash method</param>
|
||||
/// <returns></returns>
|
||||
public static string Hash(string input, byte[] salt, KeyDerivationPrf method = KeyDerivationPrf.HMACSHA256) {
|
||||
return Convert.ToBase64String(KeyDerivation.Pbkdf2(
|
||||
password: input,
|
||||
salt: salt,
|
||||
prf: method,
|
||||
iterationCount: 100000,
|
||||
numBytesRequested: 256 / 8
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using HopFrame.Database.Models.Entries;
|
||||
using HopFrame.Database.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace HopFrame.Database;
|
||||
@@ -8,25 +8,27 @@ namespace HopFrame.Database;
|
||||
/// </summary>
|
||||
public abstract class HopDbContextBase : DbContext {
|
||||
|
||||
public virtual DbSet<UserEntry> Users { get; set; }
|
||||
public virtual DbSet<PermissionEntry> Permissions { get; set; }
|
||||
public virtual DbSet<TokenEntry> Tokens { get; set; }
|
||||
public virtual DbSet<GroupEntry> Groups { get; set; }
|
||||
public virtual DbSet<User> Users { get; set; }
|
||||
public virtual DbSet<Permission> Permissions { get; set; }
|
||||
public virtual DbSet<Token> Tokens { get; set; }
|
||||
public virtual DbSet<PermissionGroup> Groups { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder) {
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
modelBuilder.Entity<UserEntry>();
|
||||
modelBuilder.Entity<PermissionEntry>();
|
||||
modelBuilder.Entity<TokenEntry>();
|
||||
modelBuilder.Entity<GroupEntry>();
|
||||
}
|
||||
modelBuilder.Entity<User>()
|
||||
.HasMany(u => u.Tokens)
|
||||
.WithOne(t => t.Owner)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
public virtual void OnUserDelete(UserEntry user) {}
|
||||
modelBuilder.Entity<User>()
|
||||
.HasMany(u => u.Permissions)
|
||||
.WithOne(p => p.User)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
modelBuilder.Entity<PermissionGroup>()
|
||||
.HasMany(g => g.Permissions)
|
||||
.WithOne(p => p.Group)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.7" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace HopFrame.Database.Models.Entries;
|
||||
|
||||
public class GroupEntry {
|
||||
[Key, Required, MaxLength(50)]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Required, DefaultValue(false)]
|
||||
public bool Default { get; set; }
|
||||
|
||||
[MaxLength(500)]
|
||||
public string Description { get; set; }
|
||||
|
||||
[Required]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace HopFrame.Database.Models.Entries;
|
||||
|
||||
public sealed class PermissionEntry {
|
||||
[Key, Required, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public long RecordId { get; set; }
|
||||
|
||||
[Required, MaxLength(255)]
|
||||
public string PermissionText { get; set; }
|
||||
|
||||
[Required, MinLength(36), MaxLength(36)]
|
||||
public string UserId { get; set; }
|
||||
|
||||
[Required]
|
||||
public DateTime GrantedAt { get; set; }
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace HopFrame.Database.Models.Entries;
|
||||
|
||||
public class UserEntry {
|
||||
[Key, Required, MinLength(36), MaxLength(36)]
|
||||
public string Id { get; set; }
|
||||
|
||||
[MaxLength(50)]
|
||||
public string Username { get; set; }
|
||||
|
||||
[Required, MaxLength(50), EmailAddress]
|
||||
public string Email { get; set; }
|
||||
|
||||
[Required, MinLength(8), MaxLength(255)]
|
||||
public string Password { get; set; }
|
||||
|
||||
[Required]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
using HopFrame.Database.Models.Entries;
|
||||
|
||||
namespace HopFrame.Database.Models;
|
||||
|
||||
public static class ModelExtensions {
|
||||
|
||||
/// <summary>
|
||||
/// Converts the database model to a friendly user model
|
||||
/// </summary>
|
||||
/// <param name="entry">the database model</param>
|
||||
/// <param name="contextBase">the data source for the permissions and users</param>
|
||||
/// <returns></returns>
|
||||
public static User ToUserModel(this UserEntry entry, HopDbContextBase contextBase) {
|
||||
var user = new User {
|
||||
Id = Guid.Parse(entry.Id),
|
||||
Username = entry.Username,
|
||||
Email = entry.Email,
|
||||
CreatedAt = entry.CreatedAt
|
||||
};
|
||||
|
||||
user.Permissions = contextBase.Permissions
|
||||
.Where(perm => perm.UserId == entry.Id)
|
||||
.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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +1,26 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace HopFrame.Database.Models;
|
||||
|
||||
public sealed class Permission {
|
||||
public class Permission {
|
||||
|
||||
[Key, Required, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public long Id { get; init; }
|
||||
|
||||
[Required, MaxLength(255)]
|
||||
public string PermissionName { get; set; }
|
||||
public Guid Owner { get; set; }
|
||||
|
||||
[Required]
|
||||
public DateTime GrantedAt { get; set; }
|
||||
|
||||
[ForeignKey("UserId"), JsonIgnore]
|
||||
public virtual User User { get; set; }
|
||||
|
||||
[ForeignKey("GroupName"), JsonIgnore]
|
||||
public virtual PermissionGroup Group { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public interface IPermissionOwner {}
|
||||
public interface IPermissionOwner;
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace HopFrame.Database.Models;
|
||||
|
||||
public class PermissionGroup : IPermissionOwner {
|
||||
|
||||
[Key, Required, MaxLength(50)]
|
||||
public string Name { get; init; }
|
||||
|
||||
[Required, DefaultValue(false)]
|
||||
public bool IsDefaultGroup { get; set; }
|
||||
|
||||
[MaxLength(500)]
|
||||
public string Description { get; set; }
|
||||
|
||||
[Required]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public IList<Permission> Permissions { get; set; }
|
||||
|
||||
public virtual IList<Permission> Permissions { get; set; }
|
||||
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace HopFrame.Database.Models.Entries;
|
||||
namespace HopFrame.Database.Models;
|
||||
|
||||
public class TokenEntry {
|
||||
public class Token {
|
||||
public const int RefreshTokenType = 0;
|
||||
public const int AccessTokenType = 1;
|
||||
|
||||
@@ -15,11 +17,11 @@ public class TokenEntry {
|
||||
public int Type { get; set; }
|
||||
|
||||
[Key, Required, MinLength(36), MaxLength(36)]
|
||||
public string Token { get; set; }
|
||||
|
||||
[Required, MinLength(36), MaxLength(36)]
|
||||
public string UserId { get; set; }
|
||||
public Guid Content { get; set; }
|
||||
|
||||
[Required]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
|
||||
[ForeignKey("UserId"), JsonIgnore]
|
||||
public virtual User Owner { get; set; }
|
||||
}
|
||||
@@ -1,9 +1,28 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace HopFrame.Database.Models;
|
||||
|
||||
public sealed class User : IPermissionOwner {
|
||||
public class User : IPermissionOwner {
|
||||
|
||||
[Key, Required, MinLength(36), MaxLength(36)]
|
||||
public Guid Id { get; init; }
|
||||
|
||||
[MaxLength(50)]
|
||||
public string Username { get; set; }
|
||||
|
||||
[Required, MaxLength(50), EmailAddress]
|
||||
public string Email { get; set; }
|
||||
|
||||
[Required, MinLength(8), MaxLength(255), JsonIgnore]
|
||||
public string Password { get; set; }
|
||||
|
||||
[Required]
|
||||
public DateTime CreatedAt { get; set; }
|
||||
public IList<Permission> Permissions { get; set; }
|
||||
|
||||
public virtual IList<Permission> Permissions { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public virtual IList<Token> Tokens { get; set; }
|
||||
|
||||
}
|
||||
25
src/HopFrame.Database/PermissionValidator.cs
Normal file
25
src/HopFrame.Database/PermissionValidator.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
namespace HopFrame.Database;
|
||||
|
||||
public static class PermissionValidator {
|
||||
|
||||
public static bool IncludesPermission(string permission, string[] permissions) {
|
||||
var permLow = permission.ToLower();
|
||||
var permsLow = permissions.Select(perm => perm.ToLower()).ToArray();
|
||||
|
||||
if (permsLow.Any(perm =>
|
||||
perm == permLow ||
|
||||
(perm.Length > permLow.Length && perm.StartsWith(permLow) && perm.ToCharArray()[permLow.Length] == '.') ||
|
||||
perm == "*"))
|
||||
return true;
|
||||
|
||||
foreach (var perm in permsLow) {
|
||||
if (!perm.EndsWith(".*")) continue;
|
||||
|
||||
var permissionGroup = perm.Substring(0, perm.Length - 1);
|
||||
if (permLow.StartsWith(permissionGroup)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
19
src/HopFrame.Database/Repositories/IGroupRepository.cs
Normal file
19
src/HopFrame.Database/Repositories/IGroupRepository.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using HopFrame.Database.Models;
|
||||
|
||||
namespace HopFrame.Database.Repositories;
|
||||
|
||||
public interface IGroupRepository {
|
||||
Task<IList<PermissionGroup>> GetPermissionGroups();
|
||||
|
||||
Task<IList<PermissionGroup>> GetDefaultGroups();
|
||||
|
||||
Task<PermissionGroup> GetPermissionGroup(string name);
|
||||
|
||||
Task EditPermissionGroup(PermissionGroup group);
|
||||
|
||||
Task<PermissionGroup> CreatePermissionGroup(PermissionGroup group);
|
||||
|
||||
Task DeletePermissionGroup(PermissionGroup group);
|
||||
|
||||
internal Task<IList<string>> GetFullGroupPermissions(string group);
|
||||
}
|
||||
23
src/HopFrame.Database/Repositories/IPermissionRepository.cs
Normal file
23
src/HopFrame.Database/Repositories/IPermissionRepository.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using HopFrame.Database.Models;
|
||||
|
||||
namespace HopFrame.Database.Repositories;
|
||||
|
||||
public interface IPermissionRepository {
|
||||
Task<bool> HasPermission(IPermissionOwner owner, params string[] permissions);
|
||||
|
||||
/// <summary>
|
||||
/// permission system:<br/>
|
||||
/// - "*" -> all rights<br/>
|
||||
/// - "group.[name]" -> group member<br/>
|
||||
/// - "[namespace].[name]" -> single permission<br/>
|
||||
/// - "[namespace].*" -> all permissions in the namespace
|
||||
/// </summary>
|
||||
/// <param name="owner"></param>
|
||||
/// <param name="permission"></param>
|
||||
/// <returns></returns>
|
||||
Task<Permission> AddPermission(IPermissionOwner owner, string permission);
|
||||
|
||||
Task RemovePermission(IPermissionOwner owner, string permission);
|
||||
|
||||
public Task<IList<string>> GetFullPermissions(IPermissionOwner owner);
|
||||
}
|
||||
9
src/HopFrame.Database/Repositories/ITokenRepository.cs
Normal file
9
src/HopFrame.Database/Repositories/ITokenRepository.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using HopFrame.Database.Models;
|
||||
|
||||
namespace HopFrame.Database.Repositories;
|
||||
|
||||
public interface ITokenRepository {
|
||||
public Task<Token> GetToken(string content);
|
||||
public Task<Token> CreateToken(int type, User owner);
|
||||
public Task DeleteUserTokens(User owner);
|
||||
}
|
||||
28
src/HopFrame.Database/Repositories/IUserRepository.cs
Normal file
28
src/HopFrame.Database/Repositories/IUserRepository.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using HopFrame.Database.Models;
|
||||
|
||||
namespace HopFrame.Database.Repositories;
|
||||
|
||||
public interface IUserRepository {
|
||||
Task<IList<User>> GetUsers();
|
||||
|
||||
Task<User> GetUser(Guid userId);
|
||||
|
||||
Task<User> GetUserByEmail(string email);
|
||||
|
||||
Task<User> GetUserByUsername(string username);
|
||||
|
||||
Task<User> AddUser(User user);
|
||||
|
||||
/// <summary>
|
||||
/// IMPORTANT:<br/>
|
||||
/// This function does not add or remove any permissions to the user.
|
||||
/// For that please use <see cref="IPermissionRepository"/>
|
||||
/// </summary>
|
||||
Task UpdateUser(User user);
|
||||
|
||||
Task DeleteUser(User user);
|
||||
|
||||
Task<bool> CheckUserPassword(User user, string password);
|
||||
|
||||
Task ChangePassword(User user, string password);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using HopFrame.Database.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace HopFrame.Database.Repositories.Implementation;
|
||||
|
||||
internal sealed class GroupRepository<TDbContext>(TDbContext context) : IGroupRepository where TDbContext : HopDbContextBase {
|
||||
public async Task<IList<PermissionGroup>> GetPermissionGroups() {
|
||||
return await context.Groups
|
||||
.Include(g => g.Permissions)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IList<PermissionGroup>> GetDefaultGroups() {
|
||||
return await context.Groups
|
||||
.Include(g => g.Permissions)
|
||||
.Where(g => g.IsDefaultGroup)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<PermissionGroup> GetPermissionGroup(string name) {
|
||||
return await context.Groups
|
||||
.Include(g => g.Permissions)
|
||||
.Where(g => g.Name == name)
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task EditPermissionGroup(PermissionGroup group) {
|
||||
var orig = await context.Groups.SingleOrDefaultAsync(g => g.Name == group.Name);
|
||||
|
||||
if (orig is null) return;
|
||||
|
||||
var entity = context.Groups.Update(orig);
|
||||
|
||||
entity.Entity.IsDefaultGroup = group.IsDefaultGroup;
|
||||
entity.Entity.Description = group.Description;
|
||||
entity.Entity.Permissions = group.Permissions;
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<PermissionGroup> CreatePermissionGroup(PermissionGroup group) {
|
||||
group.CreatedAt = DateTime.Now;
|
||||
await context.Groups.AddAsync(group);
|
||||
await context.SaveChangesAsync();
|
||||
return group;
|
||||
}
|
||||
|
||||
public async Task DeletePermissionGroup(PermissionGroup group) {
|
||||
context.Groups.Remove(group);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<IList<string>> GetFullGroupPermissions(string group) {
|
||||
var permissions = await context.Permissions
|
||||
.Include(p => p.Group)
|
||||
.Where(p => p.Group != null)
|
||||
.Where(p => p.Group.Name == group)
|
||||
.Select(p => p.PermissionName)
|
||||
.ToListAsync();
|
||||
|
||||
var groups = permissions
|
||||
.Where(p => p.StartsWith("group."))
|
||||
.ToList();
|
||||
|
||||
foreach (var subgroup in groups) {
|
||||
permissions.AddRange(await GetFullGroupPermissions(subgroup));
|
||||
}
|
||||
|
||||
return permissions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using HopFrame.Database.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
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) {
|
||||
var perms = (await GetFullPermissions(owner)).ToArray();
|
||||
|
||||
foreach (var permission in permissions) {
|
||||
if (!PermissionValidator.IncludesPermission(permission, perms)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<Permission> AddPermission(IPermissionOwner owner, string permission) {
|
||||
var entry = new Permission {
|
||||
GrantedAt = DateTime.Now,
|
||||
PermissionName = permission
|
||||
};
|
||||
|
||||
if (owner is User user) {
|
||||
entry.User = user;
|
||||
}else if (owner is PermissionGroup group) {
|
||||
entry.Group = group;
|
||||
}
|
||||
|
||||
await context.Permissions.AddAsync(entry);
|
||||
await context.SaveChangesAsync();
|
||||
return entry;
|
||||
}
|
||||
|
||||
public async Task RemovePermission(IPermissionOwner owner, string permission) {
|
||||
Permission entry = null;
|
||||
|
||||
if (owner is User user) {
|
||||
entry = await context.Permissions
|
||||
.Include(p => p.User)
|
||||
.Where(p => p.User != null)
|
||||
.Where(p => p.User.Id == user.Id)
|
||||
.Where(p => p.PermissionName == permission)
|
||||
.SingleOrDefaultAsync();
|
||||
}else if (owner is PermissionGroup group) {
|
||||
entry = await context.Permissions
|
||||
.Include(p => p.Group)
|
||||
.Where(p => p.Group != null)
|
||||
.Where(p =>p.Group.Name == group.Name)
|
||||
.Where(p => p.PermissionName == permission)
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
if (entry is not null) {
|
||||
context.Permissions.Remove(entry);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IList<string>> GetFullPermissions(IPermissionOwner owner) {
|
||||
var permissions = new List<string>();
|
||||
|
||||
if (owner is User user) {
|
||||
var perms = await context.Permissions
|
||||
.Include(p => p.User)
|
||||
.Where(p => p.User != null)
|
||||
.Where(p => p.User.Id == user.Id)
|
||||
.ToListAsync();
|
||||
|
||||
permissions.AddRange(perms.Select(p => p.PermissionName));
|
||||
}else if (owner is PermissionGroup group) {
|
||||
var perms = await context.Permissions
|
||||
.Include(p => p.Group)
|
||||
.Where(p => p.Group != null)
|
||||
.Where(p =>p.Group.Name == group.Name)
|
||||
.ToListAsync();
|
||||
|
||||
permissions.AddRange(perms.Select(p => p.PermissionName));
|
||||
}
|
||||
|
||||
var groups = permissions
|
||||
.Where(p => p.StartsWith("group."))
|
||||
.ToList();
|
||||
foreach (var group in groups) {
|
||||
permissions.AddRange(await groupRepository.GetFullGroupPermissions(group));
|
||||
}
|
||||
|
||||
return permissions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using HopFrame.Database.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace HopFrame.Database.Repositories.Implementation;
|
||||
|
||||
internal sealed class TokenRepository<TDbContext>(TDbContext context) : ITokenRepository where TDbContext : HopDbContextBase {
|
||||
|
||||
public async Task<Token> GetToken(string content) {
|
||||
var success = Guid.TryParse(content, out Guid guid);
|
||||
if (!success) return null;
|
||||
|
||||
return await context.Tokens
|
||||
.Include(t => t.Owner)
|
||||
.Where(t => t.Content == guid)
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<Token> CreateToken(int type, User owner) {
|
||||
var token = new Token {
|
||||
CreatedAt = DateTime.Now,
|
||||
Content = Guid.NewGuid(),
|
||||
Type = type,
|
||||
Owner = owner
|
||||
};
|
||||
|
||||
await context.Tokens.AddAsync(token);
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
public async Task DeleteUserTokens(User owner) {
|
||||
var tokens = await context.Tokens
|
||||
.Include(t => t.Owner)
|
||||
.Where(t => t.Owner.Id == owner.Id)
|
||||
.ToListAsync();
|
||||
|
||||
context.Tokens.RemoveRange(tokens);
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using HopFrame.Database.Models;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Query;
|
||||
|
||||
namespace HopFrame.Database.Repositories.Implementation;
|
||||
|
||||
internal sealed class UserRepository<TDbContext>(TDbContext context, IGroupRepository groupRepository) : IUserRepository where TDbContext : HopDbContextBase {
|
||||
|
||||
private IIncludableQueryable<User, IList<Token>> IncludeReferences() {
|
||||
return context.Users
|
||||
.Include(u => u.Permissions)
|
||||
.ThenInclude(p => p.Group)
|
||||
.Include(u => u.Tokens);
|
||||
}
|
||||
|
||||
public async Task<IList<User>> GetUsers() {
|
||||
return await IncludeReferences()
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<User> GetUser(Guid userId) {
|
||||
return await IncludeReferences()
|
||||
.Where(u => u.Id == userId)
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<User> GetUserByEmail(string email) {
|
||||
return await IncludeReferences()
|
||||
.Where(u => u.Email == email)
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<User> GetUserByUsername(string username) {
|
||||
return await IncludeReferences()
|
||||
.Where(u => u.Username == username)
|
||||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<User> AddUser(User user) {
|
||||
if (await GetUserByEmail(user.Email) is not null) return null;
|
||||
if (await GetUserByUsername(user.Username) is not null) return null;
|
||||
|
||||
var entry = new User {
|
||||
Id = Guid.NewGuid(),
|
||||
Email = user.Email,
|
||||
Username = user.Username,
|
||||
CreatedAt = DateTime.Now,
|
||||
Permissions = user.Permissions,
|
||||
Tokens = user.Tokens
|
||||
};
|
||||
entry.Password = EncryptionManager.Hash(user.Password, Encoding.Default.GetBytes(entry.CreatedAt.ToString(CultureInfo.InvariantCulture)));
|
||||
|
||||
var defaultGroups = await groupRepository.GetDefaultGroups();
|
||||
foreach (var group in defaultGroups) {
|
||||
entry.Permissions.Add(new Permission {
|
||||
PermissionName = group.Name,
|
||||
//TODO: Check if user needs to be set
|
||||
});
|
||||
}
|
||||
|
||||
await context.Users.AddAsync(entry);
|
||||
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
return entry;
|
||||
}
|
||||
|
||||
public async Task UpdateUser(User user) {
|
||||
var entry = await IncludeReferences()
|
||||
.SingleOrDefaultAsync(entry => entry.Id == user.Id);
|
||||
if (entry is null) return;
|
||||
|
||||
entry.Email = user.Email;
|
||||
entry.Username = user.Username;
|
||||
entry.Permissions = user.Permissions;
|
||||
entry.Tokens = user.Tokens;
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteUser(User user) {
|
||||
var entry = await context.Users
|
||||
.SingleOrDefaultAsync(entry => entry.Id == user.Id);
|
||||
|
||||
if (entry is null) return;
|
||||
|
||||
context.Users.Remove(entry);
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<bool> CheckUserPassword(User user, string password) {
|
||||
var hash = EncryptionManager.Hash(password, Encoding.Default.GetBytes(user.CreatedAt.ToString(CultureInfo.InvariantCulture)));
|
||||
|
||||
var entry = await context.Users
|
||||
.Where(entry => entry.Id == user.Id)
|
||||
.SingleOrDefaultAsync();
|
||||
|
||||
return entry.Password == hash;
|
||||
}
|
||||
|
||||
public async Task ChangePassword(User user, string password) {
|
||||
var entry = await context.Users
|
||||
.Where(entry => entry.Id == user.Id)
|
||||
.SingleOrDefaultAsync();
|
||||
|
||||
if (entry is null) return;
|
||||
|
||||
var hash = EncryptionManager.Hash(password, Encoding.Default.GetBytes(user.CreatedAt.ToString(CultureInfo.InvariantCulture)));
|
||||
entry.Password = hash;
|
||||
await context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
}
|
||||
18
src/HopFrame.Database/ServiceCollectionExtensions.cs
Normal file
18
src/HopFrame.Database/ServiceCollectionExtensions.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using HopFrame.Database.Repositories;
|
||||
using HopFrame.Database.Repositories.Implementation;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace HopFrame.Database;
|
||||
|
||||
public static class ServiceCollectionExtensions {
|
||||
|
||||
public static IServiceCollection AddHopFrameRepositories<TDbContext>(this IServiceCollection services) where TDbContext : HopDbContextBase {
|
||||
services.AddScoped<IGroupRepository, GroupRepository<TDbContext>>();
|
||||
services.AddScoped<IPermissionRepository, PermissionRepository<TDbContext>>();
|
||||
services.AddScoped<IUserRepository, UserRepository<TDbContext>>();
|
||||
services.AddScoped<ITokenRepository, TokenRepository<TDbContext>>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user