public async Task <ActionResult> RecoveryCodes() { using (UserDefinitionDataProvider userDP = new UserDefinitionDataProvider()) { UserDefinition user = await userDP.GetItemByUserIdAsync(Manager.UserId); if (user == null) { throw new InternalError("User with id {0} not found", Manager.UserId); } // Make sure this user is not using an external account using (UserLoginInfoDataProvider logInfoDP = new UserLoginInfoDataProvider()) { if (await logInfoDP.IsExternalUserAsync(Manager.UserId)) { return(new EmptyResult()); } } // Make sure there are any 2fa processors TwoStepAuth twoStep = new TwoStepAuth(); List <ITwoStepAuth> list = await twoStep.GetTwoStepAuthProcessorsAsync(); if (list.Count == 0) { return(new EmptyResult()); } // If there is no recovery code, generate one (upgraded system) if (user.RecoveryCode == null) { await GenerateRecoveryCodeAsync(userDP, user); } EditModel.ModelProgressEnum progress = (EditModel.ModelProgressEnum)Manager.SessionSettings.SiteSettings.GetValue <int>(IDENTITY_RECOVERY_PROGRESS, (int)EditModel.ModelProgressEnum.New); EditModel model = new EditModel() { ModelProgress = progress, }; model.UpdateData(user); await Manager.AddOnManager.AddAddOnNamedAsync("YetaWF_ComponentsHTML", "clipboardjs.com.clipboard");// add clipboard support which is needed later (after partial form update) return(View(model)); } }
public async Task <ActionResult> NeedNewPasswordDisplay() { if (!Manager.NeedNewPassword) { return(new EmptyResult()); } if (Manager.EditMode) { return(new EmptyResult()); } if (Manager.IsInPopup) { return(new EmptyResult()); } using (UserLoginInfoDataProvider logInfoDP = new UserLoginInfoDataProvider()) { if (await logInfoDP.IsExternalUserAsync(Manager.UserId)) { return(new EmptyResult()); } } UserPasswordModule modNewPassword = (UserPasswordModule)await ModuleDefinition.LoadAsync(ModuleDefinition.GetPermanentGuid(typeof(UserPasswordModule))); if (modNewPassword == null) { throw new InternalError($"nameof(UserPasswordModule) module not found"); } LoginConfigData config = await LoginConfigDataProvider.GetConfigAsync(); ModuleAction actionNewPassword = modNewPassword.GetAction_UserPassword(config.ChangePasswordUrl); if (actionNewPassword == null) { throw new InternalError("Change password action not found"); } DisplayModel model = new DisplayModel { ChangePasswordAction = actionNewPassword, }; return(View(model)); }
public async Task <ActionResult> ResetPassword(int userId = 0, string resetKey = null) { UserManager <UserDefinition> userManager = Managers.GetUserManager(); UserDefinition user; #if MVC6 user = await userManager.FindByIdAsync(userId.ToString()); #else user = userManager.FindById(userId.ToString()); #endif if (user == null) { throw new Error(this.__ResStr("notFound", "User account not found.")); } using (UserLoginInfoDataProvider logInfoDP = new UserLoginInfoDataProvider()) { if (await logInfoDP.IsExternalUserAsync(user.UserId)) { return(View("ShowMessage", this.__ResStr("extUser", "Your account uses an external login provider - The password (if available) must be set up using the external login provider."), UseAreaViewName: false)); } } EditModel model = new EditModel { ResetKey = resetKey, UserId = userId, }; if (resetKey != null) { if (user.ResetKey == null || user.ResetValidUntil == null || user.ResetValidUntil < DateTime.UtcNow) { ModelState.AddModelError(nameof(model.ResetKey), this.__ResStr("expired", "The reset key has expired and is no longer valid")); } } using (LoginConfigDataProvider logConfigDP = new LoginConfigDataProvider()) { model.PasswordRules = Module.ShowPasswordRules ? logConfigDP.PasswordRules : null; } return(View(model)); }
public async Task <ActionResult> ResetPassword_Partial(EditModel model) { UserManager <UserDefinition> userManager = Managers.GetUserManager(); UserDefinition user; #if MVC6 user = await userManager.FindByIdAsync(model.UserId.ToString()); #else user = userManager.FindById(model.UserId.ToString()); #endif if (user == null) { throw new Error(this.__ResStr("notFound", "User account not found.")); } using (UserLoginInfoDataProvider logInfoDP = new UserLoginInfoDataProvider()) { if (await logInfoDP.IsExternalUserAsync(model.UserId)) { throw new Error(this.__ResStr("extUser", "Your account uses an external login provider - The password (if available) must be set up using the external login provider.")); } } if (Module.ShowPasswordRules) { using (LoginConfigDataProvider logConfigDP = new LoginConfigDataProvider()) { model.PasswordRules = logConfigDP.PasswordRules; } } if (!ModelState.IsValid) { return(PartialView(model)); } Guid?resetKey = null; if (user.ResetKey == null || user.ResetValidUntil == null || user.ResetValidUntil < DateTime.UtcNow) { ModelState.AddModelError(nameof(model.ResetKey), this.__ResStr("expired", "The reset key has expired and is no longer valid")); } else { try { resetKey = new Guid(model.ResetKey); } catch (Exception) { ModelState.AddModelError(nameof(model.ResetKey), this.__ResStr("invReset", "The reset key is invalid - Please make sure to copy/paste the key in its entirety")); } } if (resetKey != null && user.ResetKey != (Guid)resetKey) { ModelState.AddModelError(nameof(model.ResetKey), this.__ResStr("invReset", "The reset key is invalid - Please make sure to copy/paste the key in its entirety")); } if (!ModelState.IsValid) { return(PartialView(model)); } // change the password IdentityResult result; #if MVC6 IPasswordValidator <UserDefinition> passVal = (IPasswordValidator <UserDefinition>)YetaWFManager.ServiceProvider.GetService(typeof(IPasswordValidator <UserDefinition>)); result = await passVal.ValidateAsync(userManager, user, model.NewPassword); #else result = await userManager.PasswordValidator.ValidateAsync(model.NewPassword); #endif if (!result.Succeeded) { foreach (var err in result.Errors) { ModelState.AddModelError(nameof(model.NewPassword), err.Description); } return(PartialView(model)); } #if MVC6 await userManager.RemovePasswordAsync(user); result = await userManager.AddPasswordAsync(user, model.NewPassword); #else await userManager.RemovePasswordAsync(user.Id); result = await userManager.AddPasswordAsync(user.Id, model.NewPassword); #endif if (!result.Succeeded) { foreach (var err in result.Errors) { #if MVC6 ModelState.AddModelError(nameof(model.NewPassword), err.Description); #else ModelState.AddModelError(nameof(model.NewPassword), err); #endif } return(PartialView(model)); } #if MVC6 IPasswordHasher <UserDefinition> passwordHasher = (IPasswordHasher <UserDefinition>)YetaWFManager.ServiceProvider.GetService(typeof(IPasswordHasher <UserDefinition>)); user.PasswordHash = passwordHasher.HashPassword(user, model.NewPassword); #else user.PasswordHash = userManager.PasswordHasher.HashPassword(model.NewPassword); #endif // update user info LoginConfigData config = await LoginConfigDataProvider.GetConfigAsync(); user.LastPasswordChangedDate = DateTime.UtcNow; user.PasswordChangeIP = Manager.UserHostAddress; bool forceReload = false; if (user.NeedsNewPassword) { forceReload = true; // we need to reload the page to get rid of the warning from NeedPasswordDisplay } user.NeedsNewPassword = false; user.PasswordPlainText = config.SavePlainTextPassword ? model.NewPassword : null; user.ResetKey = null; user.ResetValidUntil = null; // finally update the user definition #if MVC6 result = await userManager.UpdateAsync(user); #else result = userManager.Update(user); #endif if (!result.Succeeded) { foreach (var err in result.Errors) { #if MVC6 ModelState.AddModelError(nameof(model.NewPassword), err.Description); #else ModelState.AddModelError(nameof(model.NewPassword), err); #endif } return(PartialView(model)); } // logoff/logon for any side effects in identity (like SecurityStamp update/cookies) await LoginModuleController.UserLoginAsync(user); return(FormProcessed(model, this.__ResStr("okSaved", "Your new password has been saved"), ForceRedirect: forceReload)); }
public async Task <ActionResult> ForgotPassword_Partial(EditModel model) { LoginConfigData config = await LoginConfigDataProvider.GetConfigAsync(); if (model.ShowCaptcha != config.CaptchaForgotPassword) { throw new InternalError("Hidden field tampering detected"); } await model.UpdateAsync(); model.ShowCaptcha = config.CaptchaForgotPassword; model.RegistrationType = config.RegistrationType; if (!ModelState.IsValid) { return(PartialView(model)); } using (UserDefinitionDataProvider userDP = new UserDefinitionDataProvider()) { UserDefinition userDef; switch (model.RegistrationType) { case RegistrationTypeEnum.NameOnly: userDef = await userDP.GetItemAsync(model.UserName); if (userDef == null) { ModelState.AddModelError(nameof(model.UserName), this.__ResStr("badName", "According to our records there is no account associated with this name")); return(PartialView(model)); } break; case RegistrationTypeEnum.NameAndEmail: userDef = await userDP.GetItemAsync(model.UserNameOrEmail); if (userDef == null) { userDef = await userDP.GetItemByEmailAsync(model.UserNameOrEmail); if (userDef == null) { ModelState.AddModelError(nameof(model.UserNameOrEmail), this.__ResStr("badNameEmail", "According to our records there is no account associated with this name or email address")); return(PartialView(model)); } } break; default: case RegistrationTypeEnum.EmailOnly: userDef = await userDP.GetItemByEmailAsync(model.Email); if (userDef == null) { ModelState.AddModelError(nameof(model.Email), this.__ResStr("badEmail", "According to our records there is no account associated with this email address")); return(PartialView(model)); } break; } using (UserLoginInfoDataProvider logInfoDP = new UserLoginInfoDataProvider()) { if (await logInfoDP.IsExternalUserAsync(userDef.UserId)) { ModelState.AddModelError(nameof(model.Email), this.__ResStr("extEmail", "According to our records there is no account associated with this email address")); ModelState.AddModelError(nameof(model.UserName), this.__ResStr("extName", "According to our records there is no account associated with this name")); ModelState.AddModelError(nameof(model.UserNameOrEmail), this.__ResStr("extUser", "According to our records there is no account associated with this name or email address")); return(PartialView(model)); } } switch (userDef.UserStatus) { case UserStatusEnum.Approved: Emails emails = new Emails(); if (config.SavePlainTextPassword) { await emails.SendForgottenEmailAsync(userDef, config.BccForgottenPassword?Manager.CurrentSite.AdminEmail : null); string from = emails.GetSendingEmailAddress(); return(FormProcessed(model, this.__ResStr("okForgot", "We just sent an email to your email address with your password information - Please allow a few minutes for delivery and make sure your spam filters allow emails from {0}", from), OnClose: OnCloseEnum.Nothing, OnPopupClose: OnPopupCloseEnum.ReloadModule)); } else { if (userDef.ResetKey != null && userDef.ResetValidUntil != null && userDef.ResetValidUntil > DateTime.UtcNow) { // preserve existing key in case user resends } else { userDef.ResetKey = Guid.NewGuid(); userDef.ResetValidUntil = DateTime.UtcNow.Add(config.ResetTimeSpan); if (await userDP.UpdateItemAsync(userDef) != Core.DataProvider.UpdateStatusEnum.OK) // update reset key info { throw new Error(this.__ResStr("resetUpdate", "User information could not be updated")); } } await emails.SendPasswordResetEmailAsync(userDef, config.BccForgottenPassword?Manager.CurrentSite.AdminEmail : null); string from = emails.GetSendingEmailAddress(); return(FormProcessed(model, this.__ResStr("okReset", "We just sent an email to your email address to reset your password - Please allow a few minutes for delivery and make sure your spam filters allow emails from {0}", from), OnClose: OnCloseEnum.Nothing, OnPopupClose: OnPopupCloseEnum.ReloadModule)); } case UserStatusEnum.NeedApproval: throw new Error(this.__ResStr("needApproval", "This account has not yet been approved and is awaiting approval by the site administrator")); case UserStatusEnum.NeedValidation: throw new Error(this.__ResStr("needValidation", "This account has not yet been verified - Please check your emails for our verification email")); case UserStatusEnum.Rejected: throw new Error(this.__ResStr("rejected", "This account has been rejected and is not accessible")); case UserStatusEnum.Suspended: throw new Error(this.__ResStr("suspended", "This account has been suspended and is not accessible")); default: throw new Error(this.__ResStr("unknownState", "This account is in an undefined state and is not accessible")); } } }
private async Task <ActionResult> CompleteLoginAsync(LoginModel model, LoginConfigData config, bool useTwoStep) { Manager.SessionSettings.SiteSettings.ClearValue(LoginTwoStepController.IDENTITY_TWOSTEP_USERID); Manager.SessionSettings.SiteSettings.ClearValue(LoginTwoStepController.IDENTITY_TWOSTEP_NEXTURL); Manager.SessionSettings.SiteSettings.ClearValue(LoginTwoStepController.IDENTITY_TWOSTEP_CLOSEONLOGIN); Manager.SessionSettings.SiteSettings.Save(); model.Success = false; // make sure it's a valid user UserDefinition user = await Managers.GetUserManager().FindByNameAsync(model.UserName); if (user == null) { Logging.AddErrorLog("User login failed: {0} - no such user", model.UserName); ModelState.AddModelError(nameof(LoginModel.Email), this.__ResStr("invLogin", "Invalid user name or password")); ModelState.AddModelError(nameof(LoginModel.UserName), this.__ResStr("invLogin", "Invalid user name or password")); ModelState.AddModelError(nameof(LoginModel.Password), this.__ResStr("invLogin", "Invalid user name or password")); return(PartialView(model)); } using (UserLoginInfoDataProvider logInfoDP = new UserLoginInfoDataProvider()) { if (await logInfoDP.IsExternalUserAsync(user.UserId)) { throw new Error(this.__ResStr("extUser", "This account can only be accessed using an external login provider")); } } TwoStepAuth twoStep = new TwoStepAuth();// clear any two-step info we may have await twoStep.ClearTwoStepAutheticationAsync(user.UserId); if (config.MaxLoginFailures != 0 && user.LoginFailures >= config.MaxLoginFailures) { ModelState.AddModelError(nameof(LoginModel.Email), this.__ResStr("maxAttemps", "The maximum number of login attempts has been exceeded - Your account has been suspended")); ModelState.AddModelError(nameof(LoginModel.UserName), this.__ResStr("maxAttemps", "The maximum number of login attempts has been exceeded - Your account has been suspended")); if (user.UserStatus != UserStatusEnum.Suspended) { user.UserStatus = UserStatusEnum.Suspended; await Managers.GetUserManager().UpdateAsync(user); } return(PartialView(model)); } UserDefinition foundUser = user; user = null; // Handle random super user password (only supported on Core) if (foundUser.UserId == SuperuserDefinitionDataProvider.SuperUserId && SuperuserDefinitionDataProvider.SuperuserAvailable && SuperuserDefinitionDataProvider.SuperUserPasswordRandom && model.UserName == SuperuserDefinitionDataProvider.SuperUserName && model.Password == SuperuserDefinitionDataProvider.SuperUserPassword) { user = foundUser; } if (user == null) { user = await Managers.GetUserManager().FindByNameAsync(model.UserName); if (string.IsNullOrWhiteSpace(model.Password) || !await Managers.GetUserManager().CheckPasswordAsync(user, model.Password)) { user = null; } } if (user == null) { foundUser.LoginFailures = foundUser.LoginFailures + 1; await Managers.GetUserManager().UpdateAsync(foundUser); Logging.AddErrorLog("User login failed: {0}, {1}, {2}", model.UserName, model.Password, model.VerificationCode); ModelState.AddModelError(nameof(LoginModel.Email), this.__ResStr("invLogin", "Invalid user name or password")); ModelState.AddModelError(nameof(LoginModel.UserName), this.__ResStr("invLogin", "Invalid user name or password")); ModelState.AddModelError(nameof(LoginModel.Password), this.__ResStr("invLogin", "Invalid user name or password")); return(PartialView(model)); } // if verification code valid, advance user to approved or needs approval if (user.UserStatus == UserStatusEnum.NeedValidation && model.VerificationCode == user.VerificationCode) { Logging.AddLog("User {0} validated ({1})", model.UserName, model.VerificationCode); if (config.ApproveNewUsers) { user.UserStatus = UserStatusEnum.NeedApproval; user.LastActivityDate = DateTime.UtcNow; user.LastActivityIP = Manager.UserHostAddress; await Managers.GetUserManager().UpdateAsync(user); Emails emails = new Emails(); await emails.SendApprovalNeededAsync(user); string nextPage = string.IsNullOrWhiteSpace(config.ApprovalPendingUrl) ? Manager.CurrentSite.HomePageUrl : config.ApprovalPendingUrl; return(FormProcessed(model, this.__ResStr("notApproved", "You just verified your account. Now your account has to be approved by the site administrator. You will receive an email confirmation as soon as your account is active."), NextPage: nextPage)); } user.UserStatus = UserStatusEnum.Approved; // this is saved below, before we're logged in } // check what to do based on account status if (user.UserStatus == UserStatusEnum.NeedValidation) { if (model.ShowVerification) { Logging.AddErrorLog("User {0} - invalid verification code({1})", model.UserName, model.VerificationCode); ModelState.AddModelError(nameof(LoginModel.VerificationCode), this.__ResStr("invVerification", "The verification code is invalid. Please make sure to copy/paste it from the email to avoid any typos.")); user.LoginFailures = user.LoginFailures + 1; await Managers.GetUserManager().UpdateAsync(user); } else { ModelState.AddModelError(nameof(LoginModel.VerificationCode), this.__ResStr("notValidated", "Your account has not yet been verified. You will receive an email with verification information. Please copy and enter the verification code here.")); } model.ShowVerification = true; model.ResendVerificationCode = await Module.GetAction_ResendVerificationEmailAsync(user.UserName); model.ShowCaptcha = false; return(PartialView(model)); } else if (user.UserStatus == UserStatusEnum.NeedApproval) { Logging.AddErrorLog("User {0} - not yet approved", model.UserName); string nextPage = string.IsNullOrWhiteSpace(config.ApprovalPendingUrl) ? Manager.CurrentSite.HomePageUrl : config.ApprovalPendingUrl; return(FormProcessed(model, this.__ResStr("notApproved2", "Your account has not yet been approved by the site administrator. You will receive an email confirmation as soon as your account is active."), NextPage: nextPage)); } else if (user.UserStatus == UserStatusEnum.Rejected) { Logging.AddErrorLog("User {0} - rejected user", model.UserName); string nextPage = string.IsNullOrWhiteSpace(config.RejectedUrl) ? Manager.CurrentSite.HomePageUrl : config.RejectedUrl; return(FormProcessed(model, this.__ResStr("accountRejected", "Your account has been rejected by the site administrator."), NextPage: nextPage)); } else if (user.UserStatus == UserStatusEnum.Suspended) { Logging.AddErrorLog("User {0} - suspended user", model.UserName); string nextPage = string.IsNullOrWhiteSpace(config.SuspendedUrl) ? Manager.CurrentSite.HomePageUrl : config.SuspendedUrl; return(FormProcessed(model, this.__ResStr("accountSuspended", "Your account has been suspended."), NextPage: nextPage)); } else if (user.UserStatus == UserStatusEnum.Approved) { string nextUrl = null; if (Manager.HaveReturnToUrl) { nextUrl = Manager.ReturnToUrl; } if (string.IsNullOrWhiteSpace(nextUrl)) { nextUrl = await Resource.ResourceAccess.GetUserPostLoginUrlAsync((from u in user.RolesList select u.RoleId).ToList()); } if (string.IsNullOrWhiteSpace(nextUrl)) { nextUrl = Manager.CurrentSite.PostLoginUrl; } if (string.IsNullOrWhiteSpace(nextUrl)) { nextUrl = YetaWFManager.Manager.CurrentSite.HomePageUrl; } if (useTwoStep) { ActionResult actionResult = await TwoStepAuthetication(user); if (actionResult != null) { Manager.SessionSettings.SiteSettings.SetValue <int>(LoginTwoStepController.IDENTITY_TWOSTEP_USERID, user.UserId);// marker that user has entered correct name/password Manager.SessionSettings.SiteSettings.SetValue <string>(LoginTwoStepController.IDENTITY_TWOSTEP_NEXTURL, nextUrl); Manager.SessionSettings.SiteSettings.SetValue <bool>(LoginTwoStepController.IDENTITY_TWOSTEP_CLOSEONLOGIN, model.CloseOnLogin); Manager.SessionSettings.SiteSettings.Save(); return(actionResult); } } await LoginModuleController.UserLoginAsync(user, model.RememberMe); model.Success = true; Logging.AddLog("User {0} - logged on", model.UserName); return(FormProcessed(model, OnClose: OnCloseEnum.GotoNewPage, OnPopupClose: OnPopupCloseEnum.GotoNewPage, NextPage: nextUrl, ForceRedirect: true)); } else { throw new InternalError("badUserStatus", "Unexpected account status {0}", user.UserStatus); } }