diff --git a/LearningHub.Nhs.WebUI/Controllers/LoginWizardController.cs b/LearningHub.Nhs.WebUI/Controllers/LoginWizardController.cs index fab43e688..ab0d07dce 100644 --- a/LearningHub.Nhs.WebUI/Controllers/LoginWizardController.cs +++ b/LearningHub.Nhs.WebUI/Controllers/LoginWizardController.cs @@ -144,31 +144,7 @@ public async Task Index(string returnUrl) if (currentStage?.Id == (int)LoginWizardStageEnum.SecurityQuestions) { - SecurityQuestionsViewModel securityQuestions = await this.loginWizardService.GetSecurityQuestionsModel(this.CurrentUserId); - - while (securityQuestions.UserSecurityQuestions.Count < this.Settings.SecurityQuestionsToAsk) - { - securityQuestions.UserSecurityQuestions.Add(new UserSecurityQuestionViewModel()); - } - - foreach (var answer in securityQuestions.UserSecurityQuestions) - { - if (!string.IsNullOrEmpty(answer.SecurityQuestionAnswerHash)) - { - answer.SecurityQuestionAnswerHash = "********"; - } - } - - this.TempData.Clear(); - var securityViewModel = new SecurityViewModel() - { - SecurityQuestions = securityQuestions.SecurityQuestions, - UserSecurityQuestions = securityQuestions.UserSecurityQuestions, - }; - - await this.multiPageFormService.SetMultiPageFormData(securityViewModel, MultiPageFormDataFeature.EditRegistrationPrompt, this.TempData); - - return this.RedirectToAction("SelectSecurityQuestion", new RouteValueDictionary { { "questionIndex", 0 }, { "returnUrl", returnUrl } }); + return this.RedirectToAction("SelectSecurityQuestions", new { returnUrl }); } if (currentStage?.Id == (int)LoginWizardStageEnum.JobRole || currentStage?.Id == (int)LoginWizardStageEnum.PlaceOfWork || currentStage?.Id == (int)LoginWizardStageEnum.PersonalDetails) @@ -213,11 +189,19 @@ await this.multiPageFormService.SetMultiPageFormData( { return this.RedirectToAction("AccountInformationNeeded"); } + else if (currentStage?.Id == (int)LoginWizardStageEnum.JobRole || currentStage?.Id == (int)LoginWizardStageEnum.PlaceOfWork) + { + return this.RedirectToAction("MyEmploymentDetails", "MyAccount", new { returnUrl, checkDetails = true }); + } else { return this.RedirectToAction("Index", "MyAccount", new { returnUrl, checkDetails = true }); } } + else if (currentStage?.Id == (int)LoginWizardStageEnum.JobRole || currentStage?.Id == (int)LoginWizardStageEnum.PlaceOfWork) + { + return this.RedirectToAction("MyEmploymentDetails", "MyAccount", new { returnUrl, checkDetails = true }); + } else { return this.RedirectToAction("Index", "MyAccount", new { returnUrl, checkDetails = true }); @@ -585,6 +569,106 @@ public async Task AccountConfirmationPost() return this.RedirectToAction("Index", new RouteValueDictionary { { "returnUrl", accountModel.WizardReturnUrl } }); } + /// + /// Action for starting the security question multiPageForm stage of the wizard. + /// + /// The URL to return to after the login wizard has been completed. + /// The . + [ResponseCache(CacheProfileName = "Never")] + public async Task SelectSecurityQuestions(string returnUrl) + { + MyAcountSecurityQuestionsViewModel securityViewModel = new MyAcountSecurityQuestionsViewModel(); + var result = await this.loginWizardService.GetSecurityQuestionsModel(this.CurrentUserId); + + if (result != null) + { + securityViewModel.FirstSecurityQuestions = SelectListHelper.MapSelectListWithSelection(result.SecurityQuestions, Convert.ToString(securityViewModel.SelectedFirstQuestionId)); + securityViewModel.SecondSecurityQuestions = SelectListHelper.MapSelectListWithSelection(result.SecurityQuestions, Convert.ToString(securityViewModel.SelectedSecondQuestionId)); + } + + this.ViewBag.ReturnUrl = returnUrl; + return this.View("SecurityQuestionsDetails", securityViewModel); + } + + /// + /// Action for choosing security questions. + /// + /// The MyAcountSecurityQuestionsViewModel. + /// The URL to return to after the login wizard has been completed. + /// The . + [HttpPost] + [ResponseCache(CacheProfileName = "Never")] + public async Task UpdateSecurityQuestionPost(MyAcountSecurityQuestionsViewModel model, string returnUrl) + { + MyAcountSecurityQuestionsViewModel securityViewModel = new MyAcountSecurityQuestionsViewModel(); + var result = await this.loginWizardService.GetSecurityQuestionsModel(this.CurrentUserId); + + if (result != null) + { + securityViewModel.FirstSecurityQuestions = SelectListHelper.MapSelectListWithSelection(result.SecurityQuestions, Convert.ToString(securityViewModel.SelectedFirstQuestionId)); + securityViewModel.SecondSecurityQuestions = SelectListHelper.MapSelectListWithSelection(result.SecurityQuestions, Convert.ToString(securityViewModel.SelectedSecondQuestionId)); + } + + if (model != null) + { + if (model.SelectedFirstQuestionId == model.SelectedSecondQuestionId) + { + this.ModelState.AddModelError("DuplicateQuestion", CommonValidationErrorMessages.DuplicateQuestion); + } + + if (model.SelectedFirstQuestionId > 0 && string.IsNullOrEmpty(model.SecurityFirstQuestionAnswerHash)) + { + this.ModelState.AddModelError(nameof(model.SecurityFirstQuestionAnswerHash), CommonValidationErrorMessages.InvalidSecurityQuestionAnswer); + } + + if (model.SelectedSecondQuestionId > 0 && string.IsNullOrEmpty(model.SecuritySecondQuestionAnswerHash)) + { + this.ModelState.AddModelError(nameof(model.SecuritySecondQuestionAnswerHash), CommonValidationErrorMessages.InvalidSecurityQuestionAnswer); + } + + if (this.ModelState.IsValid) + { + var userSecurityQuestions = new List + { + new UserSecurityQuestionViewModel + { + SecurityQuestionId = model.SelectedFirstQuestionId, + SecurityQuestionAnswerHash = model.SecurityFirstQuestionAnswerHash, + UserId = this.CurrentUserId, + }, + new UserSecurityQuestionViewModel + { + SecurityQuestionId = model.SelectedSecondQuestionId, + SecurityQuestionAnswerHash = model.SecuritySecondQuestionAnswerHash, + UserId = this.CurrentUserId, + }, + }; + + await this.userService.UpdateUserSecurityQuestions(userSecurityQuestions); + + // Mark stage complete. + var (cacheExists, loginWizard) = await this.cacheService.TryGetAsync(this.LoginWizardCacheKey); + + if (cacheExists) + { + await this.CompleteLoginWizardStageAsync(loginWizard, LoginWizardStageEnum.SecurityQuestions); + this.TempData.Clear(); + return this.RedirectToAction("Index", new RouteValueDictionary { { "returnUrl", returnUrl } }); + } + + this.TempData.Clear(); + return this.Redirect("/"); + } + else + { + this.ViewBag.ReturnUrl = returnUrl; + return this.View("SecurityQuestionsDetails", securityViewModel); + } + } + + return this.View("SecurityQuestionsDetails", securityViewModel); + } + /// /// The complete login wizard stage. /// diff --git a/LearningHub.Nhs.WebUI/Controllers/MyAccountController.cs b/LearningHub.Nhs.WebUI/Controllers/MyAccountController.cs index f1c3e1758..feebe7124 100644 --- a/LearningHub.Nhs.WebUI/Controllers/MyAccountController.cs +++ b/LearningHub.Nhs.WebUI/Controllers/MyAccountController.cs @@ -138,11 +138,35 @@ public async Task Index(string returnUrl = null, bool? checkDetai /// /// MyEmploymentDetails. /// + /// Whether to check account details. /// IActionResult. [HttpGet] [Route("myaccount-employement")] - public async Task MyEmploymentDetails() + public async Task MyEmploymentDetails(bool? checkDetails = false) { + string loginWizardCacheKey = $"{this.CurrentUserId}:LoginWizard"; + var (cacheExists, loginWizard) = await this.cacheService.TryGetAsync(loginWizardCacheKey); + + if (checkDetails == true || cacheExists) + { + this.ViewBag.CheckDetails = true; + + var rules = loginWizard.LoginWizardStagesRemaining.SelectMany(l => l.LoginWizardRules.Where(r => r.Required)); + foreach (var rule in rules) + { + this.ModelState.AddModelError(string.Empty, rule.Description); + } + + if (this.TempData.ContainsKey("IsJobRoleRequired")) + { + if (this.TempData["IsJobRoleRequired"] != null && (bool)this.TempData["IsJobRoleRequired"] == true) + { + this.ModelState.AddModelError(string.Empty, CommonValidationErrorMessages.RoleRequired); + this.TempData["IsJobRoleRequired"] = null; + } + } + } + var employmentDetails = await this.userService.GetMyEmploymentDetailsAsync(); return this.View("MyEmployment", employmentDetails); } @@ -312,13 +336,13 @@ public async Task MyAccountSecurityQuestionsDetails([FromQuery] M return this.View("MyAccountSecurityQuestionsDetails", securityViewModel); } - if (viewModel.SelectedFirstQuestionId > 0) + if (viewModel.SelectedFirstQuestionId > 0 && string.IsNullOrEmpty(viewModel.SecurityFirstQuestionAnswerHash)) { this.ModelState.AddModelError(nameof(viewModel.SecurityFirstQuestionAnswerHash), CommonValidationErrorMessages.InvalidSecurityQuestionAnswer); return this.View("MyAccountSecurityQuestionsDetails", securityViewModel); } - if (viewModel.SelectedSecondQuestionId > 0) + if (viewModel.SelectedSecondQuestionId > 0 && string.IsNullOrEmpty(viewModel.SecuritySecondQuestionAnswerHash)) { this.ModelState.AddModelError(nameof(viewModel.SecuritySecondQuestionAnswerHash), CommonValidationErrorMessages.InvalidSecurityQuestionAnswer); return this.View("MyAccountSecurityQuestionsDetails", securityViewModel); @@ -331,15 +355,15 @@ public async Task MyAccountSecurityQuestionsDetails([FromQuery] M new UserSecurityQuestionViewModel { Id = securityViewModel.UserSecurityFirstQuestionId, - SecurityQuestionId = securityViewModel.SelectedFirstQuestionId, - SecurityQuestionAnswerHash = securityViewModel.SecurityFirstQuestionAnswerHash, + SecurityQuestionId = viewModel.SelectedFirstQuestionId, + SecurityQuestionAnswerHash = viewModel.SecurityFirstQuestionAnswerHash, UserId = this.CurrentUserId, }, new UserSecurityQuestionViewModel { Id = securityViewModel.UserSecuritySecondQuestionId, - SecurityQuestionId = securityViewModel.SelectedSecondQuestionId, - SecurityQuestionAnswerHash = securityViewModel.SecuritySecondQuestionAnswerHash, + SecurityQuestionId = viewModel.SelectedSecondQuestionId, + SecurityQuestionAnswerHash = viewModel.SecuritySecondQuestionAnswerHash, UserId = this.CurrentUserId, }, }; diff --git a/LearningHub.Nhs.WebUI/Styles/nhsuk/layout.scss b/LearningHub.Nhs.WebUI/Styles/nhsuk/layout.scss index 4448f4ee9..a0fa1c85d 100644 --- a/LearningHub.Nhs.WebUI/Styles/nhsuk/layout.scss +++ b/LearningHub.Nhs.WebUI/Styles/nhsuk/layout.scss @@ -377,7 +377,10 @@ li.autosuggestion-option:last-of-type { transform: rotate(180deg); } - +.form-group-wrapper--error { + border-left: none !important; + padding-left: 0 !important; +} /* large desktop */ @media (min-width: px2rem(990)) { diff --git a/LearningHub.Nhs.WebUI/Views/LoginWizard/SecurityQuestionsDetails.cshtml b/LearningHub.Nhs.WebUI/Views/LoginWizard/SecurityQuestionsDetails.cshtml new file mode 100644 index 000000000..2602c68f6 --- /dev/null +++ b/LearningHub.Nhs.WebUI/Views/LoginWizard/SecurityQuestionsDetails.cshtml @@ -0,0 +1,25 @@ +@model LearningHub.Nhs.WebUI.Models.UserProfile.MyAcountSecurityQuestionsViewModel + +@{ + ViewData["DisableValidation"] = true; + ViewData["Title"] = "Login Wizard"; + var errorHasOccurred = !ViewData.ModelState.IsValid; +} + +
+
+@* *@ + +
+ + + +
+ + +
+ +
+
\ No newline at end of file diff --git a/LearningHub.Nhs.WebUI/Views/MyAccount/Index.cshtml b/LearningHub.Nhs.WebUI/Views/MyAccount/Index.cshtml index 4dda70a4d..48418e361 100644 --- a/LearningHub.Nhs.WebUI/Views/MyAccount/Index.cshtml +++ b/LearningHub.Nhs.WebUI/Views/MyAccount/Index.cshtml @@ -132,7 +132,7 @@ {
-
+
diff --git a/LearningHub.Nhs.WebUI/Views/MyAccount/MyAccountSecurityQuestionsDetails.cshtml b/LearningHub.Nhs.WebUI/Views/MyAccount/MyAccountSecurityQuestionsDetails.cshtml index a47354bc5..e470e3c20 100644 --- a/LearningHub.Nhs.WebUI/Views/MyAccount/MyAccountSecurityQuestionsDetails.cshtml +++ b/LearningHub.Nhs.WebUI/Views/MyAccount/MyAccountSecurityQuestionsDetails.cshtml @@ -11,86 +11,14 @@ -
-
- @if (errorHasOccurred) - { - - } -
-
-
- Set your security question -
-

Choose your security question

-
-
-
- These security questions can be used to help you to sign in if you forget your password. Please be assured that anything you enter on this page is encrypted and will not be visible to anyone, including system administrators. -
-
-

Your two security questions must be different from each other.

-
-
-

We will only use this information to help you sign in to the Learning Hub.

-
-
- -
+ -
- - -
-
- -
- -
- - -
- -
- - -
-
+
+ +
diff --git a/LearningHub.Nhs.WebUI/Views/MyAccount/MyEmployment.cshtml b/LearningHub.Nhs.WebUI/Views/MyAccount/MyEmployment.cshtml index a7bc5e1b0..2f9fa11e9 100644 --- a/LearningHub.Nhs.WebUI/Views/MyAccount/MyEmployment.cshtml +++ b/LearningHub.Nhs.WebUI/Views/MyAccount/MyEmployment.cshtml @@ -30,6 +30,14 @@ @await Component.InvokeAsync("SideNav", new { groupTitle = "Account" })
+ @if (this.ViewBag.CheckDetails == true) + { +
+ Information: +

Please check that your details are up-to-date.

+
+ } + @if (!ViewData.ModelState.IsValid) { @@ -115,7 +123,7 @@ {
-
+
diff --git a/LearningHub.Nhs.WebUI/Views/MyAccount/_MyAccountSecurityQuestions.cshtml b/LearningHub.Nhs.WebUI/Views/MyAccount/_MyAccountSecurityQuestions.cshtml new file mode 100644 index 000000000..097575505 --- /dev/null +++ b/LearningHub.Nhs.WebUI/Views/MyAccount/_MyAccountSecurityQuestions.cshtml @@ -0,0 +1,81 @@ +@model LearningHub.Nhs.WebUI.Models.UserProfile.MyAcountSecurityQuestionsViewModel + +@{ + ViewData["DisableValidation"] = true; + var errorHasOccurred = !ViewData.ModelState.IsValid; +} + +
+
+ @if (errorHasOccurred) + { + + } +
+
+
+ Set your security questions +
+

Choose your security questions

+
+
+
+ These security questions can be used to help you to sign in if you forget your password. Please be assured that anything you enter on this page is encrypted and will not be visible to anyone, including system administrators. +
+
+

Your two security questions must be different from each other.

+
+
+

We will only use this information to help you sign in to the Learning Hub.

+
+ +
+ +
+ +
+ + +
+
+ +
+ +
+ + +
+
+
\ No newline at end of file