Exemple #1
0
        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));
            }
        }
Exemple #2
0
        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));
        }
Exemple #3
0
        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));
        }
Exemple #4
0
        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));
        }
Exemple #5
0
        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"));
                }
            }
        }
Exemple #6
0
        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);
            }
        }