コード例 #1
0
 public void Setup()
 {
     _controllerName   = "Account";
     _actionName       = "Register";
     _httpMethod       = "POST";
     _expectedFormKeys = new List <string>()
     {
         "TestField1", "TestField2"
     };
     _logger       = MockRepository.GenerateMock <ILogger>();
     _userIdentity = MockRepository.GenerateMock <IUserIdentity>();
     _userIdentity.Stub(a => a.GetRequester(Arg <Controller> .Is.Anything, Arg <AppSensorDetectionPointKind> .Is.Anything))
     .Return(null)                     // will be ignored but still the API requires it
     .WhenCalled(_ => {
         var appSensorDetectionPointKind = (AppSensorDetectionPointKind?)_.Arguments[1];
         var requester = new Requester()
         {
             AppSensorDetectionPoint = appSensorDetectionPointKind
         };
         _.ReturnValue = requester;
     });
     _sut         = new AppSensor(_userIdentity, _logger);
     _httpSession = MockRepository.GenerateMock <HttpSessionStateBase>();
     _httpContext = MockRepository.GenerateMock <HttpContextBase>();
     _httpRequest = MockRepository.GenerateMock <HttpRequestBase>();
     _httpContext.Stub(c => c.Request).Return(_httpRequest);
     _httpContext.Stub(c => c.Session).Return(_httpSession);
     _controller = new HomeController();
     _controller.ControllerContext = new ControllerContext(_httpContext, new RouteData(), _controller);
 }
コード例 #2
0
        // GET api/values
        public AppSensor Get()
        {
            var sensor = new AppSensor();

            sensor.Update();
            return(sensor);
        }
コード例 #3
0
        public async Task <ActionResult> ChangeEmailAddressAsync(ChangeEmailAddressViewModel model)
        {
            var userId = UserIdentity.GetUserId(this);
            var user   = _context.User.FirstOrDefault(u => u.Id == userId && u.Enabled && u.EmailVerified && u.Approved);

            AppSensor.ValidateFormData(this, new List <string> {
                "NewEmailAddress", "Password"
            });
            if (user == null)
            {
                return(HttpNotFound());
            }
            if (ModelState.IsValid)
            {
                if (_context.User.Any(a => a.Id != user.Id && model.NewEmailAddress == a.UserName))
                {
                    ModelState.AddModelError("NewEmailAddress", "This email address is already in use");
                    Logger.Information("Failed Account ChangeEmailAddress Post, Username already exists");
                }
                else
                {
                    var logonResult = await _userManager.TryLogOnAsync(UserIdentity.GetUserName(this), model.Password);

                    if (logonResult.Success)
                    {
                        user.NewEmailAddressToken = Guid.NewGuid().ToString().Replace("-", "");
                        user.NewEmailAddressRequestExpiryDateUtc = DateTime.UtcNow.AddMinutes(15);
                        user.NewEmailAddress = model.NewEmailAddress;
                        // Send change username with link to recover password form
                        string emailBody = EmailTemplates.ChangeEmailAddressPendingBodyText(user.FirstName, user.LastName,
                                                                                            _configuration.ApplicationName, _configuration.WebsiteBaseUrl, user.NewEmailAddressToken);
                        string emailSubject = $"{_configuration.ApplicationName} - Complete the change email address process";
                        _services.SendEmail(_configuration.DefaultFromEmailAddress, new List <string>()
                        {
                            user.UserName
                        }, null, null,
                                            emailSubject, emailBody, true);
                        user.UserLogs.Add(new UserLog
                        {
                            Description = $"Change email address request started to change from {user.UserName} to {user.NewEmailAddress}"
                        });
                        await _context.SaveChangesAsync();

                        return(View("ChangeEmailAddressPending"));
                    }
                    else
                    {
                        Logger.Information("Failed Account ChangeEmailAddress Post, Password incorrect by requester {@requester}",
                                           UserIdentity.GetRequester(this, AppSensorDetectionPointKind.Ae1));
                        ModelState.AddModelError("Password", "The password is not correct");
                    }
                }
            }
            else
            {
                AppSensor.InspectModelStateErrors(this);
            }
            return(View("ChangeEmailAddress", new ChangeEmailAddressViewModel(user.UserName, user.NewEmailAddress, user.NewEmailAddressRequestExpiryDateUtc)));
        }
コード例 #4
0
        public async Task <ActionResult> ChangePasswordAsync(ChangePasswordViewModel model)
        {
            ViewBag.ReturnUrl = Url.Action("ChangePassword");
            var requester        = UserIdentity.GetRequester(this);
            var recaptchaSuccess = true;

            if (_configuration.HasRecaptcha)
            {
                recaptchaSuccess = _recaptcha.ValidateRecaptcha(this);
            }
            AppSensor.ValidateFormData(this, new List <string> {
                "ConfirmPassword", "OldPassword", "NewPassword"
            });
            if (recaptchaSuccess)
            {
                var userId = UserIdentity.GetUserId(this);
                var user   = _context.User.FirstOrDefault(u => u.Id == userId);
                if (user != null)
                {
                    var result = await _userManager.ChangePasswordAsync(UserIdentity.GetUserId(this), model.OldPassword,
                                                                        model.NewPassword);

                    if (result.Succeeded)
                    {
                        // Email recipient with password change acknowledgement
                        var emailBody = EmailTemplates.ChangePasswordCompletedBodyText(user.FirstName, user.LastName,
                                                                                       _configuration.ApplicationName);
                        var emailSubject = $"{_configuration.ApplicationName} - Password change confirmation";
                        _services.SendEmail(_configuration.DefaultFromEmailAddress, new List <string> {
                            user.UserName
                        },
                                            null, null, emailSubject, emailBody, true);
                        _context.SaveChanges();
                        _formsAuth.SignOut();
                        _userManager.SignOut();
                        _httpCache.RemoveFromCache($"MustChangePassword-{userId}");
                        Session.Abandon();
                        Logger.Debug("Account Logoff due to password change");
                        return(View("ChangePasswordSuccess"));
                    }

                    Logger.Information("Failed Account ChangePassword Post by requester {@requester}", requester);
                    AddErrors(result);
                }
                else
                {
                    return(HttpNotFound());
                }
            }
            else
            {
                Logger.Information("Failed Account Change Password Post Recaptcha failed by requester {@requester}",
                                   requester);
            }

            return(View("ChangePassword", model));
        }
コード例 #5
0
        public async Task <ActionResult> RecoverAsync(RecoverViewModel model)
        {
            var requester = UserIdentity.GetRequester(this);
            var userName  = model.UserName;

            AppSensor.ValidateFormData(this, new List <string> {
                "UserName"
            });
            if (ModelState.IsValid)
            {
                var user = _context.User.SingleOrDefault(u =>
                                                         u.UserName == model.UserName && u.Enabled && u.EmailVerified && u.Approved);
                if (_configuration.HasRecaptcha)
                {
                    if (!_recaptcha.ValidateRecaptcha(this))
                    {
                        Logger.Information("Failed Account Recover Post Recaptcha failed by requester {@requester}",
                                           requester);
                        return(View("Recover", model));
                    }
                }

                if (user != null)
                {
                    user.PasswordResetToken         = Guid.NewGuid().ToString().Replace("-", "");
                    user.PasswordResetExpiryDateUtc = DateTime.UtcNow.AddMinutes(15);
                    // Send recovery email with link to recover password form
                    var emailBody = EmailTemplates.ChangePasswordPendingBodyText(user.FirstName, user.LastName,
                                                                                 _configuration.ApplicationName, _configuration.WebsiteBaseUrl, user.PasswordResetToken);
                    var emailSubject = $"{_configuration.ApplicationName} - Complete the password recovery process";
                    _services.SendEmail(_configuration.DefaultFromEmailAddress, new List <string> {
                        user.UserName
                    }, null,
                                        null, emailSubject, emailBody, true);
                    user.UserLogs.Add(new UserLog {
                        Description = "Password reset link generated and sent"
                    });
                    await _context.SaveChangesAsync();
                }
                else
                {
                    Logger.Information("Failed Account Recover Post UserName {userName} by requester {@requester}",
                                       userName, requester);
                    return(View("RecoverSuccess"));
                }
            }
            else
            {
                AppSensor.InspectModelStateErrors(this);
            }

            return(View("RecoverSuccess"));
        }
コード例 #6
0
        public async Task <ActionResult> LogOnAsync(LogOnViewModel model, string returnUrl)
        {
            var requester = UserIdentity.GetRequester(this);
            var userName  = model.UserName;

            AppSensor.ValidateFormData(this, new List <string> {
                "UserName", "Password"
            });
            if (ModelState.IsValid)
            {
                var logonResult = await _userManager.TryLogOnAsync(model.UserName, model.Password);

                if (logonResult.Success)
                {
                    var userId = await _userManager.LogOnAsync(logonResult.UserName, model.RememberMe);

                    Logger.Information("Successful Account Logon Post for username {userName}", userName,
                                       model.UserName);
                    if (logonResult.MustChangePassword)
                    {
                        _httpCache.SetCache($"MustChangePassword-{userId}", "");
                    }
                    return(RedirectToLocal(returnUrl));
                }

                // SECURE: Increasing wait time (with random component) for each successive logon failure (instead of locking out)
                _services.Wait(500 + logonResult.FailedLogonAttemptCount * 200 + new Random().Next(4) * 200);
                ModelState.AddModelError("", "Invalid credentials or the account is locked");
                requester.AppSensorDetectionPoint = AppSensorDetectionPointKind.Ae1;
                Logger.Information(
                    "Failed Account Logon Post for username {userName} attempt by requester {@requester}", userName,
                    requester);
                if (logonResult.IsCommonUserName)
                {
                    requester.AppSensorDetectionPoint = AppSensorDetectionPointKind.Ae12;
                    Logger.Information(
                        "Failed Account Logon Post Common username {userName} attempt by requester {@requester}",
                        userName, requester);
                }
            }
            else
            {
                AppSensor.InspectModelStateErrors(this);
            }

            // If we got this far, something failed, redisplay form
            return(View("LogOn", model));
        }
コード例 #7
0
        public async Task <ActionResult> RegisterAsync(FormCollection collection)
        {
            var user            = new User();
            var password        = collection["Password"];
            var confirmPassword = collection["ConfirmPassword"];

            AppSensor.ValidateFormData(this, new List <string> {
                "Password", "ConfirmPassword", "User.FirstName", "User.LastName", "User.UserName", "User.SecurityQuestionLookupItemId", "User.SecurityAnswer"
            });
            var requester = UserIdentity.GetRequester(this);

            if (ModelState.IsValid)
            {
                var propertiesToUpdate = new[]
                {
                    "FirstName", "LastName", "UserName", "SecurityQuestionLookupItemId", "SecurityAnswer"
                };
                if (TryUpdateModel(user, "User", propertiesToUpdate, collection))
                {
                    var recaptchaSuccess = true;
                    if (_configuration.HasRecaptcha)
                    {
                        recaptchaSuccess = _recaptcha.ValidateRecaptcha(this);
                    }
                    var userName = user.UserName;
                    if (recaptchaSuccess)
                    {
                        var result = await _userManager.CreateAsync(user.UserName, user.FirstName, user.LastName, password, confirmPassword,
                                                                    user.SecurityQuestionLookupItemId, user.SecurityAnswer);

                        if (result.Succeeded || result.Errors.Any(e => e == "Username already registered"))
                        {
                            user = _context.User.First(u => u.UserName == user.UserName);
                            // Email the user to complete the email verification process or inform them of a duplicate registration and would they like to change their password
                            string emailBody;
                            string emailSubject;
                            if (result.Succeeded)
                            {
                                emailSubject = $"{_configuration.ApplicationName} - Complete your registration";
                                emailBody    = EmailTemplates.RegistrationPendingBodyText(user.FirstName, user.LastName, _configuration.ApplicationName, _configuration.WebsiteBaseUrl, user.EmailConfirmationToken);
                                Logger.Information("Successful Account Register Post for username {userName} by requester {@requester}", userName, requester);
                            }
                            else
                            {
                                emailSubject = $"{_configuration.ApplicationName} - Duplicate Registration";
                                emailBody    = EmailTemplates.RegistrationDuplicatedBodyText(user.FirstName, user.LastName, _configuration.ApplicationName, _configuration.WebsiteBaseUrl);
                                Logger.Information("Duplicate Account Register Post for username {userName} by requester {@requester}", userName, requester);
                            }
                            _services.SendEmail(_configuration.DefaultFromEmailAddress, new List <string> {
                                user.UserName
                            }, null, null, emailSubject, emailBody, true);
                            return(View("RegisterSuccess"));
                        }
                        else
                        {
                            var errorMessage = result.Errors.FirstOrDefault();
                            Logger.Information("Failed Account Register Post of username {userName} with message {errorMessage}, user creation failed by requester {@requester}", userName, errorMessage, requester);
                            AddErrors(result);
                        }
                    }
                    else
                    {
                        Logger.Information("Failed Account Register Post Recaptcha failed by requester {@requester}", requester);
                    }
                }
            }
            else
            {
                AppSensor.InspectModelStateErrors(this);
            }
            var securityQuestions = _context.LookupItem.Where(l => l.LookupTypeId == Consts.LookupTypeId.SecurityQuestion && l.IsHidden == false).OrderBy(o => o.Ordinal).ToList();
            var registerViewModel = new RegisterViewModel(confirmPassword, _configuration.HasRecaptcha, password, user, securityQuestions);

            return(View("Register", registerViewModel));
        }
コード例 #8
0
        public async Task <ActionResult> ChangeSecurityInformationAsync(ChangeSecurityInformationViewModel model)
        {
            string errorMessage = "";
            var    requester    = UserIdentity.GetRequester(this);

            AppSensor.ValidateFormData(this, new List <string> {
                "SecurityQuestionLookupItemId", "SecurityAnswer", "SecurityAnswerConfirm", "Password"
            });
            if (ModelState.IsValid)
            {
                var recaptchaSuccess = true;
                if (_configuration.HasRecaptcha)
                {
                    recaptchaSuccess = _recaptcha.ValidateRecaptcha(this);
                }
                var logonResult = await _userManager.TryLogOnAsync(UserIdentity.GetUserName(this), model.Password);

                if (recaptchaSuccess)
                {
                    if (logonResult.Success)
                    {
                        if (model.SecurityAnswer == model.SecurityAnswerConfirm)
                        {
                            var user = _context.User.First(u => u.UserName == logonResult.UserName);
                            _encryption.Encrypt(_configuration.EncryptionPassword, _configuration.EncryptionIterationCount, model.SecurityAnswer, out var encryptedSecurityAnswerSalt, out var encryptedSecurityAnswer);
                            user.SecurityAnswer               = encryptedSecurityAnswer;
                            user.SecurityAnswerSalt           = encryptedSecurityAnswerSalt;
                            user.SecurityQuestionLookupItemId = model.SecurityQuestionLookupItemId;
                            user.UserLogs.Add(new UserLog {
                                Description = "User Changed Security Information"
                            });
                            await _context.SaveChangesAsync();

                            // Email the user to complete the email verification process or inform them of a duplicate registration and would they like to change their password
                            string emailSubject = $"{_configuration.ApplicationName} - Security Information Changed";
                            string emailBody    = EmailTemplates.ChangeSecurityInformationCompletedBodyText(user.FirstName, user.LastName, _configuration.ApplicationName);
                            _services.SendEmail(_configuration.DefaultFromEmailAddress, new List <string>()
                            {
                                logonResult.UserName
                            }, null, null, emailSubject, emailBody, true);
                            return(View("ChangeSecurityInformationSuccess"));
                        }
                        else
                        {
                            Logger.Information("Failed Account ChangeSecurityInformation Post, security answers do not match by requester {@requester}", requester);
                            errorMessage = "The security question answers do not match";
                        }
                    }
                    else
                    {
                        Logger.Information("Failed Account ChangeSecurityInformation Post, security information incorrect or account locked out by requester {@requester}", requester);
                        errorMessage = "Security information incorrect or account locked out";
                    }
                }
                else
                {
                    AppSensor.InspectModelStateErrors(this);
                }
            }
            var securityQuestions = _context.LookupItem.Where(l => l.LookupTypeId == Consts.LookupTypeId.SecurityQuestion && l.IsHidden == false).OrderBy(o => o.Ordinal).ToList();
            var changeSecurityInformationViewModel = new ChangeSecurityInformationViewModel(errorMessage, _configuration.HasRecaptcha, securityQuestions);

            return(View("ChangeSecurityInformation", changeSecurityInformationViewModel));
        }
コード例 #9
0
        public async Task <ActionResult> RecoverPasswordAsync(RecoverPasswordViewModel recoverPasswordModel)
        {
            var user      = _context.User.SingleOrDefault(u => u.Id == recoverPasswordModel.Id);
            var id        = recoverPasswordModel.Id;
            var requester = UserIdentity.GetRequester(this);

            AppSensor.ValidateFormData(this, new List <string> {
                "UserName", "SecurityAnswer", "Password", "ConfirmPassword", "Id", "PasswordResetToken"
            });
            if (user == null)
            {
                HandleErrorInfo error = new HandleErrorInfo(new Exception("INFO: The user is either not valid, not approved or not active"), "Account", "RecoverPassword");
                Logger.Information("Failed Account RecoverPassword Post, recover attempted for a user id {id} which does not exist by requester {@requester}", id, requester);
                return(View("Error", error));
            }
            if (!(user.Enabled))
            {
                HandleErrorInfo error = new HandleErrorInfo(new Exception("INFO: The user is either not valid, not approved or not active"), "Account", "Recover");
                Logger.Information("Failed Account RecoverPassword Post, account user id {id} is not approved or active by requester {@requester}", id, requester);
                return(View("Error", error));
            }

            _encryption.Decrypt(_configuration.EncryptionPassword, user.SecurityAnswerSalt, _configuration.EncryptionIterationCount, user.SecurityAnswer, out var decryptedSecurityAnswer);
            if (recoverPasswordModel.SecurityAnswer != decryptedSecurityAnswer)
            {
                ModelState.AddModelError("SecurityAnswer", "The security answer is incorrect");
                Logger.Information("Failed Account RecoverPassword Post, security answer is incorrect by requester {@requester}", requester);
                return(View("RecoverPassword", recoverPasswordModel));
            }
            if (recoverPasswordModel.Password != recoverPasswordModel.ConfirmPassword)
            {
                ModelState.AddModelError("ConfirmPassword", "The passwords do not match");
                Logger.Information("Failed Account RecoverPassword Post, passwords do not match by requester {@requester}", requester);
                return(View("RecoverPassword", recoverPasswordModel));
            }
            var recaptchaSuccess = true;

            if (_configuration.HasRecaptcha)
            {
                recaptchaSuccess = _recaptcha.ValidateRecaptcha(this);
            }
            if (recaptchaSuccess)
            {
                if (ModelState.IsValid)
                {
                    var result = await _userManager.ChangePasswordFromTokenAsync(user.Id, recoverPasswordModel.PasswordResetToken, recoverPasswordModel.Password);

                    if (result.Succeeded)
                    {
                        await _context.SaveChangesAsync();

                        _httpCache.RemoveFromCache(string.Concat("MustChangePassword-", user.Id));
                        await _userManager.LogOnAsync(user.UserName, false);

                        Logger.Information("Successful RecoverPassword Post by requester {@requester}", requester);
                        return(View("RecoverPasswordSuccess"));
                    }
                    AddErrors(result);
                    Logger.Information("Failed Account RecoverPassword Post, change password async failed by requester {@requester}", requester);
                    return(View("RecoverPassword", recoverPasswordModel));
                }
                ModelState.AddModelError("", "Password change was not successful");
                AppSensor.InspectModelStateErrors(this);
            }
            else
            {
                Logger.Information("Failed Account Recover Password Post Recaptcha failed by requester {@requester}", requester);
            }
            return(View("RecoverPassword", recoverPasswordModel));
        }
コード例 #10
0
        public ActionResult Edit(int id, FormCollection collection)
        {
            var isAdmin       = UserIdentity.IsUserInRole(this, "Admin");
            var currentUserId = UserIdentity.GetUserId(this);
            var isOwnProfile  = currentUserId == id;
            var users         = _context.User.Where(u => u.Id == id);

            if (users.ToList().Count == 0)
            {
                return(new HttpNotFoundResult());
            }
            var user = users.Single();

            ViewBag.StatusMessage = "";
            var requester = UserIdentity.GetRequester(this);

            // SECURE: Check user should have access to this account
            if (!isAdmin && !isOwnProfile)
            {
                Logger.Information("Failed User Edit Post, user modification was not permitted for access rights by requester {@requester}", requester);
                return(new HttpNotFoundResult());
            }
            ViewBag.StatusMessage = "";
            var previousUserName   = user.UserName;
            var propertiesToUpdate = new List <string>
            {
                "FirstName", "LastName", "TelNoHome", "TelNoMobile", "TelNoWork", "Title",
                "Town", "Postcode", "SkypeName"
            };
            var expectedFields = new List <string> {
                "IsAccessingUserAnAdmin", "IsOwnProfile", "IsCurrentUserAnAdmin", "User.Id"
            };

            if (isAdmin)
            {
                if (currentUserId != user.Id)
                {
                    // Otherwise these fields will be disabled on the front page
                    propertiesToUpdate.AddRange(new List <string> {
                        "Approved", "EmailVerified", "Enabled"
                    });
                }
                propertiesToUpdate.AddRange(new List <string> {
                    "UserName"
                });
            }
            propertiesToUpdate.ForEach(a => expectedFields.Add(a));
            AppSensor.ValidateFormData(this, expectedFields);
            if (TryUpdateModel(user, "User", propertiesToUpdate.ToArray(), collection))
            {
                if (_context.User.Any(a => a.Id != user.Id && user.UserName == a.UserName))
                {
                    ModelState.AddModelError("User.UserName", "This username is already in use");
                }
                else
                {
                    if (user.UserName != previousUserName)
                    {
                        user.UserLogs.Add(new UserLog
                        {
                            Description = $"Username/Email was changed from {previousUserName} by {UserIdentity.GetUserName(this)}"
                        });
                        string emailSubject = $"{_configuration.ApplicationName} - Change email address process completed";
                        string emailBody    = EmailTemplates.ChangeEmailAddressCompletedBodyText(user.FirstName, user.LastName, _configuration.ApplicationName, previousUserName, user.UserName);
                        _services.SendEmail(_configuration.DefaultFromEmailAddress, new List <string> {
                            user.UserName
                        }, null, null, emailSubject, emailBody, true);
                    }
                    _context.SaveChanges();
                    if (!isOwnProfile && isAdmin)
                    {
                        return(RedirectToAction("Index", "User"));
                    }
                    ViewBag.StatusMessage = "Your account information has been saved";
                }
            }
            else
            {
                AppSensor.InspectModelStateErrors(this);
            }

            return(View("Edit", new UserViewModel(UserIdentity.GetUserId(this), isAdmin, user)));
        }