public async Task <IActionResult> Login(LoginViewModel model, string returnUrl = null) { model.User = null; ViewData["ReturnUrl"] = returnUrl; if (ModelState.IsValid) { var user = await _userManager.FindByEmailAsync(model.Email); // This doesn't count login failures towards account lockout // To enable password failures to trigger account lockout, set lockoutOnFailure: true var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure : false); if (result.Succeeded) { _logger.LogInformation("User logged in."); await _tripwire.RegisterEvent(TripwireEventType.Login); this.FlashSuccess("Logged in"); return(RedirectToLocal(returnUrl)); } if (result.RequiresTwoFactor) { return(RedirectToAction(nameof(LoginWith2fa), new { returnUrl, model.RememberMe })); } if (result.IsLockedOut) { _logger.LogWarning("User account locked out."); return(RedirectToAction(nameof(Lockout))); } else { await _tripwire.RegisterEvent(TripwireEventType.LoginAttempt); ModelState.AddModelError(string.Empty, "Invalid login attempt."); return(View(model)); } } // If we got this far, something failed, redisplay form return(View(model)); }
public async Task <IActionResult> Withdraw(WithdrawViewModel model) { var user = await GetUser(required : true); //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider) var via = new ViaJsonRpc(_settings.AccessHttpUrl); var balance = via.BalanceQuery(user.Exchange.Id, model.Asset); // fill in model in case we need to error out early model.User = user; model.AssetSettings = _settings.Assets; model.BalanceAvailable = balance.Available; // if tripwire tripped cancel if (!_tripwire.WithdrawalsEnabled()) { _logger.LogError("Tripwire tripped, exiting Withdraw()"); this.FlashError($"Withdrawals not enabled"); return(View(model)); } await _tripwire.RegisterEvent(TripwireEventType.WithdrawalAttempt); if (!ModelState.IsValid) { // redisplay form return(View(model)); } // check 2fa authentication if (user.TwoFactorEnabled) { if (model.TwoFactorCode == null) { model.TwoFactorCode = ""; } var authenticatorCode = model.TwoFactorCode.Replace(" ", string.Empty).Replace("-", string.Empty); if (!await _userManager.VerifyTwoFactorTokenAsync(user, _userManager.Options.Tokens.AuthenticatorTokenProvider, authenticatorCode)) { this.FlashError($"Invalid authenticator code"); return(View(model)); } } // lock process of checking balance and performing withdrawal lock (_userLocks.GetLock(user.Id)) { //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider) balance = via.BalanceQuery(user.Exchange.Id, model.Asset); model.BalanceAvailable = balance.Available; var wallet = _walletProvider.GetChain(model.Asset); // validate amount var amountInt = wallet.StringToAmount(model.Amount.ToString()); var availableInt = wallet.StringToAmount(balance.Available); if (amountInt > availableInt) { this.FlashError("Amount must be less then or equal to available balance"); return(View(model)); } if (amountInt <= 0) { this.FlashError("Amount must be greather then or equal to 0"); return(View(model)); } // validate address if (!wallet.ValidateAddress(model.WithdrawalAddress)) { this.FlashError("Withdrawal address is not valid"); return(View(model)); } // validate kyc level (var success, var withdrawalAssetAmount, var error) = ValidateWithdrawlLimit(user, model.Asset, model.Amount); if (!success) { this.FlashError(error); return(View(model)); } var consolidatedFundsTag = _walletProvider.ConsolidatedFundsTag(); using (var dbtx = wallet.BeginDbTransaction()) { // ensure tag exists if (!wallet.HasTag(consolidatedFundsTag)) { wallet.NewTag(consolidatedFundsTag); wallet.Save(); } // register withdrawal with wallet var tag = wallet.GetTag(user.Id); if (tag == null) { tag = wallet.NewTag(user.Id); } var spend = wallet.RegisterPendingSpend(consolidatedFundsTag, consolidatedFundsTag, model.WithdrawalAddress, amountInt, tag); wallet.Save(); var businessId = spend.Id; // register withdrawal with the exchange backend var negativeAmount = -model.Amount; try { via.BalanceUpdateQuery(user.Exchange.Id, model.Asset, "withdraw", businessId, negativeAmount.ToString(), null); } catch (ViaJsonException ex) { _logger.LogError(ex, "Failed to update (withdraw) user balance (xch id: {0}, asset: {1}, businessId: {2}, amount {3}", user.Exchange.Id, model.Asset, businessId, negativeAmount); if (ex.Err == ViaError.BALANCE_UPDATE__BALANCE_NOT_ENOUGH) { dbtx.Rollback(); this.FlashError("Balance not enough"); return(View(model)); } throw; } dbtx.Commit(); } // register withdrawal with kyc limits user.AddWithdrawal(_context, model.Asset, model.Amount, withdrawalAssetAmount); _context.SaveChanges(); } // register withdrawal with tripwire await _tripwire.RegisterEvent(TripwireEventType.Withdrawal); this.FlashSuccess(string.Format("Created withdrawal: {0} {1} to {2}", model.Amount, model.Asset, model.WithdrawalAddress)); // send email: withdrawal created await _emailSender.SendEmailChainWithdrawalCreatedAsync(user.Email, model.Asset, model.Amount.ToString()); return(View(model)); }