Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 109 additions & 25 deletions LearningHub.Nhs.WebUI/Controllers/LoginWizardController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,31 +144,7 @@ public async Task<IActionResult> 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)
Expand Down Expand Up @@ -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 });
Expand Down Expand Up @@ -585,6 +569,106 @@ public async Task<ActionResult> AccountConfirmationPost()
return this.RedirectToAction("Index", new RouteValueDictionary { { "returnUrl", accountModel.WizardReturnUrl } });
}

/// <summary>
/// Action for starting the security question multiPageForm stage of the wizard.
/// </summary>
/// <param name="returnUrl">The URL to return to after the login wizard has been completed.</param>
/// <returns>The <see cref="Task{IActionResult}"/>.</returns>
[ResponseCache(CacheProfileName = "Never")]
public async Task<IActionResult> 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);
}

/// <summary>
/// Action for choosing security questions.
/// </summary>
/// <param name="model">The MyAcountSecurityQuestionsViewModel.</param>
/// <param name="returnUrl">The URL to return to after the login wizard has been completed.</param>
/// <returns>The <see cref="Task{IActionResult}"/>.</returns>
[HttpPost]
[ResponseCache(CacheProfileName = "Never")]
public async Task<IActionResult> 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<UserSecurityQuestionViewModel>
{
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<LoginWizardViewModel>(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);
}

/// <summary>
/// The complete login wizard stage.
/// </summary>
Expand Down
38 changes: 31 additions & 7 deletions LearningHub.Nhs.WebUI/Controllers/MyAccountController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,35 @@ public async Task<IActionResult> Index(string returnUrl = null, bool? checkDetai
/// <summary>
/// MyEmploymentDetails.
/// </summary>
/// <param name="checkDetails">Whether to check account details.</param>
/// <returns>IActionResult.</returns>
[HttpGet]
[Route("myaccount-employement")]
public async Task<IActionResult> MyEmploymentDetails()
public async Task<IActionResult> MyEmploymentDetails(bool? checkDetails = false)
{
string loginWizardCacheKey = $"{this.CurrentUserId}:LoginWizard";
var (cacheExists, loginWizard) = await this.cacheService.TryGetAsync<Models.Account.LoginWizardViewModel>(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);
}
Expand Down Expand Up @@ -312,13 +336,13 @@ public async Task<IActionResult> 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);
Expand All @@ -331,15 +355,15 @@ public async Task<IActionResult> 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,
},
};
Expand Down
5 changes: 4 additions & 1 deletion LearningHub.Nhs.WebUI/Styles/nhsuk/layout.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
@model LearningHub.Nhs.WebUI.Models.UserProfile.MyAcountSecurityQuestionsViewModel

@{
ViewData["DisableValidation"] = true;
ViewData["Title"] = "Login Wizard";
var errorHasOccurred = !ViewData.ModelState.IsValid;
}

<div class="bg-white">
<div class="nhsuk-width-container app-width-container">
@* <vc:back-link asp-controller="MyAccount" asp-action="Index" link-text="Go back" /> *@

<form asp-controller="LoginWizard" asp-action="UpdateSecurityQuestionPost" asp-route-returnUrl="@ViewData["ReturnUrl"]" method="post">

<partial name="~/Views/MyAccount/_MyAccountSecurityQuestions.cshtml" model="@Model" />

<div class="nhsuk-u-padding-bottom-5">
<input type="hidden" name="formSubmission" value="true">
<button class="nhsuk-button" data-module="nhsuk-button" type="submit">
Save changes
</button>
</div>
</form>
</div>
</div>
2 changes: 1 addition & 1 deletion LearningHub.Nhs.WebUI/Views/MyAccount/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@
{
<form asp-controller="MyAccount" asp-action="CheckDetails" method="post">
<input name="returnUrl" type="hidden" asp-for="@Context.Request.Query["returnUrl"]" />
<div class="nhsuk-u-padding-bottom-5">
<div class="nhsuk-u-padding-bottom-5 nhsuk-u-padding-top-5">
<button class="nhsuk-button" data-module="nhsuk-button" type="submit">
Confirm
</button>
Expand Down
Loading
Loading