예제 #1
0
        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));
        }
예제 #2
0
        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));
        }