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 _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 /// /// ctor /// /// /// /// /// /// /// /// /// public UserManager( ILogger 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 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 UserExists(string userName) { return await _userDao.UserNameExists(userName, null); } public async Task UpdateUserSucsess(long userId) { return await _userDao.UpdateUserSucsess(userId); } public async Task UserEmailExists(string email) { return await _userDao.UserEmailExists(email); } public async Task UserNameExists(string userName, long? userId) { return await _userDao.UserNameExists(userName, userId); } public async Task> GetUsers(long? userId = null, UserType? userType = null) { return await _userDao.GetUsers(userId, (byte?)userType); } public async Task GetUser(long userId) { return await _userDao.GetUser(userId); } public async Task GetUserByEmail(string email) { return await _userDao.GetUserByEmail(email); } public async Task 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 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 GetUserInfo(string accountWalletAddress) { _logger.LogDebug($"Get User Info for wallet address: {accountWalletAddress}"); if (accountWalletAddress == PlatformUserAccount.WalletAddress) { return new UserAccounts { Accounts = new List() {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 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 PasswordReset(string token, string newPassword) { return await _userDao.PasswordReset(token, _passwordProcess.Generate(newPassword).ToFlatString()); } } }