210 lines
6.1 KiB
C#
210 lines
6.1 KiB
C#
using AuthorBuddy.Web.Data;
|
|
using LiteDB;
|
|
|
|
namespace AuthorBuddy.Web.Services;
|
|
|
|
public class AuthService : IAuthService
|
|
{
|
|
private readonly ILiteDatabase _db;
|
|
private readonly ISettingsService _settingsService;
|
|
private readonly ILocalizationService _loc;
|
|
private UserEntity? _currentUser;
|
|
private string? _pending2FASecret;
|
|
private bool _requires2FA;
|
|
|
|
public AuthService(ILiteDatabase db, ISettingsService settingsService, ILocalizationService loc)
|
|
{
|
|
_db = db;
|
|
_settingsService = settingsService;
|
|
_loc = loc;
|
|
}
|
|
|
|
public bool IsLoggedIn => _currentUser != null;
|
|
public bool Is2FARequired => _requires2FA;
|
|
public UserEntity? CurrentUser => _currentUser;
|
|
|
|
public async Task<bool> LoginAsync(string username, string password)
|
|
{
|
|
var col = _db.GetCollection<UserEntity>("users");
|
|
var user = col.FindOne(u => u.Name == username);
|
|
|
|
if (user == null || string.IsNullOrEmpty(user.PasswordHash))
|
|
return false;
|
|
|
|
if (!user.IsActive)
|
|
return false;
|
|
|
|
if (!TotpHelper.VerifyPassword(password, user.PasswordHash))
|
|
return false;
|
|
|
|
if (user.TwoFactorEnabled)
|
|
{
|
|
_pending2FASecret = user.TwoFactorSecret;
|
|
_requires2FA = true;
|
|
return true;
|
|
}
|
|
|
|
await SetAuthenticatedUser(user);
|
|
return true;
|
|
}
|
|
|
|
public async Task LogoutAsync()
|
|
{
|
|
_currentUser = null;
|
|
_requires2FA = false;
|
|
_pending2FASecret = null;
|
|
await Task.CompletedTask;
|
|
}
|
|
|
|
public async Task AdminBypassLoginAsync(int userId)
|
|
{
|
|
var col = _db.GetCollection<UserEntity>("users");
|
|
var user = col.FindById(userId);
|
|
if (user != null)
|
|
await SetAuthenticatedUser(user);
|
|
}
|
|
|
|
public async Task<bool> RegisterAsync(string username, string password)
|
|
{
|
|
var col = _db.GetCollection<UserEntity>("users");
|
|
var existing = col.FindOne(u => u.Name == username);
|
|
if (existing != null) return false;
|
|
|
|
var hasUsers = col.FindAll().Any();
|
|
var user = new UserEntity
|
|
{
|
|
Name = username,
|
|
PasswordHash = TotpHelper.HashPassword(password),
|
|
TwoFactorEnabled = false,
|
|
TwoFactorSecret = string.Empty,
|
|
Role = hasUsers ? UserRole.Author : UserRole.Administrator
|
|
};
|
|
col.Insert(user);
|
|
await SetAuthenticatedUser(user);
|
|
return true;
|
|
}
|
|
|
|
public Task<UserEntity?> GetCurrentUserAsync()
|
|
{
|
|
return Task.FromResult(_currentUser);
|
|
}
|
|
|
|
public async Task<bool> Verify2FAAsync(string code)
|
|
{
|
|
if (string.IsNullOrEmpty(_pending2FASecret)) return false;
|
|
|
|
if (!TotpHelper.VerifyCode(_pending2FASecret, code))
|
|
return false;
|
|
|
|
var col = _db.GetCollection<UserEntity>("users");
|
|
var user = col.FindOne(u => u.TwoFactorSecret == _pending2FASecret);
|
|
if (user == null) return false;
|
|
|
|
await SetAuthenticatedUser(user);
|
|
return true;
|
|
}
|
|
|
|
public Task<string> Generate2FASecretAsync()
|
|
{
|
|
return Task.FromResult(TotpHelper.GenerateSecret());
|
|
}
|
|
|
|
public string Get2FAProvisioningUri(string secret, string username)
|
|
{
|
|
return TotpHelper.GetProvisioningUri(secret, username);
|
|
}
|
|
|
|
public async Task<bool> Enable2FAAsync(string secret)
|
|
{
|
|
if (_currentUser == null) return false;
|
|
|
|
var col = _db.GetCollection<UserEntity>("users");
|
|
var user = col.FindById(_currentUser.Id);
|
|
if (user == null) return false;
|
|
|
|
user.TwoFactorSecret = secret;
|
|
user.TwoFactorEnabled = true;
|
|
col.Update(user);
|
|
_currentUser.TwoFactorSecret = secret;
|
|
_currentUser.TwoFactorEnabled = true;
|
|
return true;
|
|
}
|
|
|
|
public async Task<bool> Disable2FAAsync()
|
|
{
|
|
if (_currentUser == null) return false;
|
|
|
|
var col = _db.GetCollection<UserEntity>("users");
|
|
var user = col.FindById(_currentUser.Id);
|
|
if (user == null) return false;
|
|
|
|
user.TwoFactorSecret = string.Empty;
|
|
user.TwoFactorEnabled = false;
|
|
col.Update(user);
|
|
_currentUser.TwoFactorSecret = string.Empty;
|
|
_currentUser.TwoFactorEnabled = false;
|
|
return true;
|
|
}
|
|
|
|
public async Task<bool> Is2FAEnabledAsync()
|
|
{
|
|
if (_currentUser == null) return false;
|
|
var col = _db.GetCollection<UserEntity>("users");
|
|
var user = col.FindById(_currentUser.Id);
|
|
return user?.TwoFactorEnabled ?? false;
|
|
}
|
|
|
|
public async Task<string?> GetCurrent2FASecretAsync()
|
|
{
|
|
if (_currentUser == null) return null;
|
|
var col = _db.GetCollection<UserEntity>("users");
|
|
var user = col.FindById(_currentUser.Id);
|
|
return user?.TwoFactorSecret;
|
|
}
|
|
|
|
private async Task SetAuthenticatedUser(UserEntity user)
|
|
{
|
|
_currentUser = user;
|
|
_requires2FA = false;
|
|
_pending2FASecret = null;
|
|
_settingsService.CurrentProjectId = null;
|
|
_settingsService.CurrentFilePath = null;
|
|
_settingsService.SetCurrentUser(user.Id);
|
|
|
|
await _settingsService.InitializeAsync();
|
|
await _loc.InitAsync();
|
|
}
|
|
|
|
public Task<List<UserEntity>> GetAllUsersAsync()
|
|
{
|
|
var col = _db.GetCollection<UserEntity>("users");
|
|
return Task.FromResult(col.FindAll().OrderBy(u => u.Name).ToList());
|
|
}
|
|
|
|
public async Task<bool> SetUserActiveAsync(int userId, bool active)
|
|
{
|
|
if (_currentUser != null && userId == _currentUser.Id)
|
|
return false;
|
|
|
|
var col = _db.GetCollection<UserEntity>("users");
|
|
var user = col.FindById(userId);
|
|
if (user == null) return false;
|
|
|
|
user.IsActive = active;
|
|
col.Update(user);
|
|
return true;
|
|
}
|
|
|
|
public async Task<bool> DeleteUserAsync(int userId)
|
|
{
|
|
if (_currentUser != null && userId == _currentUser.Id)
|
|
return false;
|
|
|
|
var col = _db.GetCollection<UserEntity>("users");
|
|
var user = col.FindById(userId);
|
|
if (user == null) return false;
|
|
|
|
col.Delete(userId);
|
|
return true;
|
|
}
|
|
}
|