Gets the user store up to date with the correct queries.
This commit is contained in:
@@ -16,6 +16,8 @@ namespace Umbraco.Core.Models.Identity
|
||||
public string[] AllowedApplications { get; set; }
|
||||
public string Culture { get; set; }
|
||||
|
||||
public string UserTypeAlias { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Overridden to make the retrieval lazy
|
||||
/// </summary>
|
||||
|
||||
34
src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs
Normal file
34
src/Umbraco.Core/Models/Identity/IdentityModelMappings.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using AutoMapper;
|
||||
|
||||
using Umbraco.Core.Models.Mapping;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Core.Models.Identity
|
||||
{
|
||||
public class IdentityModelMappings : MapperConfiguration
|
||||
{
|
||||
public override void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext)
|
||||
{
|
||||
config.CreateMap<IUser, BackOfficeIdentityUser>()
|
||||
.ForMember(user => user.Email, expression => expression.MapFrom(user => user.Email))
|
||||
.ForMember(user => user.Id, expression => expression.MapFrom(user => user.Id))
|
||||
.ForMember(user => user.LockoutEnabled, expression => expression.MapFrom(user => user.IsLockedOut))
|
||||
.ForMember(user => user.LockoutEndDateUtc, expression => expression.UseValue(DateTime.MaxValue.ToUniversalTime()))
|
||||
.ForMember(user => user.UserName, expression => expression.MapFrom(user => user.Username))
|
||||
.ForMember(user => user.PasswordHash, expression => expression.MapFrom(user => GetPasswordHash(user.RawPasswordValue)))
|
||||
.ForMember(user => user.Culture, expression => expression.MapFrom(user => user.Language))
|
||||
.ForMember(user => user.Name, expression => expression.MapFrom(user => user.Name))
|
||||
.ForMember(user => user.StartMediaNode, expression => expression.MapFrom(user => user.StartMediaId))
|
||||
.ForMember(user => user.StartContentNode, expression => expression.MapFrom(user => user.StartContentId))
|
||||
.ForMember(user => user.UserTypeAlias, expression => expression.MapFrom(user => user.UserType.Alias))
|
||||
.ForMember(user => user.AllowedApplications, expression => expression.MapFrom(user => user.AllowedSections.ToArray()));
|
||||
}
|
||||
|
||||
private string GetPasswordHash(string storedPass)
|
||||
{
|
||||
return storedPass.StartsWith("___UIDEMPTYPWORD__") ? null : storedPass;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,13 @@ namespace Umbraco.Core.Models.Identity
|
||||
/// </summary>
|
||||
public class IdentityUserLogin : Entity, IIdentityUserLogin
|
||||
{
|
||||
public IdentityUserLogin(string loginProvider, string providerKey, int userId)
|
||||
{
|
||||
LoginProvider = loginProvider;
|
||||
ProviderKey = providerKey;
|
||||
UserId = userId;
|
||||
}
|
||||
|
||||
public IdentityUserLogin(int id, string loginProvider, string providerKey, int userId, DateTime createDate)
|
||||
{
|
||||
Id = id;
|
||||
|
||||
@@ -12,4 +12,6 @@ namespace Umbraco.Core.Models.Mapping
|
||||
{
|
||||
public abstract void ConfigureMappings(IConfiguration config, ApplicationContext applicationContext);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,11 +1,41 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq.Expressions;
|
||||
using Umbraco.Core.Models.Identity;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
[MapperFor(typeof(IIdentityUserLogin))]
|
||||
[MapperFor(typeof(IdentityUserLogin))]
|
||||
public sealed class ExternalLoginMapper : BaseMapper
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, DtoMapModel> PropertyInfoCacheInstance = new ConcurrentDictionary<string, DtoMapModel>();
|
||||
public ExternalLoginMapper()
|
||||
{
|
||||
BuildMap();
|
||||
}
|
||||
|
||||
#region Overrides of BaseMapper
|
||||
|
||||
internal override ConcurrentDictionary<string, DtoMapModel> PropertyInfoCache
|
||||
{
|
||||
get { return PropertyInfoCacheInstance; }
|
||||
}
|
||||
|
||||
internal override void BuildMap()
|
||||
{
|
||||
CacheMap<IdentityUserLogin, ExternalLoginDto>(src => src.Id, dto => dto.Id);
|
||||
CacheMap<IdentityUserLogin, ExternalLoginDto>(src => src.CreateDate, dto => dto.CreateDate);
|
||||
CacheMap<IdentityUserLogin, ExternalLoginDto>(src => src.LoginProvider, dto => dto.LoginProvider);
|
||||
CacheMap<IdentityUserLogin, ExternalLoginDto>(src => src.ProviderKey, dto => dto.ProviderKey);
|
||||
CacheMap<IdentityUserLogin, ExternalLoginDto>(src => src.UserId, dto => dto.UserId);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
[MapperFor(typeof(IUser))]
|
||||
[MapperFor(typeof(User))]
|
||||
public sealed class UserMapper : BaseMapper
|
||||
|
||||
@@ -3,8 +3,10 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Security;
|
||||
using AutoMapper;
|
||||
using Microsoft.AspNet.Identity;
|
||||
using Umbraco.Core.Models.Identity;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Security
|
||||
@@ -13,22 +15,19 @@ namespace Umbraco.Core.Security
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly IExternalLoginService _externalLoginService;
|
||||
private readonly MembershipProviderBase _usersMembershipProvider;
|
||||
|
||||
public BackOfficeUserStore(IUserService userService, IExternalLoginService externalLoginService, MembershipProviderBase usersMembershipProvider)
|
||||
{
|
||||
_userService = userService;
|
||||
_externalLoginService = externalLoginService;
|
||||
_usersMembershipProvider = usersMembershipProvider;
|
||||
if (userService == null) throw new ArgumentNullException("userService");
|
||||
if (usersMembershipProvider == null) throw new ArgumentNullException("usersMembershipProvider");
|
||||
if (externalLoginService == null) throw new ArgumentNullException("externalLoginService");
|
||||
|
||||
_userService = userService;
|
||||
_usersMembershipProvider = usersMembershipProvider;
|
||||
_externalLoginService = externalLoginService;
|
||||
|
||||
if (_usersMembershipProvider.PasswordFormat != MembershipPasswordFormat.Hashed)
|
||||
if (usersMembershipProvider.PasswordFormat != MembershipPasswordFormat.Hashed)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot use ASP.Net Identity with UmbracoMembersUserStore when the password format is not Hashed");
|
||||
}
|
||||
@@ -39,7 +38,6 @@ namespace Umbraco.Core.Security
|
||||
/// </summary>
|
||||
protected override void DisposeResources()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -49,7 +47,43 @@ namespace Umbraco.Core.Security
|
||||
/// <returns/>
|
||||
public Task CreateAsync(BackOfficeIdentityUser user)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
|
||||
var userType = _userService.GetUserTypeByAlias(
|
||||
user.UserTypeAlias.IsNullOrWhiteSpace() ? _userService.GetDefaultMemberType() : user.UserTypeAlias);
|
||||
|
||||
var member = new User(userType)
|
||||
{
|
||||
DefaultToLiveEditing = false,
|
||||
Email = user.Email,
|
||||
Language = Configuration.GlobalSettings.DefaultUILanguage,
|
||||
Name = user.Name,
|
||||
Username = user.UserName,
|
||||
StartContentId = -1,
|
||||
StartMediaId = -1,
|
||||
IsLockedOut = false,
|
||||
IsApproved = true
|
||||
};
|
||||
|
||||
UpdateMemberProperties(member, user);
|
||||
|
||||
//the password must be 'something' it could be empty if authenticating
|
||||
// with an external provider so we'll just generate one and prefix it, the
|
||||
// prefix will help us determine if the password hasn't actually been specified yet.
|
||||
if (member.RawPasswordValue.IsNullOrWhiteSpace())
|
||||
{
|
||||
//this will hash the guid with a salt so should be nicely random
|
||||
var aspHasher = new PasswordHasher();
|
||||
member.RawPasswordValue = "___UIDEMPTYPWORD__" +
|
||||
aspHasher.HashPassword(Guid.NewGuid().ToString("N"));
|
||||
|
||||
}
|
||||
_userService.Save(member);
|
||||
|
||||
//re-assign id
|
||||
user.Id = member.Id;
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -57,9 +91,30 @@ namespace Umbraco.Core.Security
|
||||
/// </summary>
|
||||
/// <param name="user"/>
|
||||
/// <returns/>
|
||||
public Task UpdateAsync(BackOfficeIdentityUser user)
|
||||
public async Task UpdateAsync(BackOfficeIdentityUser user)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
|
||||
var asInt = user.Id.TryConvertTo<int>();
|
||||
if (asInt == false)
|
||||
{
|
||||
throw new InvalidOperationException("The user id must be an integer to work with the Umbraco");
|
||||
}
|
||||
|
||||
var found = _userService.GetUserById(asInt.Result);
|
||||
if (found != null)
|
||||
{
|
||||
if (UpdateMemberProperties(found, user))
|
||||
{
|
||||
_userService.Save(found);
|
||||
}
|
||||
|
||||
if (user.LoginsChanged)
|
||||
{
|
||||
var logins = await GetLoginsAsync(user);
|
||||
_externalLoginService.SaveUserLogins(found.Id, logins);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -69,7 +124,22 @@ namespace Umbraco.Core.Security
|
||||
/// <returns/>
|
||||
public Task DeleteAsync(BackOfficeIdentityUser user)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
|
||||
var asInt = user.Id.TryConvertTo<int>();
|
||||
if (asInt == false)
|
||||
{
|
||||
throw new InvalidOperationException("The user id must be an integer to work with the Umbraco");
|
||||
}
|
||||
|
||||
var found = _userService.GetUserById(asInt.Result);
|
||||
if (found != null)
|
||||
{
|
||||
_userService.Delete(found);
|
||||
}
|
||||
_externalLoginService.DeleteUserLogins(asInt.Result);
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -79,7 +149,12 @@ namespace Umbraco.Core.Security
|
||||
/// <returns/>
|
||||
public Task<BackOfficeIdentityUser> FindByIdAsync(int userId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var user = _userService.GetUserById(userId);
|
||||
if (user == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return Task.FromResult(AssignLoginsCallback(Mapper.Map<BackOfficeIdentityUser>(user)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -89,7 +164,15 @@ namespace Umbraco.Core.Security
|
||||
/// <returns/>
|
||||
public Task<BackOfficeIdentityUser> FindByNameAsync(string userName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var user = _userService.GetByUsername(userName);
|
||||
if (user == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = AssignLoginsCallback(Mapper.Map<BackOfficeIdentityUser>(user));
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -99,7 +182,12 @@ namespace Umbraco.Core.Security
|
||||
/// <returns/>
|
||||
public Task SetPasswordHashAsync(BackOfficeIdentityUser user, string passwordHash)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
if (passwordHash.IsNullOrWhiteSpace()) throw new ArgumentNullException("passwordHash");
|
||||
|
||||
user.PasswordHash = passwordHash;
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -109,7 +197,9 @@ namespace Umbraco.Core.Security
|
||||
/// <returns/>
|
||||
public Task<string> GetPasswordHashAsync(BackOfficeIdentityUser user)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
|
||||
return Task.FromResult(user.PasswordHash);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -119,7 +209,9 @@ namespace Umbraco.Core.Security
|
||||
/// <returns/>
|
||||
public Task<bool> HasPasswordAsync(BackOfficeIdentityUser user)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
|
||||
return Task.FromResult(user.PasswordHash.IsNullOrWhiteSpace() == false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -129,7 +221,12 @@ namespace Umbraco.Core.Security
|
||||
/// <returns/>
|
||||
public Task SetEmailAsync(BackOfficeIdentityUser user, string email)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
if (email.IsNullOrWhiteSpace()) throw new ArgumentNullException("email");
|
||||
|
||||
user.Email = email;
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -139,7 +236,9 @@ namespace Umbraco.Core.Security
|
||||
/// <returns/>
|
||||
public Task<string> GetEmailAsync(BackOfficeIdentityUser user)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
|
||||
return Task.FromResult(user.Email);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -169,7 +268,12 @@ namespace Umbraco.Core.Security
|
||||
/// <returns/>
|
||||
public Task<BackOfficeIdentityUser> FindByEmailAsync(string email)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var user = _userService.GetByEmail(email);
|
||||
var result = user == null
|
||||
? null
|
||||
: Mapper.Map<BackOfficeIdentityUser>(user);
|
||||
|
||||
return Task.FromResult(AssignLoginsCallback(result));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -179,7 +283,15 @@ namespace Umbraco.Core.Security
|
||||
/// <returns/>
|
||||
public Task AddLoginAsync(BackOfficeIdentityUser user, UserLoginInfo login)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
if (login == null) throw new ArgumentNullException("login");
|
||||
|
||||
var logins = user.Logins;
|
||||
var instance = new IdentityUserLogin(login.LoginProvider, login.ProviderKey, user.Id);
|
||||
var userLogin = instance;
|
||||
logins.Add(userLogin);
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -189,7 +301,16 @@ namespace Umbraco.Core.Security
|
||||
/// <returns/>
|
||||
public Task RemoveLoginAsync(BackOfficeIdentityUser user, UserLoginInfo login)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
if (login == null) throw new ArgumentNullException("login");
|
||||
|
||||
var provider = login.LoginProvider;
|
||||
var key = login.ProviderKey;
|
||||
var userLogin = user.Logins.SingleOrDefault((l => l.LoginProvider == provider && l.ProviderKey == key));
|
||||
if (userLogin != null)
|
||||
user.Logins.Remove(userLogin);
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -199,7 +320,9 @@ namespace Umbraco.Core.Security
|
||||
/// <returns/>
|
||||
public Task<IList<UserLoginInfo>> GetLoginsAsync(BackOfficeIdentityUser user)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
return Task.FromResult((IList<UserLoginInfo>)
|
||||
user.Logins.Select(l => new UserLoginInfo(l.LoginProvider, l.ProviderKey)).ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -213,21 +336,13 @@ namespace Umbraco.Core.Security
|
||||
if (result.Any())
|
||||
{
|
||||
//return the first member that matches the result
|
||||
var user = (from l in result
|
||||
var output = (from l in result
|
||||
select _userService.GetUserById(l.Id)
|
||||
into member
|
||||
where member != null
|
||||
select new BackOfficeIdentityUser
|
||||
{
|
||||
Email = member.Email,
|
||||
Id = member.Id,
|
||||
LockoutEnabled = member.IsLockedOut,
|
||||
LockoutEndDateUtc = DateTime.MaxValue.ToUniversalTime(),
|
||||
UserName = member.Username,
|
||||
PasswordHash = GetPasswordHash(member.RawPasswordValue)
|
||||
}).FirstOrDefault();
|
||||
into user
|
||||
where user != null
|
||||
select Mapper.Map<BackOfficeIdentityUser>(user)).FirstOrDefault();
|
||||
|
||||
return Task.FromResult(AssignLoginsCallback(user));
|
||||
return Task.FromResult(AssignLoginsCallback(output));
|
||||
}
|
||||
|
||||
return Task.FromResult<BackOfficeIdentityUser>(null);
|
||||
@@ -243,9 +358,73 @@ namespace Umbraco.Core.Security
|
||||
return user;
|
||||
}
|
||||
|
||||
private string GetPasswordHash(string storedPass)
|
||||
private bool UpdateMemberProperties(Models.Membership.IUser user, BackOfficeIdentityUser identityUser)
|
||||
{
|
||||
return storedPass.StartsWith("___UIDEMPTYPWORD__") ? null : storedPass;
|
||||
var anythingChanged = false;
|
||||
//don't assign anything if nothing has changed as this will trigger
|
||||
//the track changes of the model
|
||||
if (user.Name != identityUser.Name && identityUser.Name.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.Name = identityUser.Name;
|
||||
}
|
||||
if (user.Email != identityUser.Email && identityUser.Email.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.Email = identityUser.Email;
|
||||
}
|
||||
if (user.FailedPasswordAttempts != identityUser.AccessFailedCount)
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.FailedPasswordAttempts = identityUser.AccessFailedCount;
|
||||
}
|
||||
if (user.IsLockedOut != identityUser.LockoutEnabled)
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.IsLockedOut = identityUser.LockoutEnabled;
|
||||
}
|
||||
if (user.Username != identityUser.UserName && identityUser.UserName.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.Username = identityUser.UserName;
|
||||
}
|
||||
if (user.RawPasswordValue != identityUser.PasswordHash && identityUser.PasswordHash.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.RawPasswordValue = identityUser.PasswordHash;
|
||||
}
|
||||
|
||||
if (user.Language != identityUser.Culture && identityUser.Culture.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.Language = identityUser.Culture;
|
||||
}
|
||||
if (user.StartMediaId != identityUser.StartMediaNode)
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.StartMediaId = identityUser.StartMediaNode;
|
||||
}
|
||||
if (user.StartContentId != identityUser.StartContentNode)
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.StartContentId = identityUser.StartContentNode;
|
||||
}
|
||||
if (user.AllowedSections.ContainsAll(identityUser.AllowedApplications) == false
|
||||
|| identityUser.AllowedApplications.ContainsAll(user.AllowedSections) == false)
|
||||
{
|
||||
anythingChanged = true;
|
||||
foreach (var allowedSection in user.AllowedSections)
|
||||
{
|
||||
user.RemoveAllowedSection(allowedSection);
|
||||
}
|
||||
foreach (var allowedApplication in identityUser.AllowedApplications)
|
||||
{
|
||||
user.AddAllowedSection(allowedApplication);
|
||||
}
|
||||
}
|
||||
|
||||
return anythingChanged;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -344,6 +344,7 @@
|
||||
<Compile Include="IDisposeOnRequestEnd.cs" />
|
||||
<Compile Include="Models\AuditItem.cs" />
|
||||
<Compile Include="Models\AuditType.cs" />
|
||||
<Compile Include="Models\Identity\IdentityModelMappings.cs" />
|
||||
<Compile Include="Models\Identity\IdentityUser.cs" />
|
||||
<Compile Include="Models\Identity\IdentityUserClaim.cs" />
|
||||
<Compile Include="Models\Identity\IdentityUserLogin.cs" />
|
||||
|
||||
@@ -77,6 +77,8 @@ namespace Umbraco.Web.Security.Identity
|
||||
{
|
||||
if (app == null) throw new ArgumentNullException("app");
|
||||
|
||||
//TODO: Figure out why this isn't working and is only working with the default one, must be a reference somewhere
|
||||
|
||||
//app.UseExternalSignInCookie("UmbracoExternalCookie");
|
||||
|
||||
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
|
||||
|
||||
Reference in New Issue
Block a user