/// <summary> /// Changes the users password /// </summary> /// <param name="data"></param> /// <returns> /// If the password is being reset it will return the newly reset password, otherwise will return an empty value /// </returns> public ModelWithNotifications <string> PostChangePassword(ChangingPasswordModel data) { var userProvider = Core.Security.MembershipProviderExtensions.GetUsersMembershipProvider(); //TODO: WE need to support this! - requires UI updates, etc... if (userProvider.RequiresQuestionAndAnswer) { throw new NotSupportedException("Currently the user editor does not support providers that have RequiresQuestionAndAnswer specified"); } var passwordChangeResult = Members.ChangePassword(Security.CurrentUser.Username, data, userProvider); if (passwordChangeResult.Success) { //even if we weren't resetting this, it is the correct value (null), otherwise if we were resetting then it will contain the new pword var result = new ModelWithNotifications <string>(passwordChangeResult.Result.ResetPassword); result.AddSuccessNotification(ui.Text("user", "password"), ui.Text("user", "passwordChanged")); return(result); } //it wasn't successful, so add the change error to the model state, we've name the property alias _umb_password on the form // so that is why it is being used here. ModelState.AddPropertyError( passwordChangeResult.Result.ChangeError, string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); }
private async Task <bool> ValidateMemberDataAsync(MemberSave contentItem) { if (contentItem.Name.IsNullOrWhiteSpace()) { ModelState.AddPropertyError( new ValidationResult("Invalid user name", new[] { "value" }), $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}login"); return(false); } if (contentItem.Password != null && !contentItem.Password.NewPassword.IsNullOrWhiteSpace()) { IdentityResult validPassword = await _memberManager.ValidatePasswordAsync(contentItem.Password.NewPassword); if (!validPassword.Succeeded) { ModelState.AddPropertyError( new ValidationResult("Invalid password: "******"value" }), $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}password"); return(false); } } IMember?byUsername = _memberService.GetByUsername(contentItem.Username); if (byUsername != null && byUsername.Key != contentItem.Key) { ModelState.AddPropertyError( new ValidationResult("Username is already in use", new[] { "value" }), $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}login"); return(false); } IMember?byEmail = _memberService.GetByEmail(contentItem.Email); if (byEmail != null && byEmail.Key != contentItem.Key) { ModelState.AddPropertyError( new ValidationResult("Email address is already in use", new[] { "value" }), $"{Constants.PropertyEditors.InternalGenericPropertiesPrefix}email"); return(false); } return(true); }
public async Task <ActionResult> EditEmail(EditEmailViewModel model) { if (ModelState.IsValid) { var user = _userService.Get(IdentityHelpers.GetUserId(User.Identity)); if (_userService.IsPasswordMatchForUser(user.UserID, model.Password)) { String originalEmail = user.Email; //Check if the new email address is already assosciated with another account if (_userService.GetByEmail(model.NewEmailAddress) != null) { _userService.SetEmailAddress(user.UserID, model.NewEmailAddress); _uow.Save(); //Send notification email String subject = "Account Email Changed"; String body = "This email is to inform you that the email address associated with your account has been changed."; var message = new MailMessage("*****@*****.**", originalEmail, subject, body); await _emailService.SendMessageAsync(message); ViewBag.SuccessMessage = "Your email address has been changed."; ModelState.Clear(); return(View()); } ModelState.AddPropertyError <EditEmailViewModel>(m => m.NewEmailAddress, "An account with this email already exists."); } else { ModelState.AddPropertyError <EditEmailViewModel>(m => m.Password, "Invalid password."); return(View(model)); } } //We got this far, some.panel .panel-default failed return(View(model)); }
public async Task <ActionResult> ForgotPassword(ForgotPasswordViewModel model) { if (ModelState.IsValid) { var user = _userService.GetByEmailWithMembership(model.Email); if (user != null) { using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, TransactionScopeAsyncFlowOption.Enabled)) { try { var verificationToken = _userService.GeneratePasswordVerificationToken(user.UserID); _uow.Save(); String changePasswordLink = Url.Action("EditPasswordForgot", "Account", new { confirmationToken = verificationToken }, Request.Url.Scheme); String subject = "Forgotten Password Request"; String body = "A forgotten password request has been made, please enter this link in your browser's address bar " + "to recover your account." + changePasswordLink; await _userService.SendEmailAsync(user.UserID, subject, body); scope.Complete(); ViewBag.SuccessMessage = "An email has been sent detailing how to recover your account."; return(View()); } catch (Exception) { throw; } } } ModelState.AddPropertyError <ForgotPasswordViewModel>(m => m.Email, "Invalid Email."); } return(View(model)); }
/// <summary> /// This is going to create the user with the membership provider and check for validation /// </summary> /// <param name="contentItem"></param> /// <param name="status"></param> /// <returns></returns> /// <remarks> /// Depending on if the Umbraco membership provider is active or not, the process differs slightly: /// /// * If the umbraco membership provider is used - we create the membership user first with the membership provider, since /// it's the umbraco membership provider, this writes to the umbraco tables. When that is complete we re-fetch the IMember /// model data from the db. In this case we don't care what the provider user key is. /// * If we're using a non-umbraco membership provider - we check if there is a 'Member' member type - if so /// we create an empty IMember instance first (of type 'Member'), this gives us a unique ID (GUID) /// that we then use to create the member in the custom membership provider. This acts as the link between Umbraco data and /// the custom membership provider data. This gives us the ability to eventually have custom membership properties but still use /// a custom membership provider. If there is no 'Member' member type, then we will simply just create the membership provider member /// with no link to our data. /// /// If this is successful, it will go and re-fetch the IMember from the db because it will now have an ID because the Umbraco provider /// uses the umbraco data store - then of course we need to re-map it to the saved property values. /// </remarks> private MembershipUser CreateWithMembershipProvider(MemberSave contentItem, out MembershipCreateStatus status) { MembershipUser membershipUser; switch (MembershipScenario) { case MembershipScenario.NativeUmbraco: //We are using the umbraco membership provider, create the member using the membership provider first. var umbracoMembershipProvider = (UmbracoMembershipProviderBase)_provider; // TODO: We are not supporting q/a - passing in empty here membershipUser = umbracoMembershipProvider.CreateUser( contentItem.ContentTypeAlias, contentItem.Username, contentItem.Password.NewPassword, contentItem.Email, "", "", contentItem.IsApproved, Guid.NewGuid(), //since it's the umbraco provider, the user key here doesn't make any difference out status); break; case MembershipScenario.CustomProviderWithUmbracoLink: //We are using a custom membership provider, we'll create an empty IMember first to get the unique id to use // as the provider user key. //create it - this persisted item has already been set in the MemberBinder based on the 'Member' member type: Services.MemberService.Save(contentItem.PersistedContent); // TODO: We are not supporting q/a - passing in empty here membershipUser = _provider.CreateUser( contentItem.Username, contentItem.Password.NewPassword, contentItem.Email, "TEMP", //some membership provider's require something here even if q/a is disabled! "TEMP", //some membership provider's require something here even if q/a is disabled! contentItem.IsApproved, contentItem.PersistedContent.Key, //custom membership provider, we'll link that based on the IMember unique id (GUID) out status); break; case MembershipScenario.StandaloneCustomProvider: // we don't have a member type to use so we will just create the basic membership user with the provider with no // link back to the umbraco data var newKey = Guid.NewGuid(); // TODO: We are not supporting q/a - passing in empty here membershipUser = _provider.CreateUser( contentItem.Username, contentItem.Password.NewPassword, contentItem.Email, "TEMP", //some membership provider's require something here even if q/a is disabled! "TEMP", //some membership provider's require something here even if q/a is disabled! contentItem.IsApproved, newKey, out status); break; default: throw new ArgumentOutOfRangeException(); } // TODO: Localize these! switch (status) { case MembershipCreateStatus.Success: //map the key back contentItem.Key = membershipUser.ProviderUserKey.TryConvertTo <Guid>().Result; contentItem.PersistedContent.Key = contentItem.Key; //if the comments are there then we need to save them if (contentItem.Comments.IsNullOrWhiteSpace() == false) { membershipUser.Comment = contentItem.Comments; _provider.UpdateUser(membershipUser); } RefetchMemberData(contentItem, LookupType.ByUserName); break; case MembershipCreateStatus.InvalidUserName: ModelState.AddPropertyError( new ValidationResult("Invalid user name", new[] { "value" }), string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); break; case MembershipCreateStatus.InvalidPassword: ModelState.AddPropertyError( new ValidationResult("Invalid password", new[] { "value" }), string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); break; case MembershipCreateStatus.InvalidQuestion: case MembershipCreateStatus.InvalidAnswer: throw new NotSupportedException("Currently the member editor does not support providers that have RequiresQuestionAndAnswer specified"); case MembershipCreateStatus.InvalidEmail: ModelState.AddPropertyError( new ValidationResult("Invalid email", new[] { "value" }), string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); break; case MembershipCreateStatus.DuplicateUserName: ModelState.AddPropertyError( new ValidationResult("Username is already in use", new[] { "value" }), string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); break; case MembershipCreateStatus.DuplicateEmail: ModelState.AddPropertyError( new ValidationResult("Email address is already in use", new[] { "value" }), string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); break; case MembershipCreateStatus.InvalidProviderUserKey: ModelState.AddPropertyError( //specify 'default' just so that it shows up as a notification - is not assigned to a property new ValidationResult("Invalid provider user key"), "default"); break; case MembershipCreateStatus.DuplicateProviderUserKey: ModelState.AddPropertyError( //specify 'default' just so that it shows up as a notification - is not assigned to a property new ValidationResult("Duplicate provider user key"), "default"); break; case MembershipCreateStatus.ProviderError: case MembershipCreateStatus.UserRejected: ModelState.AddPropertyError( //specify 'default' just so that it shows up as a notification - is not assigned to a property new ValidationResult("User could not be created (rejected by provider)"), "default"); break; default: throw new ArgumentOutOfRangeException(); } return(membershipUser); }
/// <summary> /// Update the membership user using the membership provider (for things like email, etc...) /// If a password change is detected then we'll try that too. /// </summary> /// <param name="contentItem"></param> /// <returns> /// If the password has been reset then this method will return the reset/generated password, otherwise will return null. /// </returns> private string UpdateWithMembershipProvider(MemberSave contentItem) { //Get the member from the provider var membershipUser = _provider.GetUser(contentItem.PersistedContent.Key, false); if (membershipUser == null) { //This should never happen! so we'll let it YSOD if it does. throw new InvalidOperationException("Could not get member from membership provider " + _provider.Name + " with key " + contentItem.PersistedContent.Key); } var shouldReFetchMember = false; var providedUserName = contentItem.PersistedContent.Username; //if the user doesn't have access to sensitive values, then we need to check if any of the built in member property types //have been marked as sensitive. If that is the case we cannot change these persisted values no matter what value has been posted. //There's only 3 special ones we need to deal with that are part of the MemberSave instance if (Security.CurrentUser.HasAccessToSensitiveData() == false) { var memberType = Services.MemberTypeService.Get(contentItem.PersistedContent.ContentTypeId); var sensitiveProperties = memberType .PropertyTypes.Where(x => memberType.IsSensitiveProperty(x.Alias)) .ToList(); foreach (var sensitiveProperty in sensitiveProperties) { //if found, change the value of the contentItem model to the persisted value so it remains unchanged switch (sensitiveProperty.Alias) { case Constants.Conventions.Member.Comments: contentItem.Comments = contentItem.PersistedContent.Comments; break; case Constants.Conventions.Member.IsApproved: contentItem.IsApproved = contentItem.PersistedContent.IsApproved; break; case Constants.Conventions.Member.IsLockedOut: contentItem.IsLockedOut = contentItem.PersistedContent.IsLockedOut; break; } } } //Update the membership user if it has changed try { var requiredUpdating = Members.UpdateMember(membershipUser, _provider, contentItem.Email.Trim(), contentItem.IsApproved, comment: contentItem.Comments); if (requiredUpdating.Success) { //re-map these values shouldReFetchMember = true; } } catch (Exception ex) { Logger.Warn <MemberController>(ex, "Could not update member, the provider returned an error"); ModelState.AddPropertyError( //specify 'default' just so that it shows up as a notification - is not assigned to a property new ValidationResult("Could not update member, the provider returned an error: " + ex.Message + " (see log for full details)"), "default"); } //if they were locked but now they are trying to be unlocked if (membershipUser.IsLockedOut && contentItem.IsLockedOut == false) { try { var result = _provider.UnlockUser(membershipUser.UserName); if (result == false) { //it wasn't successful - but it won't really tell us why. ModelState.AddModelError("custom", "Could not unlock the user"); } else { shouldReFetchMember = true; } } catch (Exception ex) { ModelState.AddModelError("custom", ex); } } else if (membershipUser.IsLockedOut == false && contentItem.IsLockedOut) { //NOTE: This should not ever happen unless someone is mucking around with the request data. //An admin cannot simply lock a user, they get locked out by password attempts, but an admin can un-approve them ModelState.AddModelError("custom", "An admin cannot lock a user"); } //password changes ? if (contentItem.Password == null) { //If the provider has changed some values, these values need to be reflected in the member object //that will get mapped to the display object if (shouldReFetchMember) { RefetchMemberData(contentItem, LookupType.ByKey); RestoreProvidedUserName(contentItem, providedUserName); } return(null); } var passwordChangeResult = Members.ChangePassword(membershipUser.UserName, contentItem.Password, _provider); if (passwordChangeResult.Success) { //If the provider has changed some values, these values need to be reflected in the member object //that will get mapped to the display object if (shouldReFetchMember) { RefetchMemberData(contentItem, LookupType.ByKey); RestoreProvidedUserName(contentItem, providedUserName); } //even if we weren't resetting this, it is the correct value (null), otherwise if we were resetting then it will contain the new pword return(passwordChangeResult.Result.ResetPassword); } //it wasn't successful, so add the change error to the model state ModelState.AddPropertyError( passwordChangeResult.Result.ChangeError, string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); return(null); }
/// <summary> /// Update the membership user using the membership provider (for things like email, etc...) /// If a password change is detected then we'll try that too. /// </summary> /// <param name="contentItem"></param> /// <returns> /// If the password has been reset then this method will return the reset/generated password, otherwise will return null. /// </returns> private string UpdateWithMembershipProvider(MemberSave contentItem) { //Get the member from the provider var membershipUser = _provider.GetUser(contentItem.PersistedContent.Key, false); if (membershipUser == null) { //This should never happen! so we'll let it YSOD if it does. throw new InvalidOperationException("Could not get member from membership provider " + _provider.Name + " with key " + contentItem.PersistedContent.Key); } var shouldReFetchMember = false; var providedUserName = contentItem.PersistedContent.Username; //Update the membership user if it has changed try { var requiredUpdating = Members.UpdateMember(membershipUser, _provider, contentItem.Email.Trim(), contentItem.IsApproved, comment: contentItem.Comments); if (requiredUpdating.Success) { //re-map these values shouldReFetchMember = true; } } catch (Exception ex) { LogHelper.WarnWithException <MemberController>("Could not update member, the provider returned an error", ex); ModelState.AddPropertyError( //specify 'default' just so that it shows up as a notification - is not assigned to a property new ValidationResult("Could not update member, the provider returned an error: " + ex.Message + " (see log for full details)"), "default"); } //if they were locked but now they are trying to be unlocked if (membershipUser.IsLockedOut && contentItem.IsLockedOut == false) { try { var result = _provider.UnlockUser(membershipUser.UserName); if (result == false) { //it wasn't successful - but it won't really tell us why. ModelState.AddModelError("custom", "Could not unlock the user"); } else { shouldReFetchMember = true; } } catch (Exception ex) { ModelState.AddModelError("custom", ex); } } else if (membershipUser.IsLockedOut == false && contentItem.IsLockedOut) { //NOTE: This should not ever happen unless someone is mucking around with the request data. //An admin cannot simply lock a user, they get locked out by password attempts, but an admin can un-approve them ModelState.AddModelError("custom", "An admin cannot lock a user"); } //password changes ? if (contentItem.Password == null) { //If the provider has changed some values, these values need to be reflected in the member object //that will get mapped to the display object if (shouldReFetchMember) { RefetchMemberData(contentItem, LookupType.ByKey); RestoreProvidedUserName(contentItem, providedUserName); } return(null); } var passwordChangeResult = Members.ChangePassword(membershipUser.UserName, contentItem.Password, _provider); if (passwordChangeResult.Success) { //If the provider has changed some values, these values need to be reflected in the member object //that will get mapped to the display object if (shouldReFetchMember) { RefetchMemberData(contentItem, LookupType.ByKey); RestoreProvidedUserName(contentItem, providedUserName); } //even if we weren't resetting this, it is the correct value (null), otherwise if we were resetting then it will contain the new pword return(passwordChangeResult.Result.ResetPassword); } //it wasn't successful, so add the change error to the model state ModelState.AddPropertyError( passwordChangeResult.Result.ChangeError, string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); return(null); }
/// <summary> /// Create a member from the supplied member content data /// /// All member password processing and creation is done via the identity manager /// </summary> /// <param name="contentItem">Member content data</param> /// <returns>The identity result of the created member</returns> private async Task <ActionResult <bool> > CreateMemberAsync(MemberSave contentItem) { IMemberType?memberType = _memberTypeService.Get(contentItem.ContentTypeAlias); if (memberType == null) { throw new InvalidOperationException($"No member type found with alias {contentItem.ContentTypeAlias}"); } var identityMember = MemberIdentityUser.CreateNew( contentItem.Username, contentItem.Email, memberType.Alias, contentItem.IsApproved, contentItem.Name); IdentityResult created = await _memberManager.CreateAsync(identityMember, contentItem.Password?.NewPassword); if (created.Succeeded == false) { MemberDisplay?forDisplay = _umbracoMapper.Map <MemberDisplay>(contentItem.PersistedContent); foreach (IdentityError error in created.Errors) { switch (error.Code) { case nameof(IdentityErrorDescriber.InvalidUserName): ModelState.AddPropertyError( new ValidationResult(error.Description, new[] { "value" }), string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); break; case nameof(IdentityErrorDescriber.PasswordMismatch): case nameof(IdentityErrorDescriber.PasswordRequiresDigit): case nameof(IdentityErrorDescriber.PasswordRequiresLower): case nameof(IdentityErrorDescriber.PasswordRequiresNonAlphanumeric): case nameof(IdentityErrorDescriber.PasswordRequiresUniqueChars): case nameof(IdentityErrorDescriber.PasswordRequiresUpper): case nameof(IdentityErrorDescriber.PasswordTooShort): ModelState.AddPropertyError( new ValidationResult(error.Description, new[] { "value" }), string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); break; case nameof(IdentityErrorDescriber.InvalidEmail): ModelState.AddPropertyError( new ValidationResult(error.Description, new[] { "value" }), string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); break; case nameof(IdentityErrorDescriber.DuplicateUserName): ModelState.AddPropertyError( new ValidationResult(error.Description, new[] { "value" }), string.Format("{0}login", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); break; case nameof(IdentityErrorDescriber.DuplicateEmail): ModelState.AddPropertyError( new ValidationResult(error.Description, new[] { "value" }), string.Format("{0}email", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); break; } } return(ValidationProblem(forDisplay, ModelState)); } // now re-look up the member, which will now exist IMember?member = _memberService.GetByEmail(contentItem.Email); if (member is null) { return(false); } // map the save info over onto the user member = _umbracoMapper.Map <MemberSave, IMember>(contentItem, member); int creatorId = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser?.Id ?? -1; member.CreatorId = creatorId; // assign the mapped property values that are not part of the identity properties string[] builtInAliases = ConventionsHelper.GetStandardPropertyTypeStubs(_shortStringHelper).Select(x => x.Key).ToArray(); foreach (ContentPropertyBasic property in contentItem.Properties) { if (builtInAliases.Contains(property.Alias) == false) { member.Properties[property.Alias]?.SetValue(property.Value); } } // now the member has been saved via identity, resave the member with mapped content properties _memberService.Save(member); contentItem.PersistedContent = member; ActionResult <bool> rolesChanged = await AddOrUpdateRoles(contentItem.Groups, identityMember); if (!rolesChanged.Value && rolesChanged.Result != null) { return(rolesChanged.Result); } return(true); }