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:
Mole
2022-04-21 14:47:27 +02:00
committed by GitHub
parent 6d0a0fff9c
commit 7df4f84247
36 changed files with 744 additions and 489 deletions

View File

@@ -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>