Merge branch 'v8/dev' into v8/feature/nucache-perf
# Conflicts: # src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs
This commit is contained in:
@@ -14,9 +14,13 @@ namespace Umbraco.Core.Persistence.Dtos
|
||||
[PrimaryKeyColumn(Name = "PK_umbracoExternalLogin")]
|
||||
public int Id { get; set; }
|
||||
|
||||
// TODO: This is completely missing a FK!!?
|
||||
|
||||
[Column("userId")]
|
||||
public int UserId { get; set; }
|
||||
|
||||
// TODO: There should be an index on both LoginProvider and ProviderKey
|
||||
|
||||
[Column("loginProvider")]
|
||||
[Length(4000)]
|
||||
[NullSetting(NullSetting = NullSettings.NotNull)]
|
||||
@@ -30,5 +34,13 @@ namespace Umbraco.Core.Persistence.Dtos
|
||||
[Column("createDate")]
|
||||
[Constraint(Default = SystemMethods.CurrentDateTime)]
|
||||
public DateTime CreateDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used to store any arbitrary data for the user and external provider - like user tokens returned from the provider
|
||||
/// </summary>
|
||||
[Column("userData")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
[SpecialDbType(SpecialDbTypes.NTEXT)]
|
||||
public string UserData { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
using Umbraco.Core.Models.Identity;
|
||||
using System;
|
||||
using Umbraco.Core.Models.Identity;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
internal static class ExternalLoginFactory
|
||||
{
|
||||
public static IIdentityUserLogin BuildEntity(ExternalLoginDto dto)
|
||||
public static IIdentityUserLoginExtended BuildEntity(ExternalLoginDto dto)
|
||||
{
|
||||
var entity = new IdentityUserLogin(dto.Id, dto.LoginProvider, dto.ProviderKey, dto.UserId, dto.CreateDate);
|
||||
var entity = new IdentityUserLogin(dto.Id, dto.LoginProvider, dto.ProviderKey, dto.UserId, dto.CreateDate)
|
||||
{
|
||||
UserData = dto.UserData
|
||||
};
|
||||
|
||||
// reset dirty initial properties (U4-1946)
|
||||
entity.ResetDirtyProperties(false);
|
||||
@@ -16,13 +20,30 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
|
||||
public static ExternalLoginDto BuildDto(IIdentityUserLogin entity)
|
||||
{
|
||||
var asExtended = entity as IIdentityUserLoginExtended;
|
||||
var dto = new ExternalLoginDto
|
||||
{
|
||||
Id = entity.Id,
|
||||
CreateDate = entity.CreateDate,
|
||||
LoginProvider = entity.LoginProvider,
|
||||
ProviderKey = entity.ProviderKey,
|
||||
UserId = entity.UserId
|
||||
UserId = entity.UserId,
|
||||
UserData = asExtended?.UserData
|
||||
};
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
public static ExternalLoginDto BuildDto(int userId, IExternalLogin entity, int? id = null)
|
||||
{
|
||||
var dto = new ExternalLoginDto
|
||||
{
|
||||
Id = id ?? default,
|
||||
UserId = userId,
|
||||
LoginProvider = entity.LoginProvider,
|
||||
ProviderKey = entity.ProviderKey,
|
||||
UserData = entity.UserData,
|
||||
CreateDate = DateTime.Now
|
||||
};
|
||||
|
||||
return dto;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Identity;
|
||||
using Umbraco.Core.Models.Identity;
|
||||
|
||||
@@ -6,7 +7,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public interface IExternalLoginRepository : IReadWriteQueryRepository<int, IIdentityUserLogin>
|
||||
{
|
||||
[Obsolete("Use the overload specifying IIdentityUserLoginExtended instead")]
|
||||
void SaveUserLogins(int memberId, IEnumerable<UserLoginInfo> logins);
|
||||
void Save(int userId, IEnumerable<IExternalLogin> logins);
|
||||
void DeleteUserLogins(int memberId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,25 +22,65 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
public void DeleteUserLogins(int memberId)
|
||||
{
|
||||
Database.Execute("DELETE FROM ExternalLogins WHERE UserId=@userId", new { userId = memberId });
|
||||
Database.Delete<ExternalLoginDto>("WHERE userId=@userId", new { userId = memberId });
|
||||
}
|
||||
|
||||
public void Save(int userId, IEnumerable<IExternalLogin> logins)
|
||||
{
|
||||
var sql = Sql()
|
||||
.Select<ExternalLoginDto>()
|
||||
.From<ExternalLoginDto>()
|
||||
.Where<ExternalLoginDto>(x => x.UserId == userId)
|
||||
.ForUpdate();
|
||||
|
||||
// deduplicate the logins
|
||||
logins = logins.DistinctBy(x => x.ProviderKey + x.LoginProvider).ToList();
|
||||
|
||||
var toUpdate = new Dictionary<int, IExternalLogin>();
|
||||
var toDelete = new List<int>();
|
||||
var toInsert = new List<IExternalLogin>(logins);
|
||||
|
||||
var existingLogins = Database.Query<ExternalLoginDto>(sql).OrderByDescending(x => x.CreateDate).ToList();
|
||||
// used to track duplicates so they can be removed
|
||||
var keys = new HashSet<(string, string)>();
|
||||
|
||||
foreach (var existing in existingLogins)
|
||||
{
|
||||
if (!keys.Add((existing.ProviderKey, existing.LoginProvider)))
|
||||
{
|
||||
// if it already exists we need to remove this one
|
||||
toDelete.Add(existing.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
var found = logins.FirstOrDefault(x =>
|
||||
x.LoginProvider.Equals(existing.LoginProvider, StringComparison.InvariantCultureIgnoreCase)
|
||||
&& x.ProviderKey.Equals(existing.ProviderKey, StringComparison.InvariantCultureIgnoreCase));
|
||||
|
||||
if (found != null)
|
||||
{
|
||||
toUpdate.Add(existing.Id, found);
|
||||
// if it's an update then it's not an insert
|
||||
toInsert.RemoveAll(x => x.ProviderKey == found.ProviderKey && x.LoginProvider == found.LoginProvider);
|
||||
}
|
||||
else
|
||||
{
|
||||
toDelete.Add(existing.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// do the deletes, updates and inserts
|
||||
if (toDelete.Count > 0)
|
||||
Database.DeleteMany<ExternalLoginDto>().Where(x => toDelete.Contains(x.Id)).Execute();
|
||||
foreach (var u in toUpdate)
|
||||
Database.Update(ExternalLoginFactory.BuildDto(userId, u.Value, u.Key));
|
||||
Database.InsertBulk(toInsert.Select(i => ExternalLoginFactory.BuildDto(userId, i)));
|
||||
}
|
||||
|
||||
public void SaveUserLogins(int memberId, IEnumerable<UserLoginInfo> logins)
|
||||
{
|
||||
//clear out logins for member
|
||||
Database.Execute("DELETE FROM umbracoExternalLogin WHERE userId=@userId", new { userId = memberId });
|
||||
|
||||
//add them all
|
||||
foreach (var l in logins)
|
||||
{
|
||||
Database.Insert(new ExternalLoginDto
|
||||
{
|
||||
LoginProvider = l.LoginProvider,
|
||||
ProviderKey = l.ProviderKey,
|
||||
UserId = memberId,
|
||||
CreateDate = DateTime.Now
|
||||
});
|
||||
}
|
||||
Save(memberId, logins.Select(x => new ExternalLogin(x.LoginProvider, x.ProviderKey)));
|
||||
}
|
||||
|
||||
protected override IIdentityUserLogin PerformGet(int id)
|
||||
@@ -67,7 +107,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
return PerformGetAllOnIds(ids);
|
||||
}
|
||||
|
||||
var sql = GetBaseQuery(false);
|
||||
var sql = GetBaseQuery(false).OrderByDescending<ExternalLoginDto>(x => x.CreateDate);
|
||||
|
||||
return ConvertFromDtos(Database.Fetch<ExternalLoginDto>(sql))
|
||||
.ToArray();// we don't want to re-iterate again!
|
||||
@@ -103,7 +143,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
foreach (var dto in dtos)
|
||||
{
|
||||
yield return Get(dto.Id);
|
||||
yield return ExternalLoginFactory.BuildEntity(dto);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user