/// <summary> /// Registers a new member. /// </summary> /// <param name="model">Register member model.</param> /// <param name="logMemberIn">Flag for whether to log the member in upon successful registration.</param> /// <returns>Result of registration operation.</returns> private async Task <IdentityResult> RegisterMemberAsync(RegisterModel model, bool logMemberIn = true) { using ICoreScope scope = _scopeProvider.CreateCoreScope(autoComplete: true); // U4-10762 Server error with "Register Member" snippet (Cannot save member with empty name) // If name field is empty, add the email address instead. if (string.IsNullOrEmpty(model.Name) && string.IsNullOrEmpty(model.Email) == false) { model.Name = model.Email; } model.Username = (model.UsernameIsEmail || model.Username == null) ? model.Email : model.Username; var identityUser = MemberIdentityUser.CreateNew(model.Username, model.Email, model.MemberTypeAlias, true, model.Name); IdentityResult identityResult = await _memberManager.CreateAsync( identityUser, model.Password); if (identityResult.Succeeded) { // Update the custom properties // TODO: See TODO in MembersIdentityUser, Should we support custom member properties for persistence/retrieval? IMember?member = _memberService.GetByKey(identityUser.Key); if (member == null) { // should never happen throw new InvalidOperationException($"Could not find a member with key: {member?.Key}."); } if (model.MemberProperties != null) { foreach (MemberPropertyModel property in model.MemberProperties.Where(p => p.Value != null) .Where(property => member.Properties.Contains(property.Alias))) { member.Properties[property.Alias]?.SetValue(property.Value); } } _memberService.Save(member); if (logMemberIn) { await _memberSignInManager.SignInAsync(identityUser, false); } } return(identityResult); }
/// <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); }