PlotDirector/PlotLine/Data/AuthRepositories.cs
2026-06-06 17:21:58 +01:00

188 lines
7.7 KiB
C#

using System.Data;
using Dapper;
using PlotLine.Models;
namespace PlotLine.Data;
public interface IUserRepository
{
Task<AppUser?> GetByEmailAsync(string email);
Task<AppUser?> GetByIdAsync(int userId);
Task<AppUser> CreateAsync(string email, string displayName, string passwordHash);
Task<AppUser?> UpdateLoginSuccessAsync(int userId);
Task<AppUser?> UpdateFailedLoginAsync(int userId, DateTime? lockoutEndUtc);
Task<bool> ConfirmEmailAsync(int userId, Guid token);
Task<bool> ResetPasswordAsync(int userId, Guid token, string passwordHash);
Task<AppUser?> EnableTwoFactorAsync(int userId);
Task<AppUser?> DisableTwoFactorAsync(int userId);
}
public interface IAuthenticationRepository
{
Task<UserEmailVerificationToken> CreateEmailVerificationTokenAsync(int userId, Guid token, DateTime expiryUtc);
Task<UserEmailVerificationToken?> GetEmailVerificationTokenAsync(Guid token);
Task<UserPasswordResetToken> CreatePasswordResetTokenAsync(int userId, Guid token, DateTime expiryUtc);
Task<UserPasswordResetToken?> GetPasswordResetTokenAsync(Guid token);
Task<UserTwoFactor?> GetTwoFactorAsync(int userId);
Task<UserTwoFactor> SaveTwoFactorAsync(int userId, string secretKey, string? recoveryCodes);
Task<long> InsertLoginAuditAsync(UserLoginAudit audit);
}
public sealed class UserRepository(ISqlConnectionFactory connectionFactory) : IUserRepository
{
public async Task<AppUser?> GetByEmailAsync(string email)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleOrDefaultAsync<AppUser>(
"dbo.User_GetByEmail",
new { Email = email },
commandType: CommandType.StoredProcedure);
}
public async Task<AppUser?> GetByIdAsync(int userId)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleOrDefaultAsync<AppUser>(
"dbo.User_GetById",
new { UserID = userId },
commandType: CommandType.StoredProcedure);
}
public async Task<AppUser> CreateAsync(string email, string displayName, string passwordHash)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleAsync<AppUser>(
"dbo.User_Create",
new { Email = email, DisplayName = displayName, PasswordHash = passwordHash },
commandType: CommandType.StoredProcedure);
}
public async Task<AppUser?> UpdateLoginSuccessAsync(int userId)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleOrDefaultAsync<AppUser>(
"dbo.User_UpdateLoginSuccess",
new { UserID = userId },
commandType: CommandType.StoredProcedure);
}
public async Task<AppUser?> UpdateFailedLoginAsync(int userId, DateTime? lockoutEndUtc)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleOrDefaultAsync<AppUser>(
"dbo.User_UpdateFailedLogin",
new { UserID = userId, LockoutEndUtc = lockoutEndUtc },
commandType: CommandType.StoredProcedure);
}
public async Task<bool> ConfirmEmailAsync(int userId, Guid token)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleAsync<bool>(
"dbo.User_ConfirmEmail",
new { UserID = userId, Token = token },
commandType: CommandType.StoredProcedure);
}
public async Task<bool> ResetPasswordAsync(int userId, Guid token, string passwordHash)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleAsync<bool>(
"dbo.User_ResetPassword",
new { UserID = userId, Token = token, PasswordHash = passwordHash },
commandType: CommandType.StoredProcedure);
}
public async Task<AppUser?> EnableTwoFactorAsync(int userId)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleOrDefaultAsync<AppUser>(
"dbo.User_EnableTwoFactor",
new { UserID = userId },
commandType: CommandType.StoredProcedure);
}
public async Task<AppUser?> DisableTwoFactorAsync(int userId)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleOrDefaultAsync<AppUser>(
"dbo.User_DisableTwoFactor",
new { UserID = userId },
commandType: CommandType.StoredProcedure);
}
}
public sealed class AuthenticationRepository(ISqlConnectionFactory connectionFactory) : IAuthenticationRepository
{
public async Task<UserEmailVerificationToken> CreateEmailVerificationTokenAsync(int userId, Guid token, DateTime expiryUtc)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleAsync<UserEmailVerificationToken>(
"dbo.User_CreateEmailVerificationToken",
new { UserID = userId, Token = token, ExpiryUtc = expiryUtc },
commandType: CommandType.StoredProcedure);
}
public async Task<UserEmailVerificationToken?> GetEmailVerificationTokenAsync(Guid token)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleOrDefaultAsync<UserEmailVerificationToken>(
"dbo.User_GetEmailVerificationToken",
new { Token = token },
commandType: CommandType.StoredProcedure);
}
public async Task<UserPasswordResetToken> CreatePasswordResetTokenAsync(int userId, Guid token, DateTime expiryUtc)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleAsync<UserPasswordResetToken>(
"dbo.User_CreatePasswordResetToken",
new { UserID = userId, Token = token, ExpiryUtc = expiryUtc },
commandType: CommandType.StoredProcedure);
}
public async Task<UserPasswordResetToken?> GetPasswordResetTokenAsync(Guid token)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleOrDefaultAsync<UserPasswordResetToken>(
"dbo.User_GetPasswordResetToken",
new { Token = token },
commandType: CommandType.StoredProcedure);
}
public async Task<UserTwoFactor?> GetTwoFactorAsync(int userId)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleOrDefaultAsync<UserTwoFactor>(
"dbo.User_GetTwoFactor",
new { UserID = userId },
commandType: CommandType.StoredProcedure);
}
public async Task<UserTwoFactor> SaveTwoFactorAsync(int userId, string secretKey, string? recoveryCodes)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleAsync<UserTwoFactor>(
"dbo.User_SaveTwoFactor",
new { UserID = userId, SecretKey = secretKey, RecoveryCodes = recoveryCodes },
commandType: CommandType.StoredProcedure);
}
public async Task<long> InsertLoginAuditAsync(UserLoginAudit audit)
{
using var connection = connectionFactory.CreateConnection();
return await connection.QuerySingleAsync<long>(
"dbo.User_InsertLoginAudit",
new
{
audit.UserID,
audit.EmailAttempted,
audit.IpAddress,
audit.UserAgent,
audit.WasSuccessful,
audit.Reason
},
commandType: CommandType.StoredProcedure);
}
}