V10: Migrate member properties to columns on the member table (#12205)
* Add new columns to the member table * Add missing IsApproved column * Add joins with nested query * Add query for selecting new column values from existing members * Update the member data from the same query * Make escapes work for sqlite * Use GetFieldNameForUpdate instead of GetFieldName * Left join on memberDto * Remove the now unused property types and data * Don't create member columns as properties anymore * Store old properties in fields on member Also switch the dates to nullable since they can be null * Map columns when mapping from DTO to Member object * Display columns in the member content app * Fix null exception when creating new user * Hide value if user doesn't have access to sensitive data * Remove hardcoded member properties * Obsolete old member alias constants * Map and persist member properties * Correctly update LastLogin when member logs in * Map IsApproved and IsLockedOut when saving member in backoffice * Update the query mappers for members * Fix member service tracks dirty changes test * Remove no longer existing property types from member type builder * Fix null error in UpdateMemberProperties * Fix builder tests * Fix SetupMemberTestData in MemberControllerUnitTests * Let LastLoginDate be null and handle check in controller There's no reason to default a perfectly nullable property to default(DateTime) * Add translation key for is approved and use that instead of constant * Obsolete old label constants * Fix whitespace post merge * Fix up test comments * Apply suggestions from code review Co-authored-by: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com> * Fix member properties always being sensitive Co-authored-by: Elitsa Marinovska <elm@umbraco.dk> Co-authored-by: Elitsa Marinovska <21998037+elit0451@users.noreply.github.com>
This commit is contained in:
@@ -174,6 +174,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
//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);
|
||||
@@ -183,17 +184,10 @@ namespace Umbraco.Cms.Core.Security
|
||||
var isLoginsPropertyDirty = user.IsPropertyDirty(nameof(MemberIdentityUser.Logins));
|
||||
var isTokensPropertyDirty = user.IsPropertyDirty(nameof(MemberIdentityUser.LoginTokens));
|
||||
|
||||
MemberDataChangeType memberChangeType = UpdateMemberProperties(found, user);
|
||||
if (memberChangeType == MemberDataChangeType.FullSave)
|
||||
if (UpdateMemberProperties(found, user))
|
||||
{
|
||||
_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);
|
||||
}
|
||||
|
||||
if (isLoginsPropertyDirty)
|
||||
{
|
||||
@@ -588,19 +582,16 @@ namespace Umbraco.Cms.Core.Security
|
||||
return user;
|
||||
}
|
||||
|
||||
private MemberDataChangeType UpdateMemberProperties(IMember member, MemberIdentityUser identityUser)
|
||||
private bool UpdateMemberProperties(IMember member, MemberIdentityUser identityUser)
|
||||
{
|
||||
MemberDataChangeType changeType = MemberDataChangeType.None;
|
||||
var anythingChanged = false;
|
||||
|
||||
// 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))
|
||||
|| (identityUser.LastLoginDateUtc.HasValue && member.LastLoginDate?.ToUniversalTime() != identityUser.LastLoginDateUtc.Value))
|
||||
{
|
||||
// If the LastLoginDate is default on the member we have to do a full save.
|
||||
// This is because the umbraco property data for the member doesn't exist yet in this case
|
||||
// meaning we can't just update that property data, but have to do a full save to create it
|
||||
changeType = member.LastLoginDate == default ? MemberDataChangeType.FullSave : MemberDataChangeType.LoginOnly;
|
||||
anythingChanged = true;
|
||||
|
||||
// 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();
|
||||
@@ -609,16 +600,16 @@ namespace Umbraco.Cms.Core.Security
|
||||
|
||||
if (identityUser.IsPropertyDirty(nameof(MemberIdentityUser.LastPasswordChangeDateUtc))
|
||||
|| (member.LastPasswordChangeDate != default && identityUser.LastPasswordChangeDateUtc.HasValue == false)
|
||||
|| (identityUser.LastPasswordChangeDateUtc.HasValue && member.LastPasswordChangeDate.ToUniversalTime() != identityUser.LastPasswordChangeDateUtc.Value))
|
||||
|| (identityUser.LastPasswordChangeDateUtc.HasValue && member.LastPasswordChangeDate?.ToUniversalTime() != identityUser.LastPasswordChangeDateUtc.Value))
|
||||
{
|
||||
changeType = MemberDataChangeType.FullSave;
|
||||
anythingChanged = true;
|
||||
member.LastPasswordChangeDate = identityUser.LastPasswordChangeDateUtc.Value.ToLocalTime();
|
||||
}
|
||||
|
||||
if (identityUser.IsPropertyDirty(nameof(MemberIdentityUser.Comments))
|
||||
&& member.Comments != identityUser.Comments && identityUser.Comments.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
changeType = MemberDataChangeType.FullSave;
|
||||
anythingChanged = true;
|
||||
member.Comments = identityUser.Comments;
|
||||
}
|
||||
|
||||
@@ -626,34 +617,34 @@ namespace Umbraco.Cms.Core.Security
|
||||
|| (member.EmailConfirmedDate.HasValue && member.EmailConfirmedDate.Value != default && identityUser.EmailConfirmed == false)
|
||||
|| ((member.EmailConfirmedDate.HasValue == false || member.EmailConfirmedDate.Value == default) && identityUser.EmailConfirmed))
|
||||
{
|
||||
changeType = MemberDataChangeType.FullSave;
|
||||
anythingChanged = true;
|
||||
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;
|
||||
anythingChanged = true;
|
||||
member.Name = identityUser.Name;
|
||||
}
|
||||
|
||||
if (identityUser.IsPropertyDirty(nameof(MemberIdentityUser.Email))
|
||||
&& member.Email != identityUser.Email && identityUser.Email.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
changeType = MemberDataChangeType.FullSave;
|
||||
anythingChanged = true;
|
||||
member.Email = identityUser.Email;
|
||||
}
|
||||
|
||||
if (identityUser.IsPropertyDirty(nameof(MemberIdentityUser.AccessFailedCount))
|
||||
&& member.FailedPasswordAttempts != identityUser.AccessFailedCount)
|
||||
{
|
||||
changeType = MemberDataChangeType.FullSave;
|
||||
anythingChanged = true;
|
||||
member.FailedPasswordAttempts = identityUser.AccessFailedCount;
|
||||
}
|
||||
|
||||
if (member.IsLockedOut != identityUser.IsLockedOut)
|
||||
{
|
||||
changeType = MemberDataChangeType.FullSave;
|
||||
anythingChanged = true;
|
||||
member.IsLockedOut = identityUser.IsLockedOut;
|
||||
|
||||
if (member.IsLockedOut)
|
||||
@@ -665,40 +656,40 @@ namespace Umbraco.Cms.Core.Security
|
||||
|
||||
if (member.IsApproved != identityUser.IsApproved)
|
||||
{
|
||||
changeType = MemberDataChangeType.FullSave;
|
||||
anythingChanged = true;
|
||||
member.IsApproved = identityUser.IsApproved;
|
||||
}
|
||||
|
||||
if (identityUser.IsPropertyDirty(nameof(MemberIdentityUser.UserName))
|
||||
&& member.Username != identityUser.UserName && identityUser.UserName.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
changeType = MemberDataChangeType.FullSave;
|
||||
anythingChanged = true;
|
||||
member.Username = identityUser.UserName;
|
||||
}
|
||||
|
||||
if (identityUser.IsPropertyDirty(nameof(MemberIdentityUser.PasswordHash))
|
||||
&& member.RawPasswordValue != identityUser.PasswordHash && identityUser.PasswordHash.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
changeType = MemberDataChangeType.FullSave;
|
||||
anythingChanged = true;
|
||||
member.RawPasswordValue = identityUser.PasswordHash;
|
||||
member.PasswordConfiguration = identityUser.PasswordConfig;
|
||||
}
|
||||
|
||||
if (member.PasswordConfiguration != identityUser.PasswordConfig)
|
||||
{
|
||||
changeType = MemberDataChangeType.FullSave;
|
||||
anythingChanged = true;
|
||||
member.PasswordConfiguration = identityUser.PasswordConfig;
|
||||
}
|
||||
|
||||
if (member.SecurityStamp != identityUser.SecurityStamp)
|
||||
{
|
||||
changeType = MemberDataChangeType.FullSave;
|
||||
anythingChanged = true;
|
||||
member.SecurityStamp = identityUser.SecurityStamp;
|
||||
}
|
||||
|
||||
if (identityUser.IsPropertyDirty(nameof(MemberIdentityUser.Roles)))
|
||||
{
|
||||
changeType = MemberDataChangeType.FullSave;
|
||||
anythingChanged = true;
|
||||
|
||||
var identityUserRoles = identityUser.Roles.Select(x => x.RoleId).ToArray();
|
||||
_memberService.ReplaceRoles(new[] { member.Id }, identityUserRoles);
|
||||
@@ -707,7 +698,7 @@ namespace Umbraco.Cms.Core.Security
|
||||
// reset all changes
|
||||
identityUser.ResetDirtyProperties(false);
|
||||
|
||||
return changeType;
|
||||
return anythingChanged;
|
||||
}
|
||||
|
||||
public IPublishedContent GetPublishedMember(MemberIdentityUser user)
|
||||
@@ -725,13 +716,6 @@ namespace Umbraco.Cms.Core.Security
|
||||
return publishedSnapshot.Members.Get(member);
|
||||
}
|
||||
|
||||
private enum MemberDataChangeType
|
||||
{
|
||||
None,
|
||||
LoginOnly,
|
||||
FullSave
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridden to support Umbraco's own data storage requirements
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user