Merge pull request #6 from leonhoppe/feature/relations
Feature/relations
This commit is contained in:
2
.idea/.idea.HopFrame/.idea/dataSources.xml
generated
2
.idea/.idea.HopFrame/.idea/dataSources.xml
generated
@@ -5,7 +5,7 @@
|
|||||||
<driver-ref>sqlite.xerial</driver-ref>
|
<driver-ref>sqlite.xerial</driver-ref>
|
||||||
<synchronize>true</synchronize>
|
<synchronize>true</synchronize>
|
||||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||||
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/RestApiTest/bin/Debug/net8.0/test.db</jdbc-url>
|
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/test/RestApiTest/bin/Debug/net8.0/test.db</jdbc-url>
|
||||||
<jdbc-additional-properties>
|
<jdbc-additional-properties>
|
||||||
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
|
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
|
||||||
</jdbc-additional-properties>
|
</jdbc-additional-properties>
|
||||||
|
|||||||
@@ -27,10 +27,11 @@ public static class ServiceCollectionExtensions {
|
|||||||
/// <param name="services">The service provider to add the services to</param>
|
/// <param name="services">The service provider to add the services to</param>
|
||||||
/// <typeparam name="TDbContext">The data source for all HopFrame entities</typeparam>
|
/// <typeparam name="TDbContext">The data source for all HopFrame entities</typeparam>
|
||||||
public static void AddHopFrameNoEndpoints<TDbContext>(this IServiceCollection services) where TDbContext : HopDbContextBase {
|
public static void AddHopFrameNoEndpoints<TDbContext>(this IServiceCollection services) where TDbContext : HopDbContextBase {
|
||||||
|
services.AddHopFrameRepositories<TDbContext>();
|
||||||
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||||
services.AddScoped<IAuthLogic, AuthLogic<TDbContext>>();
|
services.AddScoped<IAuthLogic, AuthLogic>();
|
||||||
|
|
||||||
services.AddHopFrameAuthentication<TDbContext>();
|
services.AddHopFrameAuthentication();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
using HopFrame.Api.Models;
|
using HopFrame.Api.Models;
|
||||||
using HopFrame.Database;
|
using HopFrame.Database.Models;
|
||||||
using HopFrame.Database.Models.Entries;
|
using HopFrame.Database.Repositories;
|
||||||
using HopFrame.Security.Authentication;
|
using HopFrame.Security.Authentication;
|
||||||
using HopFrame.Security.Claims;
|
using HopFrame.Security.Claims;
|
||||||
using HopFrame.Security.Models;
|
using HopFrame.Security.Models;
|
||||||
using HopFrame.Security.Services;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace HopFrame.Api.Logic.Implementation;
|
namespace HopFrame.Api.Logic.Implementation;
|
||||||
|
|
||||||
public class AuthLogic<TDbContext>(TDbContext context, IUserService users, ITokenContext tokenContext, IHttpContextAccessor accessor) : IAuthLogic where TDbContext : HopDbContextBase {
|
public class AuthLogic(IUserRepository users, ITokenRepository tokens, ITokenContext tokenContext, IHttpContextAccessor accessor) : IAuthLogic {
|
||||||
|
|
||||||
public async Task<LogicResult<SingleValueResult<string>>> Login(UserLogin login) {
|
public async Task<LogicResult<SingleValueResult<string>>> Login(UserLogin login) {
|
||||||
var user = await users.GetUserByEmail(login.Email);
|
var user = await users.GetUserByEmail(login.Email);
|
||||||
@@ -21,34 +19,21 @@ public class AuthLogic<TDbContext>(TDbContext context, IUserService users, IToke
|
|||||||
if (!await users.CheckUserPassword(user, login.Password))
|
if (!await users.CheckUserPassword(user, login.Password))
|
||||||
return LogicResult<SingleValueResult<string>>.Forbidden("The provided password is not correct");
|
return LogicResult<SingleValueResult<string>>.Forbidden("The provided password is not correct");
|
||||||
|
|
||||||
var refreshToken = new TokenEntry {
|
var refreshToken = await tokens.CreateToken(Token.RefreshTokenType, user);
|
||||||
CreatedAt = DateTime.Now,
|
var accessToken = await tokens.CreateToken(Token.AccessTokenType, user);
|
||||||
Token = Guid.NewGuid().ToString(),
|
|
||||||
Type = TokenEntry.RefreshTokenType,
|
|
||||||
UserId = user.Id.ToString()
|
|
||||||
};
|
|
||||||
var accessToken = new TokenEntry {
|
|
||||||
CreatedAt = DateTime.Now,
|
|
||||||
Token = Guid.NewGuid().ToString(),
|
|
||||||
Type = TokenEntry.AccessTokenType,
|
|
||||||
UserId = user.Id.ToString()
|
|
||||||
};
|
|
||||||
|
|
||||||
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.RefreshTokenType, refreshToken.Token, new CookieOptions {
|
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.RefreshTokenType, refreshToken.Content.ToString(), new CookieOptions {
|
||||||
MaxAge = HopFrameAuthentication<TDbContext>.RefreshTokenTime,
|
MaxAge = HopFrameAuthentication.RefreshTokenTime,
|
||||||
HttpOnly = true,
|
HttpOnly = true,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Token, new CookieOptions {
|
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Content.ToString(), new CookieOptions {
|
||||||
MaxAge = HopFrameAuthentication<TDbContext>.AccessTokenTime,
|
MaxAge = HopFrameAuthentication.AccessTokenTime,
|
||||||
HttpOnly = true,
|
HttpOnly = true,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
|
|
||||||
await context.Tokens.AddRangeAsync(refreshToken, accessToken);
|
return LogicResult<SingleValueResult<string>>.Ok(accessToken.Content.ToString());
|
||||||
await context.SaveChangesAsync();
|
|
||||||
|
|
||||||
return LogicResult<SingleValueResult<string>>.Ok(accessToken.Token);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<LogicResult<SingleValueResult<string>>> Register(UserRegister register) {
|
public async Task<LogicResult<SingleValueResult<string>>> Register(UserRegister register) {
|
||||||
@@ -59,36 +44,27 @@ public class AuthLogic<TDbContext>(TDbContext context, IUserService users, IToke
|
|||||||
if (allUsers.Any(user => user.Username == register.Username || user.Email == register.Email))
|
if (allUsers.Any(user => user.Username == register.Username || user.Email == register.Email))
|
||||||
return LogicResult<SingleValueResult<string>>.Conflict("Username or Email is already registered");
|
return LogicResult<SingleValueResult<string>>.Conflict("Username or Email is already registered");
|
||||||
|
|
||||||
var user = await users.AddUser(register);
|
var user = await users.AddUser(new User {
|
||||||
|
Username = register.Username,
|
||||||
|
Email = register.Email,
|
||||||
|
Password = register.Password
|
||||||
|
});
|
||||||
|
|
||||||
var refreshToken = new TokenEntry {
|
var refreshToken = await tokens.CreateToken(Token.RefreshTokenType, user);
|
||||||
CreatedAt = DateTime.Now,
|
var accessToken = await tokens.CreateToken(Token.AccessTokenType, user);
|
||||||
Token = Guid.NewGuid().ToString(),
|
|
||||||
Type = TokenEntry.RefreshTokenType,
|
|
||||||
UserId = user.Id.ToString()
|
|
||||||
};
|
|
||||||
var accessToken = new TokenEntry {
|
|
||||||
CreatedAt = DateTime.Now,
|
|
||||||
Token = Guid.NewGuid().ToString(),
|
|
||||||
Type = TokenEntry.AccessTokenType,
|
|
||||||
UserId = user.Id.ToString()
|
|
||||||
};
|
|
||||||
|
|
||||||
await context.Tokens.AddRangeAsync(refreshToken, accessToken);
|
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.RefreshTokenType, refreshToken.Content.ToString(), new CookieOptions {
|
||||||
await context.SaveChangesAsync();
|
MaxAge = HopFrameAuthentication.RefreshTokenTime,
|
||||||
|
|
||||||
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.RefreshTokenType, refreshToken.Token, new CookieOptions {
|
|
||||||
MaxAge = HopFrameAuthentication<TDbContext>.RefreshTokenTime,
|
|
||||||
HttpOnly = true,
|
HttpOnly = true,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Token, new CookieOptions {
|
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Content.ToString(), new CookieOptions {
|
||||||
MaxAge = HopFrameAuthentication<TDbContext>.AccessTokenTime,
|
MaxAge = HopFrameAuthentication.AccessTokenTime,
|
||||||
HttpOnly = false,
|
HttpOnly = false,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
|
|
||||||
return LogicResult<SingleValueResult<string>>.Ok(accessToken.Token);
|
return LogicResult<SingleValueResult<string>>.Ok(accessToken.Content.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<LogicResult<SingleValueResult<string>>> Authenticate() {
|
public async Task<LogicResult<SingleValueResult<string>>> Authenticate() {
|
||||||
@@ -97,31 +73,26 @@ public class AuthLogic<TDbContext>(TDbContext context, IUserService users, IToke
|
|||||||
if (string.IsNullOrEmpty(refreshToken))
|
if (string.IsNullOrEmpty(refreshToken))
|
||||||
return LogicResult<SingleValueResult<string>>.Conflict("Refresh token not provided");
|
return LogicResult<SingleValueResult<string>>.Conflict("Refresh token not provided");
|
||||||
|
|
||||||
var token = await context.Tokens.SingleOrDefaultAsync(token => token.Token == refreshToken && token.Type == TokenEntry.RefreshTokenType);
|
var token = await tokens.GetToken(refreshToken);
|
||||||
|
|
||||||
|
if (token.Type != Token.RefreshTokenType)
|
||||||
|
return LogicResult<SingleValueResult<string>>.BadRequest("The provided token is not a refresh token");
|
||||||
|
|
||||||
if (token is null)
|
if (token is null)
|
||||||
return LogicResult<SingleValueResult<string>>.NotFound("Refresh token not valid");
|
return LogicResult<SingleValueResult<string>>.NotFound("Refresh token not valid");
|
||||||
|
|
||||||
if (token.CreatedAt + HopFrameAuthentication<TDbContext>.RefreshTokenTime < DateTime.Now)
|
if (token.CreatedAt + HopFrameAuthentication.RefreshTokenTime < DateTime.Now)
|
||||||
return LogicResult<SingleValueResult<string>>.Conflict("Refresh token is expired");
|
return LogicResult<SingleValueResult<string>>.Conflict("Refresh token is expired");
|
||||||
|
|
||||||
var accessToken = new TokenEntry {
|
var accessToken = await tokens.CreateToken(Token.AccessTokenType, token.Owner);
|
||||||
CreatedAt = DateTime.Now,
|
|
||||||
Token = Guid.NewGuid().ToString(),
|
|
||||||
Type = TokenEntry.AccessTokenType,
|
|
||||||
UserId = token.UserId
|
|
||||||
};
|
|
||||||
|
|
||||||
await context.Tokens.AddAsync(accessToken);
|
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Content.ToString(), new CookieOptions {
|
||||||
await context.SaveChangesAsync();
|
MaxAge = HopFrameAuthentication.AccessTokenTime,
|
||||||
|
|
||||||
accessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Token, new CookieOptions {
|
|
||||||
MaxAge = HopFrameAuthentication<TDbContext>.AccessTokenTime,
|
|
||||||
HttpOnly = false,
|
HttpOnly = false,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
|
|
||||||
return LogicResult<SingleValueResult<string>>.Ok(accessToken.Token);
|
return LogicResult<SingleValueResult<string>>.Ok(accessToken.Content.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<LogicResult> Logout() {
|
public async Task<LogicResult> Logout() {
|
||||||
@@ -131,17 +102,7 @@ public class AuthLogic<TDbContext>(TDbContext context, IUserService users, IToke
|
|||||||
if (string.IsNullOrEmpty(accessToken) || string.IsNullOrEmpty(refreshToken))
|
if (string.IsNullOrEmpty(accessToken) || string.IsNullOrEmpty(refreshToken))
|
||||||
return LogicResult.Conflict("access or refresh token not provided");
|
return LogicResult.Conflict("access or refresh token not provided");
|
||||||
|
|
||||||
var tokenEntries = await context.Tokens.Where(token =>
|
await tokens.DeleteUserTokens(tokenContext.User);
|
||||||
(token.Token == accessToken && token.Type == TokenEntry.AccessTokenType) ||
|
|
||||||
(token.Token == refreshToken && token.Type == TokenEntry.RefreshTokenType))
|
|
||||||
.ToArrayAsync();
|
|
||||||
|
|
||||||
if (tokenEntries.Length != 2)
|
|
||||||
return LogicResult.NotFound("One or more of the provided tokens was not found");
|
|
||||||
|
|
||||||
context.Tokens.Remove(tokenEntries[0]);
|
|
||||||
context.Tokens.Remove(tokenEntries[1]);
|
|
||||||
await context.SaveChangesAsync();
|
|
||||||
|
|
||||||
accessor.HttpContext?.Response.Cookies.Delete(ITokenContext.RefreshTokenType);
|
accessor.HttpContext?.Response.Cookies.Delete(ITokenContext.RefreshTokenType);
|
||||||
accessor.HttpContext?.Response.Cookies.Delete(ITokenContext.AccessTokenType);
|
accessor.HttpContext?.Response.Cookies.Delete(ITokenContext.AccessTokenType);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
|
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
|
||||||
|
|
||||||
namespace HopFrame.Security;
|
namespace HopFrame.Database;
|
||||||
|
|
||||||
public static class EncryptionManager {
|
public static class EncryptionManager {
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using HopFrame.Database.Models.Entries;
|
using HopFrame.Database.Models;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace HopFrame.Database;
|
namespace HopFrame.Database;
|
||||||
@@ -8,25 +8,27 @@ namespace HopFrame.Database;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class HopDbContextBase : DbContext {
|
public abstract class HopDbContextBase : DbContext {
|
||||||
|
|
||||||
public virtual DbSet<UserEntry> Users { get; set; }
|
public virtual DbSet<User> Users { get; set; }
|
||||||
public virtual DbSet<PermissionEntry> Permissions { get; set; }
|
public virtual DbSet<Permission> Permissions { get; set; }
|
||||||
public virtual DbSet<TokenEntry> Tokens { get; set; }
|
public virtual DbSet<Token> Tokens { get; set; }
|
||||||
public virtual DbSet<GroupEntry> Groups { get; set; }
|
public virtual DbSet<PermissionGroup> Groups { get; set; }
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder) {
|
protected override void OnModelCreating(ModelBuilder modelBuilder) {
|
||||||
base.OnModelCreating(modelBuilder);
|
base.OnModelCreating(modelBuilder);
|
||||||
|
|
||||||
modelBuilder.Entity<UserEntry>();
|
modelBuilder.Entity<User>()
|
||||||
modelBuilder.Entity<PermissionEntry>();
|
.HasMany(u => u.Tokens)
|
||||||
modelBuilder.Entity<TokenEntry>();
|
.WithOne(t => t.Owner)
|
||||||
modelBuilder.Entity<GroupEntry>();
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
modelBuilder.Entity<User>()
|
||||||
/// Gets executed when a user is deleted through the IUserService from the
|
.HasMany(u => u.Permissions)
|
||||||
/// HopFrame.Security package. You can override this method to also delete
|
.WithOne(p => p.User)
|
||||||
/// related user specific entries in the database
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
/// </summary>
|
|
||||||
/// <param name="user"></param>
|
modelBuilder.Entity<PermissionGroup>()
|
||||||
public virtual void OnUserDelete(UserEntry user) {}
|
.HasMany(g => g.Permissions)
|
||||||
|
.WithOne(p => p.Group)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="8.0.8" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.7" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.7" />
|
||||||
</ItemGroup>
|
</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;
|
namespace HopFrame.Database.Models;
|
||||||
|
|
||||||
public sealed class Permission {
|
public class Permission {
|
||||||
|
|
||||||
|
[Key, Required, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
public long Id { get; init; }
|
public long Id { get; init; }
|
||||||
|
|
||||||
|
[Required, MaxLength(255)]
|
||||||
public string PermissionName { get; set; }
|
public string PermissionName { get; set; }
|
||||||
public Guid Owner { get; set; }
|
|
||||||
|
[Required]
|
||||||
public DateTime GrantedAt { get; set; }
|
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;
|
namespace HopFrame.Database.Models;
|
||||||
|
|
||||||
public class PermissionGroup : IPermissionOwner {
|
public class PermissionGroup : IPermissionOwner {
|
||||||
|
|
||||||
|
[Key, Required, MaxLength(50)]
|
||||||
public string Name { get; init; }
|
public string Name { get; init; }
|
||||||
|
|
||||||
|
[Required, DefaultValue(false)]
|
||||||
public bool IsDefaultGroup { get; set; }
|
public bool IsDefaultGroup { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(500)]
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
public DateTime CreatedAt { get; set; }
|
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;
|
||||||
|
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 RefreshTokenType = 0;
|
||||||
public const int AccessTokenType = 1;
|
public const int AccessTokenType = 1;
|
||||||
|
|
||||||
@@ -15,11 +17,11 @@ public class TokenEntry {
|
|||||||
public int Type { get; set; }
|
public int Type { get; set; }
|
||||||
|
|
||||||
[Key, Required, MinLength(36), MaxLength(36)]
|
[Key, Required, MinLength(36), MaxLength(36)]
|
||||||
public string Token { get; set; }
|
public Guid Content { get; set; }
|
||||||
|
|
||||||
[Required, MinLength(36), MaxLength(36)]
|
|
||||||
public string UserId { get; set; }
|
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
public DateTime CreatedAt { get; set; }
|
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;
|
namespace HopFrame.Database.Models;
|
||||||
|
|
||||||
public sealed class User : IPermissionOwner {
|
public class User : IPermissionOwner {
|
||||||
|
|
||||||
|
[Key, Required, MinLength(36), MaxLength(36)]
|
||||||
public Guid Id { get; init; }
|
public Guid Id { get; init; }
|
||||||
|
|
||||||
|
[MaxLength(50)]
|
||||||
public string Username { get; set; }
|
public string Username { get; set; }
|
||||||
|
|
||||||
|
[Required, MaxLength(50), EmailAddress]
|
||||||
public string Email { get; set; }
|
public string Email { get; set; }
|
||||||
|
|
||||||
|
[Required, MinLength(8), MaxLength(255), JsonIgnore]
|
||||||
|
public string Password { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
public DateTime CreatedAt { get; set; }
|
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; }
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace HopFrame.Security.Authorization;
|
namespace HopFrame.Database;
|
||||||
|
|
||||||
public static class PermissionValidator {
|
public static class PermissionValidator {
|
||||||
|
|
||||||
21
src/HopFrame.Database/Repositories/IGroupRepository.cs
Normal file
21
src/HopFrame.Database/Repositories/IGroupRepository.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using HopFrame.Database.Models;
|
||||||
|
|
||||||
|
namespace HopFrame.Database.Repositories;
|
||||||
|
|
||||||
|
public interface IGroupRepository {
|
||||||
|
Task<IList<PermissionGroup>> GetPermissionGroups();
|
||||||
|
|
||||||
|
Task<IList<PermissionGroup>> GetDefaultGroups();
|
||||||
|
|
||||||
|
Task<IList<PermissionGroup>> GetUserGroups(User user);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
using HopFrame.Database.Models;
|
using HopFrame.Database.Models;
|
||||||
using HopFrame.Security.Models;
|
|
||||||
|
|
||||||
namespace HopFrame.Security.Services;
|
namespace HopFrame.Database.Repositories;
|
||||||
|
|
||||||
public interface IUserService {
|
public interface IUserRepository {
|
||||||
Task<IList<User>> GetUsers();
|
Task<IList<User>> GetUsers();
|
||||||
|
|
||||||
Task<User> GetUser(Guid userId);
|
Task<User> GetUser(Guid userId);
|
||||||
@@ -12,13 +11,8 @@ public interface IUserService {
|
|||||||
|
|
||||||
Task<User> GetUserByUsername(string username);
|
Task<User> GetUserByUsername(string username);
|
||||||
|
|
||||||
Task<User> AddUser(UserRegister user);
|
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="IPermissionService"/>
|
|
||||||
/// </summary>
|
|
||||||
Task UpdateUser(User user);
|
Task UpdateUser(User user);
|
||||||
|
|
||||||
Task DeleteUser(User user);
|
Task DeleteUser(User user);
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
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 Task<IList<PermissionGroup>> GetUserGroups(User user) {
|
||||||
|
return Task.FromResult((IList<PermissionGroup>) context.Groups
|
||||||
|
.Include(g => g.Permissions)
|
||||||
|
.AsEnumerable()
|
||||||
|
.Where(g => user.Permissions.Any(p => p.PermissionName == g.Name))
|
||||||
|
.ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
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,113 @@
|
|||||||
|
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)
|
||||||
|
.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 ?? new List<Permission>(),
|
||||||
|
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,
|
||||||
|
GrantedAt = DateTime.Now
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
using HopFrame.Database;
|
using HopFrame.Database.Repositories;
|
||||||
using HopFrame.Security.Claims;
|
using HopFrame.Security.Claims;
|
||||||
using HopFrame.Security.Services;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
@@ -13,15 +11,15 @@ using Microsoft.Extensions.Options;
|
|||||||
|
|
||||||
namespace HopFrame.Security.Authentication;
|
namespace HopFrame.Security.Authentication;
|
||||||
|
|
||||||
public class HopFrameAuthentication<TDbContext>(
|
public class HopFrameAuthentication(
|
||||||
IOptionsMonitor<AuthenticationSchemeOptions> options,
|
IOptionsMonitor<AuthenticationSchemeOptions> options,
|
||||||
ILoggerFactory logger,
|
ILoggerFactory logger,
|
||||||
UrlEncoder encoder,
|
UrlEncoder encoder,
|
||||||
ISystemClock clock,
|
ISystemClock clock,
|
||||||
TDbContext context,
|
ITokenRepository tokens,
|
||||||
IPermissionService perms)
|
IUserRepository users,
|
||||||
: AuthenticationHandler<AuthenticationSchemeOptions>(options, logger, encoder, clock)
|
IPermissionRepository perms)
|
||||||
where TDbContext : HopDbContextBase {
|
: AuthenticationHandler<AuthenticationSchemeOptions>(options, logger, encoder, clock) {
|
||||||
|
|
||||||
public const string SchemeName = "HopCore.Authentication";
|
public const string SchemeName = "HopCore.Authentication";
|
||||||
public static readonly TimeSpan AccessTokenTime = new(0, 0, 5, 0);
|
public static readonly TimeSpan AccessTokenTime = new(0, 0, 5, 0);
|
||||||
@@ -33,20 +31,20 @@ public class HopFrameAuthentication<TDbContext>(
|
|||||||
if (string.IsNullOrEmpty(accessToken)) accessToken = Request.Headers["Token"];
|
if (string.IsNullOrEmpty(accessToken)) accessToken = Request.Headers["Token"];
|
||||||
if (string.IsNullOrEmpty(accessToken)) return AuthenticateResult.Fail("No Access Token provided");
|
if (string.IsNullOrEmpty(accessToken)) return AuthenticateResult.Fail("No Access Token provided");
|
||||||
|
|
||||||
var tokenEntry = await context.Tokens.SingleOrDefaultAsync(token => token.Token == accessToken);
|
var tokenEntry = await tokens.GetToken(accessToken);
|
||||||
|
|
||||||
if (tokenEntry is null) return AuthenticateResult.Fail("The provided Access Token does not exist");
|
if (tokenEntry is null) return AuthenticateResult.Fail("The provided Access Token does not exist");
|
||||||
if (tokenEntry.CreatedAt + AccessTokenTime < DateTime.Now) return AuthenticateResult.Fail("The provided Access Token is expired");
|
if (tokenEntry.CreatedAt + AccessTokenTime < DateTime.Now) return AuthenticateResult.Fail("The provided Access Token is expired");
|
||||||
|
|
||||||
if (!await context.Users.AnyAsync(user => user.Id == tokenEntry.UserId))
|
if (tokenEntry.Owner is null)
|
||||||
return AuthenticateResult.Fail("The provided Access Token does not match any user");
|
return AuthenticateResult.Fail("The provided Access Token does not match any user");
|
||||||
|
|
||||||
var claims = new List<Claim> {
|
var claims = new List<Claim> {
|
||||||
new(HopFrameClaimTypes.AccessTokenId, accessToken),
|
new(HopFrameClaimTypes.AccessTokenId, accessToken),
|
||||||
new(HopFrameClaimTypes.UserId, tokenEntry.UserId)
|
new(HopFrameClaimTypes.UserId, tokenEntry.Owner.Id.ToString())
|
||||||
};
|
};
|
||||||
|
|
||||||
var permissions = await perms.GetFullPermissions(tokenEntry.UserId);
|
var permissions = await perms.GetFullPermissions(tokenEntry.Owner);
|
||||||
claims.AddRange(permissions.Select(perm => new Claim(HopFrameClaimTypes.Permission, perm)));
|
claims.AddRange(permissions.Select(perm => new Claim(HopFrameClaimTypes.Permission, perm)));
|
||||||
|
|
||||||
var principal = new ClaimsPrincipal();
|
var principal = new ClaimsPrincipal();
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
using HopFrame.Database;
|
|
||||||
using HopFrame.Security.Claims;
|
using HopFrame.Security.Claims;
|
||||||
using HopFrame.Security.Services;
|
|
||||||
using HopFrame.Security.Services.Implementation;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
@@ -17,13 +14,11 @@ public static class HopFrameAuthenticationExtensions {
|
|||||||
/// <param name="service">The service provider to add the services to</param>
|
/// <param name="service">The service provider to add the services to</param>
|
||||||
/// <typeparam name="TDbContext">The database object that saves all entities that are important for the security api</typeparam>
|
/// <typeparam name="TDbContext">The database object that saves all entities that are important for the security api</typeparam>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static IServiceCollection AddHopFrameAuthentication<TDbContext>(this IServiceCollection service) where TDbContext : HopDbContextBase {
|
public static IServiceCollection AddHopFrameAuthentication(this IServiceCollection service) {
|
||||||
service.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
service.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||||
service.AddScoped<ITokenContext, TokenContextImplementor<TDbContext>>();
|
service.AddScoped<ITokenContext, TokenContextImplementor>();
|
||||||
service.AddScoped<IPermissionService, PermissionService<TDbContext>>();
|
|
||||||
service.AddScoped<IUserService, UserService<TDbContext>>();
|
|
||||||
|
|
||||||
service.AddAuthentication(HopFrameAuthentication<TDbContext>.SchemeName).AddScheme<AuthenticationSchemeOptions, HopFrameAuthentication<TDbContext>>(HopFrameAuthentication<TDbContext>.SchemeName, _ => {});
|
service.AddAuthentication(HopFrameAuthentication.SchemeName).AddScheme<AuthenticationSchemeOptions, HopFrameAuthentication>(HopFrameAuthentication.SchemeName, _ => {});
|
||||||
service.AddAuthorization();
|
service.AddAuthorization();
|
||||||
|
|
||||||
return service;
|
return service;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using HopFrame.Database;
|
||||||
using HopFrame.Security.Claims;
|
using HopFrame.Security.Claims;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Authorization;
|
using Microsoft.AspNetCore.Mvc.Authorization;
|
||||||
|
|||||||
@@ -20,5 +20,5 @@ public interface ITokenContext {
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The access token the user provided
|
/// The access token the user provided
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Guid AccessToken { get; }
|
Token AccessToken { get; }
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,13 @@
|
|||||||
using HopFrame.Database;
|
|
||||||
using HopFrame.Database.Models;
|
using HopFrame.Database.Models;
|
||||||
|
using HopFrame.Database.Repositories;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
namespace HopFrame.Security.Claims;
|
namespace HopFrame.Security.Claims;
|
||||||
|
|
||||||
internal sealed class TokenContextImplementor<TDbContext>(IHttpContextAccessor accessor, TDbContext context) : ITokenContext where TDbContext : HopDbContextBase {
|
internal sealed class TokenContextImplementor(IHttpContextAccessor accessor, IUserRepository users, ITokenRepository tokens) : ITokenContext {
|
||||||
public bool IsAuthenticated => !string.IsNullOrEmpty(accessor.HttpContext?.User.GetAccessTokenId());
|
public bool IsAuthenticated => !string.IsNullOrEmpty(accessor.HttpContext?.User.GetAccessTokenId());
|
||||||
|
|
||||||
public User User => context.Users
|
public User User => users.GetUser(Guid.Parse(accessor.HttpContext?.User.GetUserId() ?? Guid.Empty.ToString())).GetAwaiter().GetResult();
|
||||||
.SingleOrDefault(user => user.Id == accessor.HttpContext.User.GetUserId())?
|
|
||||||
.ToUserModel(context);
|
|
||||||
|
|
||||||
public Guid AccessToken => Guid.Parse(accessor.HttpContext?.User.GetAccessTokenId() ?? Guid.Empty.ToString());
|
public Token AccessToken => tokens.GetToken(accessor.HttpContext?.User.GetAccessTokenId()).GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
using HopFrame.Database.Models;
|
|
||||||
|
|
||||||
namespace HopFrame.Security.Services;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// permission system:<br/>
|
|
||||||
/// - "*" -> all rights<br/>
|
|
||||||
/// - "group.[name]" -> group member<br/>
|
|
||||||
/// - "[namespace].[name]" -> single permission<br/>
|
|
||||||
/// - "[namespace].*" -> all permissions in the namespace
|
|
||||||
/// </summary>
|
|
||||||
public interface IPermissionService {
|
|
||||||
|
|
||||||
Task<bool> HasPermission(string permission, Guid user);
|
|
||||||
|
|
||||||
Task<IList<PermissionGroup>> GetPermissionGroups();
|
|
||||||
|
|
||||||
Task<PermissionGroup> GetPermissionGroup(string name);
|
|
||||||
|
|
||||||
Task EditPermissionGroup(PermissionGroup group);
|
|
||||||
|
|
||||||
Task<IList<PermissionGroup>> GetUserPermissionGroups(User user);
|
|
||||||
|
|
||||||
Task RemoveGroupFromUser(User user, PermissionGroup group);
|
|
||||||
|
|
||||||
Task<PermissionGroup> CreatePermissionGroup(string name, bool isDefault = false, string description = null);
|
|
||||||
|
|
||||||
Task DeletePermissionGroup(PermissionGroup group);
|
|
||||||
|
|
||||||
Task<Permission> GetPermission(string name, IPermissionOwner owner);
|
|
||||||
|
|
||||||
/// <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 AddPermission(IPermissionOwner owner, string permission);
|
|
||||||
|
|
||||||
Task RemovePermission(Permission permission);
|
|
||||||
|
|
||||||
Task<string[]> GetFullPermissions(string user);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,178 +0,0 @@
|
|||||||
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>(TDbContext context, ITokenContext current) : IPermissionService where TDbContext : HopDbContextBase {
|
|
||||||
public async Task<bool> HasPermission(string permission) {
|
|
||||||
return await HasPermission(permission, current.User.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> 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<bool> 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<bool> HasPermission(string permission, Guid user) {
|
|
||||||
var permissions = await GetFullPermissions(user.ToString());
|
|
||||||
|
|
||||||
return PermissionValidator.IncludesPermission(permission, permissions);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IList<PermissionGroup>> GetPermissionGroups() {
|
|
||||||
return await context.Groups
|
|
||||||
.Select(group => group.ToPermissionGroup(context))
|
|
||||||
.ToListAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<PermissionGroup> GetPermissionGroup(string name) {
|
|
||||||
return context.Groups
|
|
||||||
.Where(group => group.Name == name)
|
|
||||||
.Select(group => group.ToPermissionGroup(context))
|
|
||||||
.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.Default = group.IsDefaultGroup;
|
|
||||||
entity.Entity.Description = group.Description;
|
|
||||||
|
|
||||||
await context.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IList<PermissionGroup>> GetUserPermissionGroups(User user) {
|
|
||||||
var groups = await context.Groups.ToListAsync();
|
|
||||||
var perms = await GetFullPermissions(user.Id.ToString());
|
|
||||||
|
|
||||||
return groups
|
|
||||||
.Where(group => perms.Contains(group.Name))
|
|
||||||
.Select(group => group.ToPermissionGroup(context))
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task RemoveGroupFromUser(User user, PermissionGroup group) {
|
|
||||||
var entry = await context.Permissions
|
|
||||||
.Where(perm => perm.PermissionText == group.Name && perm.UserId == user.Id.ToString())
|
|
||||||
.SingleOrDefaultAsync();
|
|
||||||
|
|
||||||
if (entry is null) return;
|
|
||||||
|
|
||||||
context.Permissions.Remove(entry);
|
|
||||||
await context.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<PermissionGroup> 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);
|
|
||||||
|
|
||||||
if (isDefault) {
|
|
||||||
var users = await context.Users.ToListAsync();
|
|
||||||
|
|
||||||
foreach (var user in users) {
|
|
||||||
await context.Permissions.AddAsync(new PermissionEntry {
|
|
||||||
GrantedAt = DateTime.Now,
|
|
||||||
PermissionText = group.Name,
|
|
||||||
UserId = user.Id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await context.SaveChangesAsync();
|
|
||||||
|
|
||||||
return group.ToPermissionGroup(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DeletePermissionGroup(PermissionGroup group) {
|
|
||||||
var entry = await context.Groups.SingleOrDefaultAsync(entry => entry.Name == group.Name);
|
|
||||||
context.Groups.Remove(entry);
|
|
||||||
|
|
||||||
var permissions = await context.Permissions
|
|
||||||
.Where(perm => perm.UserId == group.Name || perm.PermissionText == group.Name)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
if (permissions.Count > 0) {
|
|
||||||
context.Permissions.RemoveRange(permissions);
|
|
||||||
}
|
|
||||||
|
|
||||||
await context.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Permission> GetPermission(string name, IPermissionOwner owner) {
|
|
||||||
var ownerId = (owner is User user) ? user.Id.ToString() : ((PermissionGroup)owner).Name;
|
|
||||||
|
|
||||||
return await context.Permissions
|
|
||||||
.Where(perm => perm.PermissionText == name && perm.UserId == ownerId)
|
|
||||||
.Select(perm => perm.ToPermissionModel())
|
|
||||||
.SingleOrDefaultAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
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 RemovePermission(Permission permission) {
|
|
||||||
var entry = await context.Permissions.SingleOrDefaultAsync(entry => entry.RecordId == permission.Id);
|
|
||||||
context.Permissions.Remove(entry);
|
|
||||||
await context.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string[]> 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<string>();
|
|
||||||
foreach (var group in groups) {
|
|
||||||
var perms = await GetFullPermissions(group);
|
|
||||||
groupPerms.AddRange(perms);
|
|
||||||
}
|
|
||||||
|
|
||||||
permissions.AddRange(groupPerms);
|
|
||||||
|
|
||||||
return permissions.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
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>(TDbContext context) : IUserService where TDbContext : HopDbContextBase {
|
|
||||||
public async Task<IList<User>> GetUsers() {
|
|
||||||
return await context.Users
|
|
||||||
.Select(user => user.ToUserModel(context))
|
|
||||||
.ToListAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<User> GetUser(Guid userId) {
|
|
||||||
var id = userId.ToString();
|
|
||||||
|
|
||||||
return context.Users
|
|
||||||
.Where(user => user.Id == id)
|
|
||||||
.Select(user => user.ToUserModel(context))
|
|
||||||
.SingleOrDefaultAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<User> GetUserByEmail(string email) {
|
|
||||||
return context.Users
|
|
||||||
.Where(user => user.Email == email)
|
|
||||||
.Select(user => user.ToUserModel(context))
|
|
||||||
.SingleOrDefaultAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<User> GetUserByUsername(string username) {
|
|
||||||
return context.Users
|
|
||||||
.Where(user => user.Username == username)
|
|
||||||
.Select(user => user.ToUserModel(context))
|
|
||||||
.SingleOrDefaultAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<User> AddUser(UserRegister user) {
|
|
||||||
if (await GetUserByEmail(user.Email) is not null) return null;
|
|
||||||
if (await GetUserByUsername(user.Username) is not null) return null;
|
|
||||||
|
|
||||||
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 async Task<bool> CheckUserPassword(User user, string password) {
|
|
||||||
var id = user.Id.ToString();
|
|
||||||
var hash = EncryptionManager.Hash(password, Encoding.Default.GetBytes(user.CreatedAt.ToString(CultureInfo.InvariantCulture)));
|
|
||||||
|
|
||||||
var entry = await context.Users
|
|
||||||
.Where(entry => entry.Id == 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.ToString())
|
|
||||||
.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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using HopFrame.Database;
|
using HopFrame.Database.Repositories;
|
||||||
using HopFrame.Security.Authentication;
|
using HopFrame.Security.Authentication;
|
||||||
using HopFrame.Security.Claims;
|
using HopFrame.Security.Claims;
|
||||||
using HopFrame.Security.Services;
|
|
||||||
using HopFrame.Web.Services;
|
using HopFrame.Web.Services;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
namespace HopFrame.Web;
|
namespace HopFrame.Web;
|
||||||
|
|
||||||
public sealed class AuthMiddleware(IAuthService auth, IPermissionService perms) : IMiddleware {
|
public sealed class AuthMiddleware(IAuthService auth, IPermissionRepository perms) : IMiddleware {
|
||||||
public async Task InvokeAsync(HttpContext context, RequestDelegate next) {
|
public async Task InvokeAsync(HttpContext context, RequestDelegate next) {
|
||||||
var loggedIn = await auth.IsLoggedIn();
|
var loggedIn = await auth.IsLoggedIn();
|
||||||
|
|
||||||
@@ -20,14 +19,14 @@ public sealed class AuthMiddleware(IAuthService auth, IPermissionService perms)
|
|||||||
}
|
}
|
||||||
|
|
||||||
var claims = new List<Claim> {
|
var claims = new List<Claim> {
|
||||||
new(HopFrameClaimTypes.AccessTokenId, token.Token),
|
new(HopFrameClaimTypes.AccessTokenId, token.Content.ToString()),
|
||||||
new(HopFrameClaimTypes.UserId, token.UserId)
|
new(HopFrameClaimTypes.UserId, token.Owner.Id.ToString())
|
||||||
};
|
};
|
||||||
|
|
||||||
var permissions = await perms.GetFullPermissions(token.UserId);
|
var permissions = await perms.GetFullPermissions(token.Owner);
|
||||||
claims.AddRange(permissions.Select(perm => new Claim(HopFrameClaimTypes.Permission, perm)));
|
claims.AddRange(permissions.Select(perm => new Claim(HopFrameClaimTypes.Permission, perm)));
|
||||||
|
|
||||||
context.User.AddIdentity(new ClaimsIdentity(claims, HopFrameAuthentication<HopDbContextBase>.SchemeName));
|
context.User.AddIdentity(new ClaimsIdentity(claims, HopFrameAuthentication.SchemeName));
|
||||||
}
|
}
|
||||||
|
|
||||||
await next?.Invoke(context);
|
await next?.Invoke(context);
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
@using BlazorStrap.V5
|
@using BlazorStrap.V5
|
||||||
@using CurrieTechnologies.Razor.SweetAlert2
|
@using CurrieTechnologies.Razor.SweetAlert2
|
||||||
@using HopFrame.Database.Models
|
@using HopFrame.Database.Models
|
||||||
|
@using HopFrame.Database.Repositories
|
||||||
@using HopFrame.Security.Claims
|
@using HopFrame.Security.Claims
|
||||||
@using HopFrame.Security.Services
|
|
||||||
@using HopFrame.Web.Model
|
@using HopFrame.Web.Model
|
||||||
|
|
||||||
<BSModal DataId="add-group-modal" HideOnValidSubmit="true" IsStaticBackdrop="true" @ref="_modal">
|
<BSModal DataId="add-group-modal" HideOnValidSubmit="true" IsStaticBackdrop="true" @ref="_modal">
|
||||||
@@ -115,7 +115,8 @@
|
|||||||
</BSForm>
|
</BSForm>
|
||||||
</BSModal>
|
</BSModal>
|
||||||
|
|
||||||
@inject IPermissionService Permissions
|
@inject IGroupRepository Groups
|
||||||
|
@inject IPermissionRepository Permissions
|
||||||
@inject SweetAlertService Alerts
|
@inject SweetAlertService Alerts
|
||||||
@inject ITokenContext Context
|
@inject ITokenContext Context
|
||||||
|
|
||||||
@@ -133,7 +134,7 @@
|
|||||||
private bool _isEdit;
|
private bool _isEdit;
|
||||||
|
|
||||||
public async Task ShowAsync(PermissionGroup group = null) {
|
public async Task ShowAsync(PermissionGroup group = null) {
|
||||||
_allGroups = await Permissions.GetPermissionGroups();
|
_allGroups = await Groups.GetPermissionGroups();
|
||||||
|
|
||||||
if (group is not null) {
|
if (group is not null) {
|
||||||
_group = new PermissionGroupAdd {
|
_group = new PermissionGroupAdd {
|
||||||
@@ -167,7 +168,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_isEdit) {
|
if (_isEdit) {
|
||||||
if (!(await Permissions.HasPermission(Security.AdminPermissions.EditGroup, Context.User.Id))) {
|
if (!await Permissions.HasPermission(Context.User, Security.AdminPermissions.EditGroup)) {
|
||||||
await NoEditPermissions();
|
await NoEditPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -176,7 +177,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
_group.Permissions.Add(new Permission {
|
_group.Permissions.Add(new Permission {
|
||||||
PermissionName = _permissionToAdd
|
PermissionName = _permissionToAdd,
|
||||||
|
GrantedAt = DateTime.Now
|
||||||
});
|
});
|
||||||
|
|
||||||
_permissionToAdd = null;
|
_permissionToAdd = null;
|
||||||
@@ -184,8 +186,7 @@
|
|||||||
|
|
||||||
private async Task RemovePermission(Permission permission) {
|
private async Task RemovePermission(Permission permission) {
|
||||||
if (_isEdit) {
|
if (_isEdit) {
|
||||||
var perm = await Permissions.GetPermission(permission.PermissionName, _group);
|
await Permissions.RemovePermission(_group, permission.PermissionName);
|
||||||
await Permissions.RemovePermission(perm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_group.Permissions.Remove(permission);
|
_group.Permissions.Remove(permission);
|
||||||
@@ -202,7 +203,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_isEdit) {
|
if (_isEdit) {
|
||||||
if (!(await Permissions.HasPermission(Security.AdminPermissions.EditGroup, Context.User.Id))) {
|
if (!await Permissions.HasPermission(Context.User, Security.AdminPermissions.EditGroup)) {
|
||||||
await NoEditPermissions();
|
await NoEditPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -219,12 +220,12 @@
|
|||||||
|
|
||||||
private async Task AddGroup() {
|
private async Task AddGroup() {
|
||||||
if (_isEdit) {
|
if (_isEdit) {
|
||||||
if (!(await Permissions.HasPermission(Security.AdminPermissions.EditGroup, Context.User.Id))) {
|
if (!await Permissions.HasPermission(Context.User, Security.AdminPermissions.EditGroup)) {
|
||||||
await NoEditPermissions();
|
await NoEditPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Permissions.EditPermissionGroup(_group);
|
await Groups.EditPermissionGroup(_group);
|
||||||
|
|
||||||
if (ReloadPage is not null)
|
if (ReloadPage is not null)
|
||||||
await ReloadPage.Invoke();
|
await ReloadPage.Invoke();
|
||||||
@@ -239,7 +240,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(await Permissions.HasPermission(Security.AdminPermissions.AddGroup, Context.User.Id))) {
|
if (!await Permissions.HasPermission(Context.User, Security.AdminPermissions.AddGroup)) {
|
||||||
await NoAddPermissions();
|
await NoAddPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -255,11 +256,12 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var dbGroup = await Permissions.CreatePermissionGroup("group." + _group.GroupName, _group.IsDefaultGroup, _group.Description);
|
await Groups.CreatePermissionGroup(new PermissionGroup {
|
||||||
|
Description = _group.Description,
|
||||||
foreach (var permission in _group.Permissions) {
|
IsDefaultGroup = _group.IsDefaultGroup,
|
||||||
await Permissions.AddPermission(dbGroup, permission.PermissionName);
|
Permissions = _group.Permissions,
|
||||||
}
|
Name = "group." + _group.GroupName
|
||||||
|
});
|
||||||
|
|
||||||
if (ReloadPage is not null)
|
if (ReloadPage is not null)
|
||||||
await ReloadPage.Invoke();
|
await ReloadPage.Invoke();
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
@using BlazorStrap.V5
|
@using BlazorStrap.V5
|
||||||
@using CurrieTechnologies.Razor.SweetAlert2
|
@using CurrieTechnologies.Razor.SweetAlert2
|
||||||
@using HopFrame.Database.Models
|
@using HopFrame.Database.Models
|
||||||
|
@using HopFrame.Database.Repositories
|
||||||
@using HopFrame.Security.Claims
|
@using HopFrame.Security.Claims
|
||||||
@using HopFrame.Security.Services
|
|
||||||
@using HopFrame.Web.Model
|
@using HopFrame.Web.Model
|
||||||
|
|
||||||
<BSModal DataId="add-user-modal" HideOnValidSubmit="true" IsStaticBackdrop="true" OnShow="() => _user = new()" @ref="_modal">
|
<BSModal DataId="add-user-modal" HideOnValidSubmit="true" IsStaticBackdrop="true" OnShow="() => _user = new()" @ref="_modal">
|
||||||
@@ -47,8 +47,9 @@
|
|||||||
</BSForm>
|
</BSForm>
|
||||||
</BSModal>
|
</BSModal>
|
||||||
|
|
||||||
@inject IUserService Users
|
@inject IUserRepository Users
|
||||||
@inject IPermissionService Permissions
|
@inject IPermissionRepository Permissions
|
||||||
|
@inject IGroupRepository Groups
|
||||||
@inject SweetAlertService Alerts
|
@inject SweetAlertService Alerts
|
||||||
@inject ITokenContext Auth
|
@inject ITokenContext Auth
|
||||||
|
|
||||||
@@ -62,14 +63,14 @@
|
|||||||
private BSModalBase _modal;
|
private BSModalBase _modal;
|
||||||
|
|
||||||
public async Task ShowAsync() {
|
public async Task ShowAsync() {
|
||||||
_allGroups = await Permissions.GetPermissionGroups();
|
_allGroups = await Groups.GetPermissionGroups();
|
||||||
_allUsers = await Users.GetUsers();
|
_allUsers = await Users.GetUsers();
|
||||||
|
|
||||||
await _modal.ShowAsync();
|
await _modal.ShowAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AddUser() {
|
private async Task AddUser() {
|
||||||
if (!(await Permissions.HasPermission(Security.AdminPermissions.AddUser, Auth.User.Id))) {
|
if (!(await Permissions.HasPermission(Auth.User, Security.AdminPermissions.AddUser))) {
|
||||||
await NoAddPermissions();
|
await NoAddPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -104,7 +105,11 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = await Users.AddUser(_user);
|
var user = await Users.AddUser(new User {
|
||||||
|
Username = _user.Username,
|
||||||
|
Email = _user.Email,
|
||||||
|
Password = _user.Password
|
||||||
|
});
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(_user.Group)) {
|
if (!string.IsNullOrWhiteSpace(_user.Group)) {
|
||||||
await Permissions.AddPermission(user, _user.Group);
|
await Permissions.AddPermission(user, _user.Group);
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
@using BlazorStrap.V5
|
@using BlazorStrap.V5
|
||||||
@using CurrieTechnologies.Razor.SweetAlert2
|
@using CurrieTechnologies.Razor.SweetAlert2
|
||||||
@using HopFrame.Database.Models
|
@using HopFrame.Database.Models
|
||||||
|
@using HopFrame.Database.Repositories
|
||||||
@using HopFrame.Security.Claims
|
@using HopFrame.Security.Claims
|
||||||
@using HopFrame.Security.Services
|
|
||||||
@using HopFrame.Web.Model
|
@using HopFrame.Web.Model
|
||||||
|
|
||||||
<BSModal DataId="edit-user-modal" HideOnValidSubmit="true" IsStaticBackdrop="true" @ref="_modal">
|
<BSModal DataId="edit-user-modal" HideOnValidSubmit="true" IsStaticBackdrop="true" @ref="_modal">
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
<option selected>Select group</option>
|
<option selected>Select group</option>
|
||||||
|
|
||||||
@foreach (var group in _allGroups) {
|
@foreach (var group in _allGroups) {
|
||||||
@if (_userGroups.All(g => g.Name != group.Name)) {
|
@if (_userGroups?.All(g => g.Name != group.Name) == true) {
|
||||||
<option value="@group.Name">@group.Name.Replace("group.", "")</option>
|
<option value="@group.Name">@group.Name.Replace("group.", "")</option>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,8 +100,9 @@
|
|||||||
</BSForm>
|
</BSForm>
|
||||||
</BSModal>
|
</BSModal>
|
||||||
|
|
||||||
@inject IUserService Users
|
@inject IUserRepository Users
|
||||||
@inject IPermissionService Permissions
|
@inject IPermissionRepository Permissions
|
||||||
|
@inject IGroupRepository Groups
|
||||||
@inject SweetAlertService Alerts
|
@inject SweetAlertService Alerts
|
||||||
@inject ITokenContext Auth
|
@inject ITokenContext Auth
|
||||||
|
|
||||||
@@ -118,19 +119,19 @@
|
|||||||
private string _permissionToAdd;
|
private string _permissionToAdd;
|
||||||
|
|
||||||
public async Task ShowAsync(User user) {
|
public async Task ShowAsync(User user) {
|
||||||
if (!(await Permissions.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id))) {
|
if (!await Permissions.HasPermission(Auth.User, Security.AdminPermissions.EditUser)) {
|
||||||
await NoEditPermissions();
|
await NoEditPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_user = user;
|
_user = user;
|
||||||
_userGroups = await Permissions.GetUserPermissionGroups(_user);
|
_userGroups = await Groups.GetUserGroups(user);
|
||||||
_allGroups = await Permissions.GetPermissionGroups();
|
_allGroups = await Groups.GetPermissionGroups();
|
||||||
await _modal.ShowAsync();
|
await _modal.ShowAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AddGroup() {
|
private async Task AddGroup() {
|
||||||
if (!(await Permissions.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id))) {
|
if (!await Permissions.HasPermission(Auth.User, Security.AdminPermissions.EditUser)) {
|
||||||
await NoEditPermissions();
|
await NoEditPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -158,7 +159,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task RemoveGroup(PermissionGroup group) {
|
private async Task RemoveGroup(PermissionGroup group) {
|
||||||
if (!(await Permissions.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id))) {
|
if (!await Permissions.HasPermission(Auth.User, Security.AdminPermissions.EditUser)) {
|
||||||
await NoEditPermissions();
|
await NoEditPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -172,7 +173,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (result.IsConfirmed) {
|
if (result.IsConfirmed) {
|
||||||
await Permissions.RemoveGroupFromUser(_user, group);
|
await Permissions.RemovePermission(_user, group.Name);
|
||||||
_userGroups.Remove(group);
|
_userGroups.Remove(group);
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
|
|
||||||
@@ -186,7 +187,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task AddPermission() {
|
private async Task AddPermission() {
|
||||||
if (!(await Permissions.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id))) {
|
if (!await Permissions.HasPermission(Auth.User, Security.AdminPermissions.EditUser)) {
|
||||||
await NoEditPermissions();
|
await NoEditPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -200,8 +201,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await Permissions.AddPermission(_user, _permissionToAdd);
|
_user.Permissions.Add(await Permissions.AddPermission(_user, _permissionToAdd));
|
||||||
_user.Permissions.Add(await Permissions.GetPermission(_permissionToAdd, _user));
|
|
||||||
_permissionToAdd = "";
|
_permissionToAdd = "";
|
||||||
|
|
||||||
await Alerts.FireAsync(new SweetAlertOptions {
|
await Alerts.FireAsync(new SweetAlertOptions {
|
||||||
@@ -213,7 +213,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task RemovePermission(Permission perm) {
|
private async Task RemovePermission(Permission perm) {
|
||||||
if (!(await Permissions.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id))) {
|
if (!await Permissions.HasPermission(Auth.User, Security.AdminPermissions.EditUser)) {
|
||||||
await NoEditPermissions();
|
await NoEditPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -227,7 +227,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (result.IsConfirmed) {
|
if (result.IsConfirmed) {
|
||||||
await Permissions.RemovePermission(perm);
|
await Permissions.RemovePermission(perm.User, perm.PermissionName);
|
||||||
_user.Permissions.Remove(perm);
|
_user.Permissions.Remove(perm);
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
|
|
||||||
@@ -241,7 +241,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async void EditUser() {
|
private async void EditUser() {
|
||||||
if (!(await Permissions.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id))) {
|
if (!await Permissions.HasPermission(Auth.User, Security.AdminPermissions.EditUser)) {
|
||||||
await NoEditPermissions();
|
await NoEditPermissions();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@using HopFrame.Security.Authorization
|
@using HopFrame.Database
|
||||||
@using HopFrame.Security.Claims
|
@using HopFrame.Security.Claims
|
||||||
@using Microsoft.AspNetCore.Http
|
@using Microsoft.AspNetCore.Http
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
.field-wrapper {
|
.field-wrapper {
|
||||||
margin-top: 25vh;
|
margin-top: 25vh;
|
||||||
min-width: 30vw;
|
min-width: 500px;
|
||||||
|
|
||||||
padding: 30px;
|
padding: 30px;
|
||||||
border: 2px solid #ced4da;
|
border: 2px solid #ced4da;
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
@using BlazorStrap.V5
|
@using BlazorStrap.V5
|
||||||
@using CurrieTechnologies.Razor.SweetAlert2
|
@using CurrieTechnologies.Razor.SweetAlert2
|
||||||
@using HopFrame.Database.Models
|
@using HopFrame.Database.Models
|
||||||
|
@using HopFrame.Database.Repositories
|
||||||
@using HopFrame.Security.Claims
|
@using HopFrame.Security.Claims
|
||||||
@using HopFrame.Security.Services
|
|
||||||
@using HopFrame.Web.Pages.Administration.Layout
|
@using HopFrame.Web.Pages.Administration.Layout
|
||||||
|
|
||||||
<PageTitle>Groups</PageTitle>
|
<PageTitle>Groups</PageTitle>
|
||||||
@@ -94,7 +94,8 @@
|
|||||||
</BSTBody>
|
</BSTBody>
|
||||||
</BSTable>
|
</BSTable>
|
||||||
|
|
||||||
@inject IPermissionService Permissions
|
@inject IGroupRepository Groups
|
||||||
|
@inject IPermissionRepository Permissions
|
||||||
@inject ITokenContext Auth
|
@inject ITokenContext Auth
|
||||||
@inject SweetAlertService Alerts
|
@inject SweetAlertService Alerts
|
||||||
|
|
||||||
@@ -110,23 +111,23 @@
|
|||||||
private GroupAddModal _groupAddModal;
|
private GroupAddModal _groupAddModal;
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync() {
|
protected override async Task OnInitializedAsync() {
|
||||||
_groups = await Permissions.GetPermissionGroups();
|
_groups = await Groups.GetPermissionGroups();
|
||||||
|
|
||||||
_hasEditPrivileges = await Permissions.HasPermission(Security.AdminPermissions.EditGroup, Auth.User.Id);
|
_hasEditPrivileges = await Permissions.HasPermission(Auth.User, Security.AdminPermissions.EditGroup);
|
||||||
_hasDeletePrivileges = await Permissions.HasPermission(Security.AdminPermissions.DeleteGroup, Auth.User.Id);
|
_hasDeletePrivileges = await Permissions.HasPermission(Auth.User, Security.AdminPermissions.DeleteGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Reload() {
|
private async Task Reload() {
|
||||||
_groups = new List<PermissionGroup>();
|
_groups = new List<PermissionGroup>();
|
||||||
|
|
||||||
_groups = await Permissions.GetPermissionGroups();
|
_groups = await Groups.GetPermissionGroups();
|
||||||
|
|
||||||
OrderBy(_currentOrder, false);
|
OrderBy(_currentOrder, false);
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Search() {
|
private async Task Search() {
|
||||||
var groups = await Permissions.GetPermissionGroups();
|
var groups = await Groups.GetPermissionGroups();
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(_searchText)) {
|
if (!string.IsNullOrWhiteSpace(_searchText)) {
|
||||||
groups = groups
|
groups = groups
|
||||||
@@ -166,7 +167,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (result.IsConfirmed) {
|
if (result.IsConfirmed) {
|
||||||
await Permissions.DeletePermissionGroup(group);
|
await Groups.DeletePermissionGroup(group);
|
||||||
await Reload();
|
await Reload();
|
||||||
|
|
||||||
await Alerts.FireAsync(new SweetAlertOptions {
|
await Alerts.FireAsync(new SweetAlertOptions {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
<BSNavbarToggle/>
|
<BSNavbarToggle/>
|
||||||
</Toggler>
|
</Toggler>
|
||||||
<Content>
|
<Content>
|
||||||
<BSNav MarginEnd="Margins.Auto" MarginBottom="Margins.Small" Class="mb-lg-0">
|
<BSNav MarginEnd="Margins.Auto" Class="mb-lg-0">
|
||||||
<BSNavItem IsActive="IsDashboardActive()" OnClick="NavigateToDashboard">Dashboard</BSNavItem>
|
<BSNavItem IsActive="IsDashboardActive()" OnClick="NavigateToDashboard">Dashboard</BSNavItem>
|
||||||
|
|
||||||
@foreach (var nav in Subpages) {
|
@foreach (var nav in Subpages) {
|
||||||
|
|||||||
@@ -7,12 +7,12 @@
|
|||||||
@using CurrieTechnologies.Razor.SweetAlert2
|
@using CurrieTechnologies.Razor.SweetAlert2
|
||||||
@using HopFrame.Database.Models
|
@using HopFrame.Database.Models
|
||||||
@using HopFrame.Security.Claims
|
@using HopFrame.Security.Claims
|
||||||
@using HopFrame.Security.Services
|
|
||||||
@using HopFrame.Web.Pages.Administration.Layout
|
@using HopFrame.Web.Pages.Administration.Layout
|
||||||
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||||
@using Microsoft.AspNetCore.Components.Web
|
@using Microsoft.AspNetCore.Components.Web
|
||||||
@using HopFrame.Web.Components
|
@using HopFrame.Web.Components
|
||||||
@using BlazorStrap.V5
|
@using BlazorStrap.V5
|
||||||
|
@using HopFrame.Database.Repositories
|
||||||
@using HopFrame.Web.Components.Administration
|
@using HopFrame.Web.Components.Administration
|
||||||
|
|
||||||
<PageTitle>Users</PageTitle>
|
<PageTitle>Users</PageTitle>
|
||||||
@@ -95,8 +95,9 @@
|
|||||||
</BSTBody>
|
</BSTBody>
|
||||||
</BSTable>
|
</BSTable>
|
||||||
|
|
||||||
@inject IUserService UserService
|
@inject IUserRepository UserService
|
||||||
@inject IPermissionService PermissionsService
|
@inject IPermissionRepository PermissionsService
|
||||||
|
@inject IGroupRepository Groups
|
||||||
@inject SweetAlertService Alerts
|
@inject SweetAlertService Alerts
|
||||||
@inject ITokenContext Auth
|
@inject ITokenContext Auth
|
||||||
|
|
||||||
@@ -119,12 +120,12 @@
|
|||||||
_users = await UserService.GetUsers();
|
_users = await UserService.GetUsers();
|
||||||
|
|
||||||
foreach (var user in _users) {
|
foreach (var user in _users) {
|
||||||
var groups = await PermissionsService.GetUserPermissionGroups(user);
|
var groups = await Groups.GetUserGroups(user);
|
||||||
_userGroups.Add(user.Id, groups.LastOrDefault());
|
_userGroups.Add(user.Id, groups.LastOrDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
_hasEditPrivileges = await PermissionsService.HasPermission(Security.AdminPermissions.EditUser, Auth.User.Id);
|
_hasEditPrivileges = await PermissionsService.HasPermission(Auth.User, Security.AdminPermissions.EditUser);
|
||||||
_hasDeletePrivileges = await PermissionsService.HasPermission(Security.AdminPermissions.DeleteUser, Auth.User.Id);
|
_hasDeletePrivileges = await PermissionsService.HasPermission(Auth.User, Security.AdminPermissions.DeleteUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Reload() {
|
private async Task Reload() {
|
||||||
@@ -134,7 +135,7 @@
|
|||||||
_users = await UserService.GetUsers();
|
_users = await UserService.GetUsers();
|
||||||
|
|
||||||
foreach (var user in _users) {
|
foreach (var user in _users) {
|
||||||
var groups = await PermissionsService.GetUserPermissionGroups(user);
|
var groups = await Groups.GetUserGroups(user);
|
||||||
_userGroups.Add(user.Id, groups.LastOrDefault());
|
_userGroups.Add(user.Id, groups.LastOrDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,14 +12,15 @@ namespace HopFrame.Web;
|
|||||||
public static class ServiceCollectionExtensions {
|
public static class ServiceCollectionExtensions {
|
||||||
public static IServiceCollection AddHopFrame<TDbContext>(this IServiceCollection services) where TDbContext : HopDbContextBase {
|
public static IServiceCollection AddHopFrame<TDbContext>(this IServiceCollection services) where TDbContext : HopDbContextBase {
|
||||||
services.AddHttpClient();
|
services.AddHttpClient();
|
||||||
services.AddScoped<IAuthService, AuthService<TDbContext>>();
|
services.AddHopFrameRepositories<TDbContext>();
|
||||||
|
services.AddScoped<IAuthService, AuthService>();
|
||||||
services.AddTransient<AuthMiddleware>();
|
services.AddTransient<AuthMiddleware>();
|
||||||
|
|
||||||
// Component library's
|
// Component library's
|
||||||
services.AddSweetAlert2();
|
services.AddSweetAlert2();
|
||||||
services.AddBlazorStrap();
|
services.AddBlazorStrap();
|
||||||
|
|
||||||
services.AddHopFrameAuthentication<TDbContext>();
|
services.AddHopFrameAuthentication();
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using HopFrame.Database.Models.Entries;
|
using HopFrame.Database.Models;
|
||||||
using HopFrame.Security.Models;
|
using HopFrame.Security.Models;
|
||||||
|
|
||||||
namespace HopFrame.Web.Services;
|
namespace HopFrame.Web.Services;
|
||||||
@@ -8,6 +8,6 @@ public interface IAuthService {
|
|||||||
Task<bool> Login(UserLogin login);
|
Task<bool> Login(UserLogin login);
|
||||||
Task Logout();
|
Task Logout();
|
||||||
|
|
||||||
Task<TokenEntry> RefreshLogin();
|
Task<Token> RefreshLogin();
|
||||||
Task<bool> IsLoggedIn();
|
Task<bool> IsLoggedIn();
|
||||||
}
|
}
|
||||||
@@ -1,47 +1,38 @@
|
|||||||
using HopFrame.Database;
|
using HopFrame.Database.Models;
|
||||||
using HopFrame.Database.Models.Entries;
|
using HopFrame.Database.Repositories;
|
||||||
using HopFrame.Security.Authentication;
|
using HopFrame.Security.Authentication;
|
||||||
using HopFrame.Security.Claims;
|
using HopFrame.Security.Claims;
|
||||||
using HopFrame.Security.Models;
|
using HopFrame.Security.Models;
|
||||||
using HopFrame.Security.Services;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace HopFrame.Web.Services.Implementation;
|
namespace HopFrame.Web.Services.Implementation;
|
||||||
|
|
||||||
internal class AuthService<TDbContext>(
|
internal class AuthService(
|
||||||
IUserService userService,
|
IUserRepository userService,
|
||||||
IHttpContextAccessor httpAccessor,
|
IHttpContextAccessor httpAccessor,
|
||||||
TDbContext context)
|
ITokenRepository tokens,
|
||||||
: IAuthService where TDbContext : HopDbContextBase {
|
ITokenContext context)
|
||||||
|
: IAuthService {
|
||||||
|
|
||||||
public async Task Register(UserRegister register) {
|
public async Task Register(UserRegister register) {
|
||||||
var user = await userService.AddUser(register);
|
var user = await userService.AddUser(new User {
|
||||||
|
Username = register.Username,
|
||||||
|
Email = register.Email,
|
||||||
|
Password = register.Password
|
||||||
|
});
|
||||||
|
|
||||||
if (user is null) return;
|
if (user is null) return;
|
||||||
|
|
||||||
var refreshToken = new TokenEntry {
|
var refreshToken = await tokens.CreateToken(Token.RefreshTokenType, user);
|
||||||
CreatedAt = DateTime.Now,
|
var accessToken = await tokens.CreateToken(Token.AccessTokenType, user);
|
||||||
Token = Guid.NewGuid().ToString(),
|
|
||||||
Type = TokenEntry.RefreshTokenType,
|
|
||||||
UserId = user.Id.ToString()
|
|
||||||
};
|
|
||||||
var accessToken = new TokenEntry {
|
|
||||||
CreatedAt = DateTime.Now,
|
|
||||||
Token = Guid.NewGuid().ToString(),
|
|
||||||
Type = TokenEntry.AccessTokenType,
|
|
||||||
UserId = user.Id.ToString()
|
|
||||||
};
|
|
||||||
|
|
||||||
context.Tokens.AddRange(refreshToken, accessToken);
|
httpAccessor.HttpContext?.Response.Cookies.Append(ITokenContext.RefreshTokenType, refreshToken.Content.ToString(), new CookieOptions {
|
||||||
await context.SaveChangesAsync();
|
MaxAge = HopFrameAuthentication.RefreshTokenTime,
|
||||||
|
|
||||||
httpAccessor.HttpContext?.Response.Cookies.Append(ITokenContext.RefreshTokenType, refreshToken.Token, new CookieOptions {
|
|
||||||
MaxAge = HopFrameAuthentication<HopDbContextBase>.RefreshTokenTime,
|
|
||||||
HttpOnly = true,
|
HttpOnly = true,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
httpAccessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Token, new CookieOptions {
|
httpAccessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Content.ToString(), new CookieOptions {
|
||||||
MaxAge = HopFrameAuthentication<TDbContext>.AccessTokenTime,
|
MaxAge = HopFrameAuthentication.AccessTokenTime,
|
||||||
HttpOnly = false,
|
HttpOnly = false,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
@@ -53,29 +44,16 @@ internal class AuthService<TDbContext>(
|
|||||||
if (user == null) return false;
|
if (user == null) return false;
|
||||||
if (await userService.CheckUserPassword(user, login.Password) == false) return false;
|
if (await userService.CheckUserPassword(user, login.Password) == false) return false;
|
||||||
|
|
||||||
var refreshToken = new TokenEntry {
|
var refreshToken = await tokens.CreateToken(Token.RefreshTokenType, user);
|
||||||
CreatedAt = DateTime.Now,
|
var accessToken = await tokens.CreateToken(Token.AccessTokenType, user);
|
||||||
Token = Guid.NewGuid().ToString(),
|
|
||||||
Type = TokenEntry.RefreshTokenType,
|
|
||||||
UserId = user.Id.ToString()
|
|
||||||
};
|
|
||||||
var accessToken = new TokenEntry {
|
|
||||||
CreatedAt = DateTime.Now,
|
|
||||||
Token = Guid.NewGuid().ToString(),
|
|
||||||
Type = TokenEntry.AccessTokenType,
|
|
||||||
UserId = user.Id.ToString()
|
|
||||||
};
|
|
||||||
|
|
||||||
context.Tokens.AddRange(refreshToken, accessToken);
|
httpAccessor.HttpContext?.Response.Cookies.Append(ITokenContext.RefreshTokenType, refreshToken.Content.ToString(), new CookieOptions {
|
||||||
await context.SaveChangesAsync();
|
MaxAge = HopFrameAuthentication.RefreshTokenTime,
|
||||||
|
|
||||||
httpAccessor.HttpContext?.Response.Cookies.Append(ITokenContext.RefreshTokenType, refreshToken.Token, new CookieOptions {
|
|
||||||
MaxAge = HopFrameAuthentication<HopDbContextBase>.RefreshTokenTime,
|
|
||||||
HttpOnly = true,
|
HttpOnly = true,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
httpAccessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Token, new CookieOptions {
|
httpAccessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Content.ToString(), new CookieOptions {
|
||||||
MaxAge = HopFrameAuthentication<TDbContext>.AccessTokenTime,
|
MaxAge = HopFrameAuthentication.AccessTokenTime,
|
||||||
HttpOnly = false,
|
HttpOnly = false,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
@@ -84,67 +62,27 @@ internal class AuthService<TDbContext>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task Logout() {
|
public async Task Logout() {
|
||||||
var accessToken = httpAccessor.HttpContext?.Request.Cookies[ITokenContext.AccessTokenType];
|
await tokens.DeleteUserTokens(context.User);
|
||||||
var refreshToken = httpAccessor.HttpContext?.Request.Cookies[ITokenContext.RefreshTokenType];
|
|
||||||
|
|
||||||
var tokenEntries = await context.Tokens.Where(token =>
|
|
||||||
(token.Token == accessToken && token.Type == TokenEntry.AccessTokenType) ||
|
|
||||||
(token.Token == refreshToken && token.Type == TokenEntry.RefreshTokenType))
|
|
||||||
.ToArrayAsync();
|
|
||||||
|
|
||||||
context.Tokens.Remove(tokenEntries[0]);
|
|
||||||
context.Tokens.Remove(tokenEntries[1]);
|
|
||||||
await context.SaveChangesAsync();
|
|
||||||
|
|
||||||
httpAccessor.HttpContext?.Response.Cookies.Delete(ITokenContext.RefreshTokenType);
|
httpAccessor.HttpContext?.Response.Cookies.Delete(ITokenContext.RefreshTokenType);
|
||||||
httpAccessor.HttpContext?.Response.Cookies.Delete(ITokenContext.AccessTokenType);
|
httpAccessor.HttpContext?.Response.Cookies.Delete(ITokenContext.AccessTokenType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<TokenEntry> RefreshLogin() {
|
public async Task<Token> RefreshLogin() {
|
||||||
var refreshToken = httpAccessor.HttpContext?.Request.Cookies[ITokenContext.RefreshTokenType];
|
var refreshToken = httpAccessor.HttpContext?.Request.Cookies[ITokenContext.RefreshTokenType];
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(refreshToken)) return null;
|
if (string.IsNullOrWhiteSpace(refreshToken)) return null;
|
||||||
|
|
||||||
var token = await context.Tokens.SingleOrDefaultAsync(token => token.Token == refreshToken && token.Type == TokenEntry.RefreshTokenType);
|
var token = await tokens.GetToken(refreshToken);
|
||||||
|
|
||||||
if (token is null) return null;
|
if (token is null || token.Type != Token.RefreshTokenType) return null;
|
||||||
|
|
||||||
var oldAccessTokens = context.Tokens
|
if (token.CreatedAt + HopFrameAuthentication.RefreshTokenTime < DateTime.Now) return null;
|
||||||
.AsEnumerable()
|
|
||||||
.Where(old =>
|
|
||||||
old.Type == TokenEntry.AccessTokenType &&
|
|
||||||
old.UserId == token.UserId &&
|
|
||||||
old.CreatedAt + HopFrameAuthentication<TDbContext>.AccessTokenTime < DateTime.Now)
|
|
||||||
.ToList();
|
|
||||||
if (oldAccessTokens.Count != 0)
|
|
||||||
context.Tokens.RemoveRange(oldAccessTokens);
|
|
||||||
|
|
||||||
var oldRefreshTokens = context.Tokens
|
var accessToken = await tokens.CreateToken(Token.AccessTokenType, token.Owner);
|
||||||
.AsEnumerable()
|
|
||||||
.Where(old =>
|
|
||||||
old.Type == TokenEntry.RefreshTokenType &&
|
|
||||||
old.UserId == token.UserId &&
|
|
||||||
old.CreatedAt + HopFrameAuthentication<TDbContext>.RefreshTokenTime < DateTime.Now)
|
|
||||||
.ToList();
|
|
||||||
if (oldRefreshTokens.Count != 0)
|
|
||||||
context.Tokens.RemoveRange(oldRefreshTokens);
|
|
||||||
|
|
||||||
await context.SaveChangesAsync();
|
httpAccessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Content.ToString(), new CookieOptions {
|
||||||
|
MaxAge = HopFrameAuthentication.AccessTokenTime,
|
||||||
if (token.CreatedAt + HopFrameAuthentication<TDbContext>.RefreshTokenTime < DateTime.Now) return null;
|
|
||||||
|
|
||||||
var accessToken = new TokenEntry {
|
|
||||||
CreatedAt = DateTime.Now,
|
|
||||||
Token = Guid.NewGuid().ToString(),
|
|
||||||
Type = TokenEntry.AccessTokenType,
|
|
||||||
UserId = token.UserId
|
|
||||||
};
|
|
||||||
|
|
||||||
await context.Tokens.AddAsync(accessToken);
|
|
||||||
await context.SaveChangesAsync();
|
|
||||||
|
|
||||||
httpAccessor.HttpContext?.Response.Cookies.Append(ITokenContext.AccessTokenType, accessToken.Token, new CookieOptions {
|
|
||||||
MaxAge = HopFrameAuthentication<TDbContext>.AccessTokenTime,
|
|
||||||
HttpOnly = false,
|
HttpOnly = false,
|
||||||
Secure = true
|
Secure = true
|
||||||
});
|
});
|
||||||
@@ -156,11 +94,12 @@ internal class AuthService<TDbContext>(
|
|||||||
var accessToken = httpAccessor.HttpContext?.Request.Cookies[ITokenContext.AccessTokenType];
|
var accessToken = httpAccessor.HttpContext?.Request.Cookies[ITokenContext.AccessTokenType];
|
||||||
if (string.IsNullOrEmpty(accessToken)) return false;
|
if (string.IsNullOrEmpty(accessToken)) return false;
|
||||||
|
|
||||||
var tokenEntry = await context.Tokens.SingleOrDefaultAsync(token => token.Token == accessToken);
|
var tokenEntry = await tokens.GetToken(accessToken);
|
||||||
|
|
||||||
if (tokenEntry is null) return false;
|
if (tokenEntry is null) return false;
|
||||||
if (tokenEntry.CreatedAt + HopFrameAuthentication<TDbContext>.AccessTokenTime < DateTime.Now) return false;
|
if (tokenEntry.Type != Token.AccessTokenType) return false;
|
||||||
if (!await context.Users.AnyAsync(user => user.Id == tokenEntry.UserId)) return false;
|
if (tokenEntry.CreatedAt + HopFrameAuthentication.AccessTokenTime < DateTime.Now) return false;
|
||||||
|
if (tokenEntry.Owner is null) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,54 @@
|
|||||||
|
using HopFrame.Api.Logic;
|
||||||
using HopFrame.Database.Models;
|
using HopFrame.Database.Models;
|
||||||
using HopFrame.Security.Authorization;
|
using HopFrame.Security.Authorization;
|
||||||
using HopFrame.Security.Claims;
|
using HopFrame.Security.Claims;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using RestApiTest.Models;
|
||||||
|
|
||||||
namespace RestApiTest.Controllers;
|
namespace RestApiTest.Controllers;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("test")]
|
[Route("test")]
|
||||||
public class TestController(ITokenContext userContext) : ControllerBase {
|
public class TestController(ITokenContext userContext, DatabaseContext context) : ControllerBase {
|
||||||
|
|
||||||
[HttpGet("permissions"), Authorized]
|
[HttpGet("permissions"), Authorized]
|
||||||
public ActionResult<IList<Permission>> Permissions() {
|
public ActionResult<IList<Permission>> Permissions() {
|
||||||
return new ActionResult<IList<Permission>>(userContext.User.Permissions);
|
return new ActionResult<IList<Permission>>(userContext.User.Permissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("generate")]
|
||||||
|
public async Task<ActionResult> GenerateData() {
|
||||||
|
var employee = new Employee() {
|
||||||
|
Name = "Max Mustermann"
|
||||||
|
};
|
||||||
|
|
||||||
|
await context.AddAsync(employee);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
|
var address = new Address() {
|
||||||
|
City = "Musterstadt",
|
||||||
|
Country = "Musterland",
|
||||||
|
State = "Musterbundesland",
|
||||||
|
ZipCode = 12345,
|
||||||
|
AddressDetails = "Musterstraße 5",
|
||||||
|
Employee = employee
|
||||||
|
};
|
||||||
|
|
||||||
|
await context.AddAsync(address);
|
||||||
|
await context.SaveChangesAsync();
|
||||||
|
|
||||||
|
return LogicResult.Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("employees")]
|
||||||
|
public async Task<ActionResult<IList<Employee>>> GetEmployees() {
|
||||||
|
return LogicResult<IList<Employee>>.Ok(await context.Employees.Include(e => e.Address).ToListAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("addresses")]
|
||||||
|
public async Task<ActionResult<IList<Address>>> GetAddresses() {
|
||||||
|
return LogicResult<IList<Address>>.Ok(await context.Addresses.Include(e => e.Employee).ToListAsync());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,25 @@
|
|||||||
using HopFrame.Database;
|
using HopFrame.Database;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using RestApiTest.Models;
|
||||||
|
|
||||||
namespace RestApiTest;
|
namespace RestApiTest;
|
||||||
|
|
||||||
public class DatabaseContext : HopDbContextBase {
|
public class DatabaseContext : HopDbContextBase {
|
||||||
|
|
||||||
|
public DbSet<Employee> Employees { get; set; }
|
||||||
|
public DbSet<Address> Addresses { get; set; }
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
|
||||||
base.OnConfiguring(optionsBuilder);
|
base.OnConfiguring(optionsBuilder);
|
||||||
|
|
||||||
optionsBuilder.UseSqlite("Data Source=C:\\Users\\Remote\\Documents\\Projekte\\HopFrame\\test\\RestApiTest\\bin\\Debug\\net8.0\\test.db;Mode=ReadWrite;");
|
optionsBuilder.UseSqlite("Data Source=C:\\Users\\Remote\\Documents\\Projekte\\HopFrame\\test\\RestApiTest\\bin\\Debug\\net8.0\\test.db;Mode=ReadWrite;");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder) {
|
||||||
|
base.OnModelCreating(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity<Employee>()
|
||||||
|
.HasOne(e => e.Address)
|
||||||
|
.WithOne(a => a.Employee);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
18
test/RestApiTest/Models/Address.cs
Normal file
18
test/RestApiTest/Models/Address.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
|
||||||
|
namespace RestApiTest.Models;
|
||||||
|
|
||||||
|
public class Address {
|
||||||
|
[ForeignKey("Employee")]
|
||||||
|
public int AddressId { get; set; }
|
||||||
|
public string AddressDetails { get; set; }
|
||||||
|
public string City { get; set; }
|
||||||
|
public int ZipCode { get; set; }
|
||||||
|
public string State { get; set; }
|
||||||
|
public string Country { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public virtual Employee Employee { get; set; }
|
||||||
|
}
|
||||||
8
test/RestApiTest/Models/Employee.cs
Normal file
8
test/RestApiTest/Models/Employee.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace RestApiTest.Models;
|
||||||
|
|
||||||
|
public class Employee {
|
||||||
|
public int EmployeeId { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public virtual Address Address { get; set; }
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>disable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user