/// <inheritdoc /> public override Task <IdentityResult> UpdateAsync(MemberIdentityUser user, CancellationToken cancellationToken = default) { try { cancellationToken.ThrowIfCancellationRequested(); ThrowIfDisposed(); if (user == null) { throw new ArgumentNullException(nameof(user)); } if (!int.TryParse(user.Id, NumberStyles.Integer, CultureInfo.InvariantCulture, out var asInt)) { //TODO: should this be thrown, or an identity result? throw new InvalidOperationException("The user id must be an integer to work with the Umbraco"); } using IScope scope = _scopeProvider.CreateScope(autoComplete: true); IMember found = _memberService.GetById(asInt); if (found != null) { // we have to remember whether Logins property is dirty, since the UpdateMemberProperties will reset it. var isLoginsPropertyDirty = user.IsPropertyDirty(nameof(MemberIdentityUser.Logins)); MemberDataChangeType memberChangeType = UpdateMemberProperties(found, user); if (memberChangeType == MemberDataChangeType.FullSave) { _memberService.Save(found); } else if (memberChangeType == MemberDataChangeType.LoginOnly) { // If the member is only logging in, just issue that command without // any write locks so we are creating a bottleneck. _memberService.SetLastLogin(found.Username, DateTime.Now); } // TODO: when to implement external login service? //if (isLoginsPropertyDirty) //{ // _externalLoginService.Save( // found.Id, // user.Logins.Select(x => new ExternalLogin( // x.LoginProvider, // x.ProviderKey, // x.UserData))); //} } return(Task.FromResult(IdentityResult.Success)); } catch (Exception ex) { return(Task.FromResult(IdentityResult.Failed(new IdentityError { Code = GenericIdentityErrorCode, Description = ex.Message }))); } }
private MemberDataChangeType UpdateMemberProperties(IMember member, MemberIdentityUser identityUser) { MemberDataChangeType changeType = MemberDataChangeType.None; // don't assign anything if nothing has changed as this will trigger the track changes of the model if (identityUser.IsPropertyDirty(nameof(MemberIdentityUser.LastLoginDateUtc)) || (member.LastLoginDate != default && identityUser.LastLoginDateUtc.HasValue == false) || (identityUser.LastLoginDateUtc.HasValue && member.LastLoginDate.ToUniversalTime() != identityUser.LastLoginDateUtc.Value)) { changeType = MemberDataChangeType.LoginOnly; // if the LastLoginDate is being set to MinValue, don't convert it ToLocalTime DateTime dt = identityUser.LastLoginDateUtc == DateTime.MinValue ? DateTime.MinValue : identityUser.LastLoginDateUtc.Value.ToLocalTime(); member.LastLoginDate = dt; } if (identityUser.IsPropertyDirty(nameof(MemberIdentityUser.LastPasswordChangeDateUtc)) || (member.LastPasswordChangeDate != default && identityUser.LastPasswordChangeDateUtc.HasValue == false) || (identityUser.LastPasswordChangeDateUtc.HasValue && member.LastPasswordChangeDate.ToUniversalTime() != identityUser.LastPasswordChangeDateUtc.Value)) { changeType = MemberDataChangeType.FullSave; member.LastPasswordChangeDate = identityUser.LastPasswordChangeDateUtc.Value.ToLocalTime(); } if (identityUser.IsPropertyDirty(nameof(MemberIdentityUser.Comments)) && member.Comments != identityUser.Comments && identityUser.Comments.IsNullOrWhiteSpace() == false) { changeType = MemberDataChangeType.FullSave; member.Comments = identityUser.Comments; } if (identityUser.IsPropertyDirty(nameof(MemberIdentityUser.EmailConfirmed)) || (member.EmailConfirmedDate.HasValue && member.EmailConfirmedDate.Value != default && identityUser.EmailConfirmed == false) || ((member.EmailConfirmedDate.HasValue == false || member.EmailConfirmedDate.Value == default) && identityUser.EmailConfirmed)) { changeType = MemberDataChangeType.FullSave; member.EmailConfirmedDate = identityUser.EmailConfirmed ? (DateTime?)DateTime.Now : null; } if (identityUser.IsPropertyDirty(nameof(MemberIdentityUser.Name)) && member.Name != identityUser.Name && identityUser.Name.IsNullOrWhiteSpace() == false) { changeType = MemberDataChangeType.FullSave; member.Name = identityUser.Name; } if (identityUser.IsPropertyDirty(nameof(MemberIdentityUser.Email)) && member.Email != identityUser.Email && identityUser.Email.IsNullOrWhiteSpace() == false) { changeType = MemberDataChangeType.FullSave; member.Email = identityUser.Email; } if (identityUser.IsPropertyDirty(nameof(MemberIdentityUser.AccessFailedCount)) && member.FailedPasswordAttempts != identityUser.AccessFailedCount) { changeType = MemberDataChangeType.FullSave; member.FailedPasswordAttempts = identityUser.AccessFailedCount; } if (member.IsLockedOut != identityUser.IsLockedOut) { changeType = MemberDataChangeType.FullSave; member.IsLockedOut = identityUser.IsLockedOut; if (member.IsLockedOut) { // need to set the last lockout date member.LastLockoutDate = DateTime.Now; } } if (member.IsApproved != identityUser.IsApproved) { changeType = MemberDataChangeType.FullSave; member.IsApproved = identityUser.IsApproved; } if (identityUser.IsPropertyDirty(nameof(MemberIdentityUser.UserName)) && member.Username != identityUser.UserName && identityUser.UserName.IsNullOrWhiteSpace() == false) { changeType = MemberDataChangeType.FullSave; member.Username = identityUser.UserName; } if (identityUser.IsPropertyDirty(nameof(MemberIdentityUser.PasswordHash)) && member.RawPasswordValue != identityUser.PasswordHash && identityUser.PasswordHash.IsNullOrWhiteSpace() == false) { changeType = MemberDataChangeType.FullSave; member.RawPasswordValue = identityUser.PasswordHash; member.PasswordConfiguration = identityUser.PasswordConfig; } if (member.PasswordConfiguration != identityUser.PasswordConfig) { changeType = MemberDataChangeType.FullSave; member.PasswordConfiguration = identityUser.PasswordConfig; } if (member.SecurityStamp != identityUser.SecurityStamp) { changeType = MemberDataChangeType.FullSave; member.SecurityStamp = identityUser.SecurityStamp; } if (identityUser.IsPropertyDirty(nameof(MemberIdentityUser.Roles))) { changeType = MemberDataChangeType.FullSave; var identityUserRoles = identityUser.Roles.Select(x => x.RoleId).ToArray(); _memberService.ReplaceRoles(new[] { member.Id }, identityUserRoles); } // reset all changes identityUser.ResetDirtyProperties(false); return(changeType); }