219 lines
8.3 KiB
C#
219 lines
8.3 KiB
C#
using Knoks.Core.Data.Interfaces;
|
||
using Knoks.Core.Entities;
|
||
using Knoks.Core.Entities.Args;
|
||
using Knoks.Core.Logic.Interfaces;
|
||
using Knoks.Framework.Cryptography;
|
||
using Knoks.Framework.Security;
|
||
using Microsoft.AspNetCore.DataProtection;
|
||
using Microsoft.Extensions.Logging;
|
||
using System.Collections.Generic;
|
||
using System.Threading.Tasks;
|
||
using System.Linq;
|
||
using System;
|
||
using Knoks.Framework.Services;
|
||
using System.Security.Cryptography;
|
||
using System.Net;
|
||
|
||
namespace Knoks.Core.Logic.Managers
|
||
{
|
||
public class UserManager : IUserManager
|
||
{
|
||
#region Fields
|
||
private readonly ILogger<UserManager> _logger;
|
||
private readonly IUserDao _userDao;
|
||
private readonly IPasswordStrength _passwordStrength;
|
||
private readonly IPasswordProcess _passwordProcess;
|
||
private readonly IEventManager _eventManager;
|
||
private readonly IUserImageManager _userImageManager;
|
||
private readonly IAccountDao _accountDao;
|
||
private readonly IAccountTransactionsSettings _settings;
|
||
private readonly IEmailService _emailService;
|
||
private readonly IGeneralManager _generalManager;
|
||
|
||
public User PlatformUser { get; private set; }
|
||
public Account PlatformUserAccount { get; private set; }
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// ctor
|
||
/// </summary>
|
||
/// <param name="logger"></param>
|
||
/// <param name="userDao"></param>
|
||
/// <param name="passwordStrength"></param>
|
||
/// <param name="passwordProcess"></param>
|
||
/// <param name="eventManager"></param>
|
||
/// <param name="userImageManager"></param>
|
||
/// <param name="accountDao"></param>
|
||
/// <param name="settings"></param>
|
||
public UserManager(
|
||
ILogger<UserManager> logger,
|
||
IUserDao userDao,
|
||
IPasswordStrength passwordStrength,
|
||
IPasswordProcess passwordProcess,
|
||
IEventManager eventManager,
|
||
IUserImageManager userImageManager,
|
||
IAccountDao accountDao,
|
||
IGeneralManager generalManager,
|
||
IAccountTransactionsSettings settings)
|
||
{
|
||
_logger = logger;
|
||
_userDao = userDao;
|
||
_passwordStrength = passwordStrength;
|
||
_passwordProcess = passwordProcess;
|
||
_eventManager = eventManager;
|
||
this._userImageManager = userImageManager;
|
||
this._generalManager = generalManager;
|
||
_accountDao = accountDao ?? throw new ArgumentNullException(nameof(accountDao));
|
||
|
||
if (settings != null) _settings = settings;
|
||
}
|
||
|
||
public async Task<User> CreateUser(CreateUserArgs args)
|
||
{
|
||
var userInfo = await _userDao.CreateUser(args, _passwordProcess.Generate(args.Password).ToFlatString(), _settings == null ? 0 : _settings.NewUserFreeCreditAmount);
|
||
await _eventManager.FireЕvent(this, EventAction.UserCreated, userInfo.user);
|
||
return userInfo.user;
|
||
}
|
||
|
||
public async Task<bool> UserExists(string userName)
|
||
{
|
||
return await _userDao.UserNameExists(userName, null);
|
||
}
|
||
public async Task<User> UpdateUserSucsess(long userId)
|
||
{
|
||
return await _userDao.UpdateUserSucsess(userId);
|
||
}
|
||
|
||
public async Task<bool> UserEmailExists(string email)
|
||
{
|
||
return await _userDao.UserEmailExists(email);
|
||
}
|
||
|
||
public async Task<bool> UserNameExists(string userName, long? userId)
|
||
{
|
||
return await _userDao.UserNameExists(userName, userId);
|
||
}
|
||
|
||
public async Task<IEnumerable<User>> GetUsers(long? userId = null, UserType? userType = null)
|
||
{
|
||
return await _userDao.GetUsers(userId, (byte?)userType);
|
||
}
|
||
|
||
public async Task<User> GetUser(long userId)
|
||
{
|
||
return await _userDao.GetUser(userId);
|
||
}
|
||
|
||
public async Task<User> GetUserByEmail(string email)
|
||
{
|
||
return await _userDao.GetUserByEmail(email);
|
||
}
|
||
|
||
public async Task<User> AuthenticateUser(int apiConsumerId, string email, string password)
|
||
{
|
||
_logger.LogDebug($"AuthenticateUser: email='{email}'");
|
||
|
||
var obj = await _userDao.GetUserCredential(apiConsumerId, email);
|
||
if (obj == null) return null; //User is not exists
|
||
|
||
var savedPasswordResult = new PasswordResult(obj.Password);
|
||
var userPasswordResult = _passwordProcess.Generate(password, savedPasswordResult.Salt);
|
||
|
||
return userPasswordResult.IsIdentical(savedPasswordResult) ? obj.User : null;
|
||
}
|
||
|
||
public async Task<User> UpdateUser(UpdateUserArgs args)
|
||
{
|
||
_logger.LogDebug($"Update user: apiConsumerId={args.ApiConsumerId}, email='{args.Email}'");
|
||
//if (await AuthenticateUser(args.ApiConsumerId, args.Email, args.Password) == null)
|
||
//{
|
||
// throw new InvalidOperationException("Invalid password for confirm");
|
||
//}
|
||
|
||
System.IO.Stream stream = args.Content != null && args.Content.Length > 0 ? new System.IO.MemoryStream(args.Content) :
|
||
!string.IsNullOrEmpty(args.ContentBase64) ? Framework.Extentions.FileInfoExtentions.ParseImage(args.ContentBase64) : null;
|
||
args.HasAvatar = args.HasAvatar || stream != null;
|
||
var user = await _userDao.UpdateUser(args);
|
||
if (stream != null)
|
||
{
|
||
try
|
||
{
|
||
await _userImageManager.SaveAvatar(user.UserId, stream);
|
||
}
|
||
catch {
|
||
args.HasAvatar = false;
|
||
user = await _userDao.UpdateUser(args);
|
||
}
|
||
}
|
||
if (user.HasAvatar.HasValue && user.HasAvatar.Value)
|
||
{
|
||
user.AvatarUrl = _generalManager.GetAvatarUrl(user.UserId);
|
||
}
|
||
return user;
|
||
}
|
||
|
||
public FileResponse GetAvatarFile(int userId)
|
||
{
|
||
DateTimeOffset modified;
|
||
var stream = _userImageManager.LoadAvatar((long)userId, out modified);
|
||
return new FileResponse() { File = stream, FileDownloadName = "avatar.png", LastModified = modified };
|
||
}
|
||
|
||
public async Task<UserAccounts> GetUserInfo(string accountWalletAddress)
|
||
{
|
||
_logger.LogDebug($"Get User Info for wallet address: {accountWalletAddress}");
|
||
|
||
if (accountWalletAddress == PlatformUserAccount.WalletAddress)
|
||
{
|
||
return new UserAccounts
|
||
{
|
||
Accounts = new List<Account>() {PlatformUserAccount},
|
||
User = PlatformUser
|
||
};
|
||
}
|
||
|
||
return await _userDao.GetUserInfo(accountWalletAddress);
|
||
}
|
||
|
||
public async Task Initialize()
|
||
{
|
||
var platformUserList = await _userDao.GetUsers(userType: (byte)UserType.Platform);
|
||
|
||
if (platformUserList.Count() != 1)
|
||
throw new InvalidOperationException($"Wrong number of platform users: {platformUserList.Count()}");
|
||
|
||
PlatformUser = platformUserList.First();
|
||
|
||
var platformUserAccount = await _accountDao.GetUserAccounts(PlatformUser.UserId);
|
||
if (!platformUserAccount.Accounts.Any())
|
||
throw new InvalidOperationException("Platform user must have at least one account");
|
||
|
||
PlatformUserAccount = platformUserAccount.Accounts.First();
|
||
}
|
||
|
||
public async Task<bool> CheckPasswordResetToken(string token)
|
||
{
|
||
return await _userDao.CheckPasswordResetToken(token);
|
||
}
|
||
|
||
public async Task SendPasswordResetLink(string email, string link)
|
||
{
|
||
using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
|
||
{
|
||
byte[] bytes = new byte[8];
|
||
rng.GetBytes(bytes);
|
||
|
||
string token = Convert.ToBase64String(bytes);
|
||
await _userDao.SendPasswordResetLink(email, token);
|
||
await _eventManager.FireЕvent(this, EventAction.ResetPassword, new UserPasswordReset() { Email = email, Link = $"{link}/{WebUtility.UrlEncode(token)}"});
|
||
await Task.CompletedTask;
|
||
}
|
||
}
|
||
|
||
public async Task<bool> PasswordReset(string token, string newPassword)
|
||
{
|
||
return await _userDao.PasswordReset(token, _passwordProcess.Generate(newPassword).ToFlatString());
|
||
}
|
||
}
|
||
}
|