Example #1
0
        public async Task <APIResponse> Confirm([FromBody] ConfirmModel model)
        {
            // validate
            if (BaseValidableModel.IsInvalid(model, out var errFields))
            {
                return(APIResponse.BadRequest(errFields));
            }

            var user       = (DAL.Models.Identity.User)null;
            var agent      = GetUserAgentInfo();
            var userLocale = GetUserLocale();

            // check token
            if (!await JWT.IsValid(
                    appConfig: AppConfig,
                    jwtToken: model.Token,
                    expectedAudience: JwtAudience.Cabinet,
                    expectedArea: Common.JwtArea.Registration,
                    validStamp: async(jwt, id) => {
                user = await UserManager.FindByNameAsync(id);
                return("");
            }
                    ) || user == null)
            {
                return(APIResponse.BadRequest(nameof(model.Token), "Invalid token"));
            }

            if (!user.EmailConfirmed)
            {
                user.EmailConfirmed = true;
                await DbContext.SaveChangesAsync();
            }
            return(APIResponse.Success());
        }
Example #2
0
        public async Task <APIResponse> NewPassword([FromBody] NewPasswordModel model)
        {
            // validate
            if (BaseValidableModel.IsInvalid(model, out var errFields))
            {
                return(APIResponse.BadRequest(errFields));
            }

            var user       = (DAL.Models.Identity.User)null;
            var audience   = GetCurrentAudience();
            var agent      = GetUserAgentInfo();
            var userLocale = GetUserLocale();

            // check token
            if (!await Core.Tokens.JWT.IsValid(
                    appConfig: AppConfig,
                    jwtToken: model.Token,
                    expectedAudience: JwtAudience.Cabinet,
                    expectedArea: JwtArea.RestorePassword,
                    validStamp: async(jwt, id) => {
                user = await UserManager.FindByNameAsync(id);
                return("");
            }
                    ) || user == null)
            {
                return(APIResponse.BadRequest(nameof(model.Token), "Invalid token"));
            }

            await UserManager.RemovePasswordAsync(user);

            await UserManager.AddPasswordAsync(user, model.Password);

            if (audience != null)
            {
                UserAccount.GenerateJwtSalt(user, audience.Value);
                await DbContext.SaveChangesAsync();
            }

            // notification
            await EmailComposer.FromTemplate(await TemplateProvider.GetEmailTemplate(EmailTemplate.PasswordChanged, userLocale))
            .Initiator(agent.Ip, agent.Agent, DateTime.UtcNow)
            .Send(user.Email, user.UserName, EmailQueue)
            ;

            // activity
            var userActivity = CoreLogic.User.CreateUserActivity(
                user: user,
                type: Common.UserActivityType.Password,
                comment: "Password changed",
                ip: agent.Ip,
                agent: agent.Agent,
                locale: userLocale
                );

            DbContext.UserActivity.Add(userActivity);
            await DbContext.SaveChangesAsync();

            return(APIResponse.Success());
        }
Example #3
0
        public async Task <APIResponse> Estimate([FromBody] EstimateModel model)
        {
            if (BaseValidableModel.IsInvalid(model, out var errFields))
            {
                return(APIResponse.BadRequest(errFields));
            }

            var exchangeCurrency         = FiatCurrency.Usd;
            TradableCurrency?cryptoToken = null;

            // try parse fiat currency
            if (Enum.TryParse(model.Currency, true, out FiatCurrency fc))
            {
                exchangeCurrency = fc;
            }
            // or crypto currency
            else if (Enum.TryParse(model.Currency, true, out TradableCurrency cc))
            {
                cryptoToken = cc;
            }
            else
            {
                return(APIResponse.BadRequest(nameof(model.Currency), "Invalid format"));
            }

            // try parse amount
            if (!BigInteger.TryParse(model.Amount, out var inputAmount) || inputAmount < 1 || (cryptoToken == null && !model.Reversed && inputAmount > long.MaxValue))
            {
                return(APIResponse.BadRequest(nameof(model.Amount), "Invalid amount"));
            }

            // ---

            var userOrNull = await GetUserFromDb();

            var rcfg = RuntimeConfigHolder.Clone();

            // get user limits
            var limits = cryptoToken != null
                                ? DepositLimits(rcfg, cryptoToken.Value)
                                : await DepositLimits(rcfg, DbContext, userOrNull?.Id, exchangeCurrency)
            ;

            // estimate
            var estimation = await Estimation(rcfg, inputAmount, cryptoToken, exchangeCurrency, model.Reversed, 0, limits.Min, limits.Max);

            if (!estimation.TradingAllowed)
            {
                return(APIResponse.BadRequest(APIErrorCode.TradingNotAllowed));
            }
            if (estimation.IsLimitExceeded)
            {
                return(APIResponse.BadRequest(APIErrorCode.TradingExchangeLimit, estimation.View.Limits));
            }

            return(APIResponse.Success(estimation.View));
        }
Example #4
0
        public async Task <APIResponse> ChangePassword([FromBody] ChangePasswordModel model)
        {
            // validate
            if (BaseValidableModel.IsInvalid(model, out var errFields))
            {
                return(APIResponse.BadRequest(errFields));
            }

            var user = await GetUserFromDb();

            var agent      = GetUserAgentInfo();
            var userLocale = GetUserLocale();

            // first check tfa
            if (user.TwoFactorEnabled && !Core.Tokens.GoogleAuthenticator.Validate(model.TfaCode, user.TfaSecret))
            {
                return(APIResponse.BadRequest(nameof(model.TfaCode), "Invalid 2fa code"));
            }

            // check current password
            if (await UserManager.HasPasswordAsync(user) && (model.Current == null || !await UserManager.CheckPasswordAsync(user, model.Current)))
            {
                return(APIResponse.BadRequest(nameof(model.Current), "Invalid current password"));
            }

            // set new password
            await UserManager.RemovePasswordAsync(user);

            await UserManager.AddPasswordAsync(user, model.New);

            // notification
            await EmailComposer.FromTemplate(await TemplateProvider.GetEmailTemplate(EmailTemplate.PasswordChanged, userLocale))
            .Initiator(agent.Ip, agent.Agent, DateTime.UtcNow)
            .Send(user.Email, user.UserName, EmailQueue)
            ;

            // activity
            var userActivity = CoreLogic.User.CreateUserActivity(
                user: user,
                type: Common.UserActivityType.Password,
                comment: "Password changed",
                ip: agent.Ip,
                agent: agent.Agent,
                locale: userLocale
                );

            DbContext.UserActivity.Add(userActivity);
            await DbContext.SaveChangesAsync();

            return(APIResponse.Success(
                       new ChangePasswordView()
            {
            }
                       ));
        }
Example #5
0
        public async Task <APIResponse> Tfa([FromBody] TfaModel model)
        {
            // validate
            if (BaseValidableModel.IsInvalid(model, out var errFields))
            {
                return(APIResponse.BadRequest(errFields));
            }

            var audience = GetCurrentAudience();

            if (audience == null)
            {
                return(APIResponse.BadRequest(APIErrorCode.Unauthorized));
            }

            var user = await GetUserFromDb();

            if (user != null && user.TwoFactorEnabled)
            {
                // locked out
                if (await UserManager.IsLockedOutAsync(user))
                {
                    return(APIResponse.BadRequest(APIErrorCode.AccountLocked, "Too many unsuccessful attempts. Account is locked, try to sign in later"));
                }

                // by code
                if (GoogleAuthenticator.Validate(model.Code, user.TfaSecret))
                {
                    return(OnSignInResultCheck(
                               services: HttpContext.RequestServices,
                               result: Microsoft.AspNetCore.Identity.SignInResult.Success,
                               user: user,
                               audience: audience.Value,
                               tfaRequired: false
                               ));
                }

                // +1 failed login
                DbContext.Attach <DAL.Models.Identity.User>(user);
                await UserManager.AccessFailedAsync(user);
            }

            return(APIResponse.BadRequest(nameof(model.Code), "Invalid code"));
        }
Example #6
0
        public async Task <APIResponse> VerificationEdit([FromBody] VerificationEditModel model)
        {
            // validate
            if (BaseValidableModel.IsInvalid(model, out var errFields))
            {
                return(APIResponse.BadRequest(errFields));
            }

            var user = await GetUserFromDb();

            var userTier = CoreLogic.User.GetTier(user);

            if (userTier == UserTier.Tier1 && !CoreLogic.User.HasKycVerification(user.UserVerification))
            {
                // format phone number
                var phoneFormatted = Common.TextFormatter.NormalizePhoneNumber(model.PhoneNumber);

                // parse dob
                var dob = DateTime.ParseExact(model.Dob, "dd.MM.yyyy", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);

                {
                    user.UserVerification.FirstName  = model.FirstName.Limit(64);
                    user.UserVerification.MiddleName = model.MiddleName?.Limit(64);
                    user.UserVerification.LastName   = model.LastName.Limit(64);
                    user.UserVerification.DoB        = dob;

                    user.UserVerification.PhoneNumber = phoneFormatted.Limit(32);
                    user.UserVerification.Country     = Common.Countries.GetNameByAlpha2(model.Country);
                    user.UserVerification.CountryCode = model.Country.ToUpper();
                    user.UserVerification.State       = model.State.Limit(256);
                    user.UserVerification.City        = model.City.Limit(256);
                    user.UserVerification.PostalCode  = model.PostalCode.Limit(16);
                    user.UserVerification.Street      = model.Street.Limit(256);
                    user.UserVerification.Apartment   = model.Apartment?.Limit(128);

                    user.UserVerification.TimeUserChanged = DateTime.UtcNow;
                }

                await DbContext.SaveChangesAsync();
            }

            return(APIResponse.Success(MakeVerificationView(user)));
        }
Example #7
0
        public async Task <APIResponse> Confirm([FromBody] ConfirmModel model)
        {
            if (BaseValidableModel.IsInvalid(model, out var errFields))
            {
                return(APIResponse.BadRequest(errFields));
            }

            var user = await GetUserFromDb();

            var userLocale = GetUserLocale();
            var agent      = GetUserAgentInfo();

            // ---

            var request = await
                          (
                from r in DbContext.BuyGoldEth
                where
                r.Status == BuySellGoldRequestStatus.Unconfirmed &&
                r.Id == model.RequestId &&
                r.UserId == user.Id &&
                (DateTime.UtcNow - r.TimeCreated).TotalHours < 1.0
                select r
                          )
                          .AsTracking()
                          .FirstOrDefaultAsync()
            ;

            // request not exists
            if (request == null)
            {
                return(APIResponse.BadRequest(nameof(model.RequestId), "Invalid id"));
            }

            request.Status = BuySellGoldRequestStatus.Confirmed;
            await DbContext.SaveChangesAsync();

            return(APIResponse.Success(
                       new ConfirmView()
            {
            }
                       ));
        }
Example #8
0
        public async Task <APIResponse> Register([FromBody] RegisterModel model)
        {
            // validate
            if (BaseValidableModel.IsInvalid(model, out var errFields))
            {
                return(APIResponse.BadRequest(errFields));
            }

            var agent      = GetUserAgentInfo();
            var userLocale = GetUserLocale();

            // captcha
            if (!HostingEnvironment.IsDevelopment())
            {
                if (!await Core.Recaptcha.Verify(AppConfig.Services.Recaptcha.SecretKey, model.Captcha, agent.Ip))
                {
                    return(APIResponse.BadRequest(nameof(model.Captcha), "Failed to validate captcha"));
                }
            }

            var result = await Core.UserAccount.CreateUserAccount(HttpContext.RequestServices, model.Email, model.Password);

            if (result.User != null)
            {
                // confirmation token
                var token = Core.Tokens.JWT.CreateSecurityToken(
                    appConfig: AppConfig,
                    entityId: result.User.UserName,
                    audience: JwtAudience.Cabinet,
                    securityStamp: "",
                    area: Common.JwtArea.Registration,
                    validFor: TimeSpan.FromDays(2)
                    );

                var callbackUrl = this.MakeAppLink(JwtAudience.Cabinet, fragment: AppConfig.Apps.Cabinet.RouteSignUpConfirmation.Replace(":token", token));

                // email confirmation
                await EmailComposer.FromTemplate(await TemplateProvider.GetEmailTemplate(EmailTemplate.EmailConfirmation, userLocale))
                .Link(callbackUrl)
                .Send(model.Email, "", EmailQueue)
                ;

                // auth token
                return(APIResponse.Success(
                           new Models.API.v1.User.UserModels.AuthenticateView()
                {
                    Token = JWT.CreateAuthToken(
                        appConfig: AppConfig,
                        user: result.User,
                        audience: JwtAudience.Cabinet,
                        area: JwtArea.Authorized
                        ),
                    TfaRequired = false,
                }
                           ));
            }
            else
            {
                if (result.IsUsernameExists || result.IsEmailExists)
                {
                    return(APIResponse.BadRequest(APIErrorCode.AccountEmailTaken, "Email is already taken"));
                }
            }

            throw new Exception("Registration failed");
        }
Example #9
0
        public async Task <APIResponse> VerificationKycStart([FromBody] VerificationKycStartModel model)
        {
            // validate
            if (BaseValidableModel.IsInvalid(model, out var errFields))
            {
                return(APIResponse.BadRequest(errFields));
            }

            var user = await GetUserFromDb();

            var userTier = CoreLogic.User.GetTier(user);

            // tos not signed, didn't fill personal data, has kyc already
            if (
                !CoreLogic.User.HasTosSigned(user.UserVerification) ||
                !CoreLogic.User.HasFilledPersonalData(user.UserVerification) ||
                CoreLogic.User.HasKycVerification(user.UserVerification)
                )
            {
                return(APIResponse.BadRequest(APIErrorCode.AccountNotVerified));
            }

            // check previous verification attempt
            var status = MakeVerificationView(user);

            if (status.IsKycPending)
            {
                return(APIResponse.BadRequest(APIErrorCode.RateLimit));
            }

            // ---

            // new kyc ticket
            var ticket = new KycTicket()
            {
                UserId      = user.Id,
                ReferenceId = Guid.NewGuid().ToString("N"),
                Method      = "general",

                FirstName   = user.UserVerification.FirstName,
                LastName    = user.UserVerification.LastName,
                DoB         = user.UserVerification.DoB.Value,
                CountryCode = user.UserVerification.CountryCode,
                PhoneNumber = user.UserVerification.PhoneNumber,
                TimeCreated = DateTime.UtcNow,
            };

            DbContext.KycShuftiProTicket.Add(ticket);
            await DbContext.SaveChangesAsync();

            // set last ticket
            user.UserVerification.LastKycTicket = ticket;
            await DbContext.SaveChangesAsync();

            // new redirect
            var kycUser = new CoreLogic.Services.KYC.UserData()
            {
                FirstName    = ticket.FirstName,
                LastName     = ticket.LastName,
                CountryCode  = ticket.CountryCode,
                LanguageCode = GetUserLocale().ToString().ToUpper(),
                DoB          = ticket.DoB,
                PhoneNumber  = ticket.PhoneNumber,
                Email        = user.Email,
            };

            var callbackUrl         = Url.Link("CallbackShuftiPro", new { /*secret = AppConfig.Services.ShuftiPro.CallbackSecret*/ });
            var userTempRedirectUrl = Url.Link("CallbackRedirect", new { to = System.Web.HttpUtility.UrlEncode(model.Redirect) });
            var kycRedirect         = await KycExternalProvider.GetRedirect(
                kycUser,
                ticket.ReferenceId,
                userTempRedirectUrl,
                callbackUrl
                );

            Logger.Verbose($"{user.UserName} got kyc redirect to {kycRedirect} with callback to {callbackUrl} and middle redirect to {userTempRedirectUrl}");

            return(APIResponse.Success(new VerificationKycStartView()
            {
                TicketId = ticket.Id.ToString(),
                Redirect = kycRedirect,
            }));
        }
Example #10
0
        public async Task <APIResponse> Password([FromBody] RestoreModel model)
        {
            // validate
            if (BaseValidableModel.IsInvalid(model, out var errFields))
            {
                return(APIResponse.BadRequest(errFields));
            }

            var agent      = GetUserAgentInfo();
            var userLocale = GetUserLocale();

            // captcha
            if (!HostingEnvironment.IsDevelopment())
            {
                if (!await Core.Recaptcha.Verify(AppConfig.Services.Recaptcha.SecretKey, model.Captcha, agent.Ip))
                {
                    return(APIResponse.BadRequest(nameof(model.Captcha), "Failed to validate captcha"));
                }
            }

            // try find user
            var user = await UserManager.FindByEmailAsync(model.Email);

            if (user == null || !(await UserManager.IsEmailConfirmedAsync(user)))
            {
                return(APIResponse.Success());
            }

            // confirmation token
            var token = Core.Tokens.JWT.CreateSecurityToken(
                appConfig: AppConfig,
                entityId: user.UserName,
                audience: JwtAudience.Cabinet,
                area: Common.JwtArea.RestorePassword,
                securityStamp: "",
                validFor: TimeSpan.FromHours(24)
                );

            var callbackUrl = this.MakeAppLink(JwtAudience.Cabinet, fragment: AppConfig.Apps.Cabinet.RoutePasswordRestoration.Replace(":token", token));

            // restoration email
            await EmailComposer.FromTemplate(await TemplateProvider.GetEmailTemplate(EmailTemplate.PasswordRestoration, userLocale))
            .Link(callbackUrl)
            .Initiator(agent.Ip, agent.Agent, DateTime.UtcNow)
            .Send(model.Email, user.UserName, EmailQueue)
            ;

            // activity
            var userActivity = CoreLogic.User.CreateUserActivity(
                user: user,
                type: Common.UserActivityType.Password,
                comment: "Password restoration requested",
                ip: agent.Ip,
                agent: agent.Agent,
                locale: userLocale
                );

            DbContext.UserActivity.Add(userActivity);
            await DbContext.SaveChangesAsync();

            return(APIResponse.Success());
        }
Example #11
0
        public async Task <APIResponse> Confirm([FromBody] ConfirmModel model)
        {
            // validate
            if (BaseValidableModel.IsInvalid(model, out var errFields))
            {
                return(APIResponse.BadRequest(errFields));
            }

            var user = await GetUserFromDb();

            var userTier   = CoreLogic.User.GetTier(user);
            var userLocale = GetUserLocale();
            var agent      = GetUserAgentInfo();

            if (userTier < UserTier.Tier2)
            {
                return(APIResponse.BadRequest(APIErrorCode.AccountNotVerified));
            }

            // ---

            var request = await
                          (
                from r in DbContext.SellGoldEth
                where
                r.Status == BuySellGoldRequestStatus.Unconfirmed &&
                r.Id == model.RequestId &&
                r.UserId == user.Id &&
                (DateTime.UtcNow - r.TimeCreated).TotalHours < 1.0
                select r
                          )
                          .Include(_ => _.RelFinHistory)
                          .AsTracking()
                          .FirstOrDefaultAsync()
            ;

            // request not exists
            if (request == null)
            {
                return(APIResponse.BadRequest(nameof(model.RequestId), "Invalid id"));
            }

            // charge
            using (var scope = HttpContext.RequestServices.CreateScope()) {
                if (!await CoreLogic.Finance.SumusWallet.Charge(scope.ServiceProvider, request.UserId, request.GoldAmount, SumusToken.Gold))
                {
                    // mark request failed
                    request.Status = BuySellGoldRequestStatus.Failed;
                    if (request.RelFinHistory != null)
                    {
                        request.RelFinHistory.Status  = UserFinHistoryStatus.Failed;
                        request.RelFinHistory.Comment = "Not enough GOLD";
                    }
                    await DbContext.SaveChangesAsync();

                    return(APIResponse.BadRequest(APIErrorCode.TradingNotAllowed));
                }
                else
                {
                    // mark request for processing
                    request.Status = BuySellGoldRequestStatus.Confirmed;
                    if (request.RelFinHistory != null)
                    {
                        request.RelFinHistory.Status = UserFinHistoryStatus.Processing;
                    }
                    await DbContext.SaveChangesAsync();

                    return(APIResponse.Success(
                               new ConfirmView()
                    {
                    }
                               ));
                }
            }
        }
Example #12
0
        public async Task <APIResponse> AssetEth([FromBody] AssetEthModel model)
        {
            // validate
            if (BaseValidableModel.IsInvalid(model, out var errFields))
            {
                return(APIResponse.BadRequest(errFields));
            }

            // try parse amount
            if (!BigInteger.TryParse(model.Amount, out var inputAmount) || inputAmount < 1)
            {
                return(APIResponse.BadRequest(nameof(model.Amount), "Invalid amount"));
            }

            // try parse fiat currency
            var exchangeCurrency = FiatCurrency.Usd;

            if (Enum.TryParse(model.Currency, true, out FiatCurrency fc))
            {
                exchangeCurrency = fc;
            }

            // ---

            var rcfg = RuntimeConfigHolder.Clone();

            var user = await GetUserFromDb();

            var userTier = CoreLogic.User.GetTier(user);
            var agent    = GetUserAgentInfo();

            if (userTier < UserTier.Tier2)
            {
                return(APIResponse.BadRequest(APIErrorCode.AccountNotVerified));
            }

            // ---

            if (!rcfg.Gold.AllowTradingEth)
            {
                return(APIResponse.BadRequest(APIErrorCode.TradingNotAllowed));
            }

            var limits = WithdrawalLimits(rcfg, TradableCurrency.Eth);

            var estimation = await Estimation(rcfg, inputAmount, TradableCurrency.Eth, exchangeCurrency, model.EthAddress, model.Reversed, limits.Min, limits.Max);

            if (!estimation.TradingAllowed || estimation.ResultCurrencyAmount < 1)
            {
                return(APIResponse.BadRequest(APIErrorCode.TradingNotAllowed));
            }
            if (estimation.IsLimitExceeded)
            {
                return(APIResponse.BadRequest(APIErrorCode.TradingExchangeLimit, estimation.View.Limits));
            }

            // limit gold amount to max available
            if (estimation.ResultGoldAmount.FromSumus() > user.UserSumusWallet.BalanceGold)
            {
                estimation = await Estimation(rcfg, user.UserSumusWallet.BalanceGold.ToSumus(), TradableCurrency.Eth, exchangeCurrency, model.EthAddress, false, limits.Min, limits.Max);

                if (!estimation.TradingAllowed || estimation.ResultCurrencyAmount < 1)
                {
                    return(APIResponse.BadRequest(APIErrorCode.TradingNotAllowed));
                }
                if (estimation.IsLimitExceeded)
                {
                    return(APIResponse.BadRequest(APIErrorCode.TradingExchangeLimit, estimation.View.Limits));
                }
            }

            var timeNow = DateTime.UtcNow;

            // history
            var finHistory = new DAL.Models.UserFinHistory()
            {
                Status            = UserFinHistoryStatus.Unconfirmed,
                Type              = UserFinHistoryType.GoldSell,
                Source            = "GOLD",
                SourceAmount      = TextFormatter.FormatTokenAmountFixed(estimation.ResultGoldAmount, TokensPrecision.Sumus),
                Destination       = "ETH",
                DestinationAmount = TextFormatter.FormatTokenAmountFixed(estimation.ResultCurrencyAmount, TokensPrecision.Ethereum),
                Comment           = "",
                TimeCreated       = timeNow,
                UserId            = user.Id,
            };

            DbContext.UserFinHistory.Add(finHistory);
            await DbContext.SaveChangesAsync();

            // request
            var request = new DAL.Models.SellGoldEth()
            {
                Status           = BuySellGoldRequestStatus.Unconfirmed,
                GoldAmount       = estimation.ResultGoldAmount.FromSumus(),
                Destination      = model.EthAddress,
                EthAmount        = estimation.ResultCurrencyAmount.FromSumus(),
                ExchangeCurrency = exchangeCurrency,
                GoldRateCents    = estimation.CentsPerGoldRate,
                EthRateCents     = estimation.CentsPerAssetRate,
                TimeCreated      = timeNow,
                RelFinHistoryId  = finHistory.Id,
                UserId           = user.Id,
            };

            // add and save
            DbContext.SellGoldEth.Add(request);
            await DbContext.SaveChangesAsync();

            var assetPerGold = CoreLogic.Finance.Estimation.AssetPerGold(TradableCurrency.Eth, estimation.CentsPerAssetRate, estimation.CentsPerGoldRate);

            return(APIResponse.Success(
                       new AssetEthView()
            {
                RequestId = request.Id,
                EthRate = estimation.CentsPerAssetRate / 100d,
                GoldRate = estimation.CentsPerGoldRate / 100d,
                Currency = exchangeCurrency.ToString().ToUpper(),
                EthPerGoldRate = assetPerGold.ToString(),
                Estimation = estimation.View,
            }
                       ));
        }
Example #13
0
        public async Task <APIResponse> Authenticate([FromBody] AuthenticateModel model)
        {
            var notFoundDesc = "Account not found";

            // validate
            if (BaseValidableModel.IsInvalid(model, out var errFields))
            {
                return(APIResponse.BadRequest(APIErrorCode.AccountNotFound, notFoundDesc));
            }

            var agent      = GetUserAgentInfo();
            var userLocale = GetUserLocale();

            var user = await UserManager.FindByNameAsync(model.Username) ?? await UserManager.FindByEmailAsync(model.Username);

            if (user != null)
            {
                bool isLockedOut = false;

                // locked out
                if (user.LockoutEnd != null && user.LockoutEnd.Value.UtcDateTime > DateTime.UtcNow)
                {
                    // unlock before this check
                    isLockedOut     = true;
                    user.LockoutEnd = null;
                }

                // get audience
                JwtAudience audience = JwtAudience.Cabinet;
                if (!string.IsNullOrWhiteSpace(model.Audience))
                {
                    if (Enum.TryParse(model.Audience, true, out JwtAudience aud))
                    {
                        audience = aud;
                    }
                }

                // load options
                await DbContext.Entry(user).Reference(_ => _.UserOptions).LoadAsync();

                var sres = OnSignInResultCheck(
                    services: HttpContext.RequestServices,
                    result: await SignInManager.CheckPasswordSignInAsync(user, model.Password, lockoutOnFailure: true),
                    audience: audience,
                    user: user,
                    tfaRequired: user.TwoFactorEnabled
                    );
                if (sres != null)
                {
                    // successful result
                    if (sres.GetHttpStatusCode() == System.Net.HttpStatusCode.OK && sres.GetErrorCode() == null)
                    {
                        // notification
                        await EmailComposer.FromTemplate(await TemplateProvider.GetEmailTemplate(EmailTemplate.SignedIn, userLocale))
                        .ReplaceBodyTag("IP", agent.Ip)
                        .Initiator(agent.Ip, agent.Agent, DateTime.UtcNow)
                        .Send(user.Email, user.UserName, EmailQueue)
                        ;

                        // activity
                        var userActivity = CoreLogic.User.CreateUserActivity(
                            user: user,
                            type: Common.UserActivityType.Auth,
                            comment: "Signed in with password",
                            ip: agent.Ip,
                            agent: agent.Agent,
                            locale: userLocale
                            );
                        DbContext.UserActivity.Add(userActivity);
                        await DbContext.SaveChangesAsync();
                    }

                    return(sres);
                }

                // was locked before
                if (isLockedOut)
                {
                    await UserManager.SetLockoutEndDateAsync(user, (DateTimeOffset)DateTime.UtcNow.AddMinutes(60));

                    return(APIResponse.BadRequest(APIErrorCode.AccountLocked, "Too many unsuccessful attempts. Account is locked, try to sign in later"));
                }
            }

            return(APIResponse.BadRequest(APIErrorCode.AccountNotFound, notFoundDesc));
        }
Example #14
0
        public async Task <APIResponse> LiteWallet([FromBody] LiteWalletModel model)
        {
            // validate
            if (BaseValidableModel.IsInvalid(model, out var errFields))
            {
                return(APIResponse.BadRequest(errFields));
            }

            var user = await GetUserFromDb();

            var userTier   = CoreLogic.User.GetTier(user);
            var userLocale = GetUserLocale();
            var agent      = GetUserAgentInfo();

            if (userTier < UserTier.Tier2)
            {
                return(APIResponse.BadRequest(APIErrorCode.AccountNotVerified));
            }

            if (model.Amount <= 0.001m || user.UserSumusWallet.BalanceGold < model.Amount)
            {
                return(APIResponse.BadRequest(APIErrorCode.TradingNotAllowed));
            }

            // ---

            var timeNow = DateTime.UtcNow;

            // charge
            using (var scope = HttpContext.RequestServices.CreateScope()) {
                if (await CoreLogic.Finance.SumusWallet.Charge(scope.ServiceProvider, user.Id, model.Amount, SumusToken.Gold))
                {
                    try {
                        var finHistory = new UserFinHistory()
                        {
                            Status            = UserFinHistoryStatus.Processing,
                            Type              = UserFinHistoryType.GoldWithdraw,
                            Source            = "GOLD",
                            SourceAmount      = TextFormatter.FormatTokenAmountFixed(model.Amount),
                            Destination       = "",
                            DestinationAmount = "",
                            Comment           = "",
                            TimeCreated       = timeNow,
                            UserId            = user.Id,
                        };
                        DbContext.UserFinHistory.Add(finHistory);
                        await DbContext.SaveChangesAsync();

                        var request = new WithdrawGold()
                        {
                            Status          = EmissionRequestStatus.Requested,
                            SumAddress      = model.SumusAddress,
                            Amount          = model.Amount,
                            TimeCreated     = timeNow,
                            UserId          = user.Id,
                            RelFinHistoryId = finHistory.Id,
                        };
                        DbContext.WithdrawGold.Add(request);
                        await DbContext.SaveChangesAsync();

                        // mint-sender service
                        {
                            var reply = await Bus.Request(
                                MintSender.Subject.Sender.Request.Send,
                                new MintSender.Sender.Request.Send()
                            {
                                Id        = request.Id.ToString(),
                                Amount    = model.Amount.ToString("F18"),
                                PublicKey = model.SumusAddress,
                                Service   = "core_gold_withdrawer",
                                Token     = "GOLD",
                            },
                                MintSender.Sender.Request.SendReply.Parser,
                                3000
                                );

                            if (!reply.Success)
                            {
                                throw new Exception(reply.Error);
                            }
                        }

                        return(APIResponse.Success(
                                   new LiteWalletView()
                        {
                        }
                                   ));
                    } catch (Exception e) {
                        try {
                            await CoreLogic.Finance.SumusWallet.Refill(scope.ServiceProvider, user.Id, model.Amount, SumusToken.Gold);
                        } catch { }
                        Logger.Error(e, $"Failed to withdraw user {model.Amount} GOLD");
                        return(APIResponse.GeneralInternalFailure(e));
                    }
                }
                else
                {
                    return(APIResponse.BadRequest(APIErrorCode.TradingNotAllowed));
                }
            }
        }
Example #15
0
        public async Task <APIResponse> TFAEdit([FromBody] TfaEditModel model)
        {
            // validate
            if (BaseValidableModel.IsInvalid(model, out var errFields))
            {
                return(APIResponse.BadRequest(nameof(model.Code), "Invalid 2fa code"));
            }

            var user = await GetUserFromDb();

            var agent      = GetUserAgentInfo();
            var userLocale = GetUserLocale();

            var makeChange = user.TwoFactorEnabled != model.Enable;

            if (makeChange)
            {
                if (!Core.Tokens.GoogleAuthenticator.Validate(model.Code, user.TfaSecret))
                {
                    return(APIResponse.BadRequest(nameof(model.Code), "Invalid 2fa code"));
                }
                user.TwoFactorEnabled = model.Enable;
            }

            user.UserOptions.InitialTfaQuest = true;
            await DbContext.SaveChangesAsync();

            // notify
            if (makeChange)
            {
                if (model.Enable)
                {
                    // notification
                    await EmailComposer.FromTemplate(await TemplateProvider.GetEmailTemplate(EmailTemplate.TfaEnabled, userLocale))
                    .Initiator(agent.Ip, agent.Agent, DateTime.UtcNow)
                    .Send(user.Email, user.UserName, EmailQueue)
                    ;

                    // activity
                    var userActivity = CoreLogic.User.CreateUserActivity(
                        user: user,
                        type: Common.UserActivityType.Settings,
                        comment: "Two factor authentication enabled",
                        ip: agent.Ip,
                        agent: agent.Agent,
                        locale: userLocale
                        );
                    DbContext.UserActivity.Add(userActivity);
                    await DbContext.SaveChangesAsync();
                }
                else
                {
                    // notification
                    await EmailComposer.FromTemplate(await TemplateProvider.GetEmailTemplate(EmailTemplate.TfaDisabled, userLocale))
                    .Initiator(agent.Ip, agent.Agent, DateTime.UtcNow)
                    .Send(user.Email, user.UserName, EmailQueue)
                    ;

                    // activity
                    var userActivity = CoreLogic.User.CreateUserActivity(
                        user: user,
                        type: Common.UserActivityType.Settings,
                        comment: "Two factor authentication disabled",
                        ip: agent.Ip,
                        agent: agent.Agent,
                        locale: userLocale
                        );
                    DbContext.UserActivity.Add(userActivity);
                    await DbContext.SaveChangesAsync();
                }
            }

            return(APIResponse.Success(MakeTFASetupView(user)));
        }
Example #16
0
        public async Task <APIResponse> AssetEth([FromBody] AssetEthModel model)
        {
            // validate
            if (BaseValidableModel.IsInvalid(model, out var errFields))
            {
                return(APIResponse.BadRequest(errFields));
            }

            // try parse amount
            if (!BigInteger.TryParse(model.Amount, out var inputAmount) || inputAmount < 1)
            {
                return(APIResponse.BadRequest(nameof(model.Amount), "Invalid amount"));
            }

            // try parse fiat currency
            var exchangeCurrency = FiatCurrency.Usd;

            if (Enum.TryParse(model.Currency, true, out FiatCurrency fc))
            {
                exchangeCurrency = fc;
            }

            // ---

            var rcfg = RuntimeConfigHolder.Clone();
            var user = await GetUserFromDb();

            // ---

            if (!rcfg.Gold.AllowTradingEth)
            {
                return(APIResponse.BadRequest(APIErrorCode.TradingNotAllowed));
            }

            var limits = DepositLimits(rcfg, TradableCurrency.Eth);

            // estimation
            var estimation = await Estimation(rcfg, inputAmount, TradableCurrency.Eth, exchangeCurrency, model.Reversed, 0d, limits.Min, limits.Max);

            if (!estimation.TradingAllowed || estimation.ResultCurrencyAmount < 1)
            {
                return(APIResponse.BadRequest(APIErrorCode.TradingNotAllowed));
            }
            if (estimation.IsLimitExceeded)
            {
                return(APIResponse.BadRequest(APIErrorCode.TradingExchangeLimit, estimation.View.Limits));
            }

            var timeNow = DateTime.UtcNow;

            // request
            var request = new DAL.Models.BuyGoldEth()
            {
                Status           = BuySellGoldRequestStatus.Unconfirmed,
                ExchangeCurrency = exchangeCurrency,
                GoldRateCents    = estimation.CentsPerGoldRate,
                EthRateCents     = estimation.CentsPerAssetRate,
                TimeCreated      = timeNow,
                UserId           = user.Id,
            };

            DbContext.BuyGoldEth.Add(request);
            await DbContext.SaveChangesAsync();

            // get a token from eth2gold service
            var contractToken = "";
            {
                try {
                    var reply = await Bus.Request(
                        Eth2Gold.Subject.Request.OrderCreate,
                        new Eth2Gold.Request.OrderCreate()
                    {
                        ExternalID = (ulong)request.Id,
                    },
                        Eth2Gold.Request.OrderCreateReply.Parser
                        );

                    if (reply.ResultCase == Eth2Gold.Request.OrderCreateReply.ResultOneofCase.Token)
                    {
                        if (reply.Token.ToByteArray().Length != 32)
                        {
                            throw new Exception($"token is length of {reply.Token.ToByteArray().Length}");
                        }
                        contractToken = BitConverter.ToString(reply.Token.ToByteArray()).Replace("-", string.Empty);
                    }
                    else
                    {
                        throw new Exception(reply.Error);
                    }
                } catch (Exception e) {
                    Logger.Error(e, "Failed to get token from eth2gold service");
                    return(APIResponse.Failure(APIErrorCode.InternalServerError));
                }
            }

            var assetPerGold = CoreLogic.Finance.Estimation.AssetPerGold(TradableCurrency.Eth, estimation.CentsPerAssetRate, estimation.CentsPerGoldRate);

            return(APIResponse.Success(
                       new AssetEthView()
            {
                RequestId = request.Id,
                EthRate = estimation.CentsPerAssetRate / 100d,
                GoldRate = estimation.CentsPerGoldRate / 100d,
                EthPerGoldRate = assetPerGold.ToString(),
                Currency = exchangeCurrency.ToString().ToUpper(),
                Expires = ((DateTimeOffset)timeNow.AddHours(1)).ToUnixTimeSeconds(),
                Estimation = estimation.View,
                ContractToken = contractToken,
            }
                       ));
        }