예제 #1
0
        public ActionResult <MilvanethProtocol> AccountRecoveryEmail(MilvanethProtocol data)
        {
            if (!(data?.Data is RecoveryEmail email) || !email.Check())
            {
                return(new MilvanethProtocol
                {
                    Context = null,
                    Data = new ServerResponse
                    {
                        Message = GlobalMessage.DATA_INVALID_INPUT,
                        ReportTime = _time.SafeNow,
                    }
                });
            }

            try
            {
                var user = _context.AccountData.Include(x => x.PrivilegeLevelNavigation).SingleOrDefault(x => x.AccountName == email.Username);

                if (user == null)
                {
                    return(new MilvanethProtocol
                    {
                        Context = null,
                        Data = new ServerResponse
                        {
                            Message = GlobalMessage.DATA_NO_SUCH_USER,
                            ReportTime = _time.SafeNow,
                        }
                    });
                }

                _auth.EnsureAccount(user, new PrivilegeConfig {
                    AccountOperation = true
                }, GlobalOperation.ACCOUNT_RECOVERY_EMAIL, 0, "Recovery account via account/recovery with email", _accessor.GetIp());

                if (user.Email == null)
                {
                    return(new MilvanethProtocol
                    {
                        Context = null,
                        Data = new ServerResponse
                        {
                            Message = GlobalMessage.DATA_NO_EMAIL_RECORDED,
                            ReportTime = _time.SafeNow,
                        }
                    });
                }

                var record = _context.EmailVerifyCode.OrderByDescending(x => x.SendTime).FirstOrDefault();

                if (string.IsNullOrEmpty(email.Code))
                {
                    if (record != null && record.SendTime.AddSeconds(GlobalConfig.ACCOUNT_VERIFY_CODE_COOLDOWN) > _time.UtcNow)
                    {
                        return(new MilvanethProtocol
                        {
                            Context = null,
                            Data = new ServerResponse
                            {
                                Message = GlobalMessage.RATE_LIMIT,
                                ReportTime = _time.SafeNow,
                            }
                        });
                    }

                    if (user.Email.Equals(email.Email, StringComparison.InvariantCultureIgnoreCase))
                    {
                        string code;
                        if (record != null && record.ValidTo > _time.UtcNow.AddSeconds(3 * GlobalConfig.ACCOUNT_VERIFY_CODE_COOLDOWN))
                        {
                            code = record.Code;

                            record.SendTime = _time.UtcNow;

                            _context.EmailVerifyCode.Update(record);
                        }
                        else
                        {
                            using (var cryptoRng = new RNGCryptoServiceProvider())
                            {
                                var seed = new byte[5];
                                cryptoRng.GetBytes(seed);
                                code = Helper.ToCode(seed);
                            }

                            _context.EmailVerifyCode.Add(new EmailVerifyCode
                            {
                                AccountId   = user.AccountId,
                                Email       = user.Email,
                                FailedRetry = 0,
                                ValidTo     = _time.UtcNow.AddSeconds(GlobalConfig.ACCOUNT_VERIFY_CODE_LIFE_TIME),
                                Code        = code,
                                SendTime    = _time.UtcNow
                            });
                        }

                        _context.SaveChanges();

                        _mail.SendCode(user.Email, user.DisplayName, code);
                    }
                    else
                    {
                        var rand = new Random();
                        // prevent timing attack
                        Thread.Sleep(1000 + rand.Next(3000));
                    }

                    return(new MilvanethProtocol
                    {
                        Context = null,
                        Data = new ServerResponse
                        {
                            Message = GlobalMessage.OK_SUCCESS,
                            ReportTime = _time.SafeNow,
                        }
                    });
                }

                if (record == null || record.ValidTo < _time.UtcNow || record.FailedRetry >= 5)
                {
                    return(new MilvanethProtocol
                    {
                        Context = null,
                        Data = new ServerResponse
                        {
                            Message = GlobalMessage.DATA_INVALID_INPUT,
                            ReportTime = _time.SafeNow,
                        }
                    });
                }

                if (record.Code != email.Code.ToUpperInvariant())
                {
                    record.FailedRetry += 1;

                    _context.EmailVerifyCode.Update(record);

                    _context.SaveChanges();

                    return(new MilvanethProtocol
                    {
                        Context = null,
                        Data = new ServerResponse
                        {
                            Message = GlobalMessage.DATA_INVALID_CAPTCHA,
                            ReportTime = _time.SafeNow,
                        }
                    });
                }

                var recovery = _api.Sign(_changeToken, 1, user, _time.UtcNow, _time.UtcNow.AddSeconds(GlobalConfig.TOKEN_ACCOUNT_LIFE_TIME));

                return(new MilvanethProtocol
                {
                    Context = null,
                    Data = new ServerResponse
                    {
                        Message = GlobalMessage.OK_SUCCESS,
                        ReportTime = _time.SafeNow,
                        AuthToken = recovery.Key,
                    }
                });
            }
            catch (Exception e)
            {
                Log.Error(e, "Error in ACCOUNT/RECOVERYMAIL");
                return(new MilvanethProtocol
                {
                    Context = null,
                    Data = new ServerResponse
                    {
                        Message = GlobalMessage.OP_INVALID,
                        ReportTime = _time.SafeNow,
                    }
                });
            }
        }