Port 7.7 - WIP
This commit is contained in:
@@ -8,9 +8,13 @@ using System.Web.Security;
|
||||
using AutoMapper;
|
||||
using Microsoft.AspNet.Identity;
|
||||
using Microsoft.Owin;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Models.Identity;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.Services;
|
||||
using IUser = Umbraco.Core.Models.Membership.IUser;
|
||||
using Task = System.Threading.Tasks.Task;
|
||||
|
||||
namespace Umbraco.Core.Security
|
||||
{
|
||||
@@ -31,13 +35,15 @@ namespace Umbraco.Core.Security
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly IMemberTypeService _memberTypeService;
|
||||
private readonly IEntityService _entityService;
|
||||
private readonly IExternalLoginService _externalLoginService;
|
||||
private bool _disposed = false;
|
||||
|
||||
public BackOfficeUserStore(IUserService userService, IMemberTypeService memberTypeService, IExternalLoginService externalLoginService, MembershipProviderBase usersMembershipProvider)
|
||||
public BackOfficeUserStore(IUserService userService, IMemberTypeService memberTypeService, IEntityService entityService, IExternalLoginService externalLoginService, MembershipProviderBase usersMembershipProvider)
|
||||
{
|
||||
_userService = userService;
|
||||
_memberTypeService = memberTypeService;
|
||||
_entityService = entityService;
|
||||
_externalLoginService = externalLoginService;
|
||||
if (userService == null) throw new ArgumentNullException("userService");
|
||||
if (usersMembershipProvider == null) throw new ArgumentNullException("usersMembershipProvider");
|
||||
@@ -70,41 +76,33 @@ namespace Umbraco.Core.Security
|
||||
ThrowIfDisposed();
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
|
||||
var userType = _userService.GetUserTypeByAlias(
|
||||
user.UserTypeAlias.IsNullOrWhiteSpace() ? _memberTypeService.GetDefault() : user.UserTypeAlias);
|
||||
//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.
|
||||
//this will hash the guid with a salt so should be nicely random
|
||||
var aspHasher = new PasswordHasher();
|
||||
var emptyPasswordValue = Constants.Security.EmptyPasswordPrefix +
|
||||
aspHasher.HashPassword(Guid.NewGuid().ToString("N"));
|
||||
|
||||
var member = new User(userType)
|
||||
var userEntity = new User(user.Name, user.Email, user.UserName, emptyPasswordValue)
|
||||
{
|
||||
DefaultToLiveEditing = false,
|
||||
Email = user.Email,
|
||||
Language = user.Culture ?? Configuration.GlobalSettings.DefaultUILanguage,
|
||||
Name = user.Name,
|
||||
Username = user.UserName,
|
||||
StartContentId = user.StartContentId == 0 ? -1 : user.StartContentId,
|
||||
StartMediaId = user.StartMediaId == 0 ? -1 : user.StartMediaId,
|
||||
StartContentIds = user.StartContentIds ?? new int[] { },
|
||||
StartMediaIds = user.StartMediaIds ?? new int[] { },
|
||||
IsLockedOut = user.IsLockedOut,
|
||||
IsApproved = true
|
||||
};
|
||||
|
||||
UpdateMemberProperties(member, user);
|
||||
UpdateMemberProperties(userEntity, user);
|
||||
|
||||
//TODO: We should deal with Roles --> User Groups here which we currently are not doing
|
||||
|
||||
//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(userEntity);
|
||||
|
||||
}
|
||||
_userService.Save(member);
|
||||
|
||||
if (member.Id == 0) throw new DataException("Could not create the user, check logs for details");
|
||||
if (userEntity.Id == 0) throw new DataException("Could not create the user, check logs for details");
|
||||
|
||||
//re-assign id
|
||||
user.Id = member.Id;
|
||||
user.Id = userEntity.Id;
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
@@ -133,7 +131,7 @@ namespace Umbraco.Core.Security
|
||||
_userService.Save(found);
|
||||
}
|
||||
|
||||
if (user.LoginsChanged)
|
||||
if (user.IsPropertyDirty("Logins"))
|
||||
{
|
||||
var logins = await GetLoginsAsync(user);
|
||||
_externalLoginService.SaveUserLogins(found.Id, logins);
|
||||
@@ -201,7 +199,7 @@ namespace Umbraco.Core.Security
|
||||
|
||||
return await Task.FromResult(result);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Set the user password hash
|
||||
/// </summary>
|
||||
@@ -211,7 +209,7 @@ namespace Umbraco.Core.Security
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
if (passwordHash.IsNullOrWhiteSpace()) throw new ArgumentNullException("passwordHash");
|
||||
if (string.IsNullOrEmpty(passwordHash)) throw new ArgumentNullOrEmptyException(nameof(passwordHash));
|
||||
|
||||
user.PasswordHash = passwordHash;
|
||||
|
||||
@@ -227,7 +225,7 @@ namespace Umbraco.Core.Security
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
|
||||
|
||||
return Task.FromResult(user.PasswordHash);
|
||||
}
|
||||
|
||||
@@ -241,7 +239,7 @@ namespace Umbraco.Core.Security
|
||||
ThrowIfDisposed();
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
|
||||
return Task.FromResult(user.PasswordHash.IsNullOrWhiteSpace() == false);
|
||||
return Task.FromResult(string.IsNullOrEmpty(user.PasswordHash) == false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -281,7 +279,9 @@ namespace Umbraco.Core.Security
|
||||
public Task<bool> GetEmailConfirmedAsync(BackOfficeIdentityUser user)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
throw new NotImplementedException();
|
||||
if (user == null) throw new ArgumentNullException(nameof(user));
|
||||
|
||||
return Task.FromResult(user.EmailConfirmed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -292,7 +292,8 @@ namespace Umbraco.Core.Security
|
||||
public Task SetEmailConfirmedAsync(BackOfficeIdentityUser user, bool confirmed)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
throw new NotImplementedException();
|
||||
user.EmailConfirmed = confirmed;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -376,12 +377,17 @@ namespace Umbraco.Core.Security
|
||||
var result = _externalLoginService.Find(login).ToArray();
|
||||
if (result.Any())
|
||||
{
|
||||
//return the first member that matches the result
|
||||
var output = (from l in result
|
||||
select _userService.GetUserById(l.UserId)
|
||||
into user
|
||||
where user != null
|
||||
select Mapper.Map<BackOfficeIdentityUser>(user)).FirstOrDefault();
|
||||
//return the first user that matches the result
|
||||
BackOfficeIdentityUser output = null;
|
||||
foreach (var l in result)
|
||||
{
|
||||
var user = _userService.GetUserById(l.UserId);
|
||||
if (user != null)
|
||||
{
|
||||
output = Mapper.Map<BackOfficeIdentityUser>(user);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult(AssignLoginsCallback(output));
|
||||
}
|
||||
@@ -391,7 +397,7 @@ namespace Umbraco.Core.Security
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds a user to a role (section)
|
||||
/// Adds a user to a role (user group)
|
||||
/// </summary>
|
||||
/// <param name="user"/><param name="roleName"/>
|
||||
/// <returns/>
|
||||
@@ -399,27 +405,20 @@ namespace Umbraco.Core.Security
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
if (string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("Value cannot be null or whitespace.", "roleName");
|
||||
|
||||
if (user.AllowedSections.InvariantContains(roleName)) return Task.FromResult(0);
|
||||
var userRole = user.Roles.SingleOrDefault(r => r.RoleId == roleName);
|
||||
|
||||
var asInt = user.Id.TryConvertTo<int>();
|
||||
if (asInt == false)
|
||||
if (userRole == null)
|
||||
{
|
||||
throw new InvalidOperationException("The user id must be an integer to work with the Umbraco");
|
||||
}
|
||||
|
||||
var found = _userService.GetUserById(asInt.Result);
|
||||
|
||||
if (found != null)
|
||||
{
|
||||
found.AddAllowedSection(roleName);
|
||||
user.AddRole(roleName);
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the role (allowed section) for the user
|
||||
/// Removes the role (user group) for the user
|
||||
/// </summary>
|
||||
/// <param name="user"/><param name="roleName"/>
|
||||
/// <returns/>
|
||||
@@ -427,27 +426,20 @@ namespace Umbraco.Core.Security
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
if (string.IsNullOrWhiteSpace(roleName)) throw new ArgumentException("Value cannot be null or whitespace.", "roleName");
|
||||
|
||||
if (user.AllowedSections.InvariantContains(roleName) == false) return Task.FromResult(0);
|
||||
var userRole = user.Roles.SingleOrDefault(r => r.RoleId == roleName);
|
||||
|
||||
var asInt = user.Id.TryConvertTo<int>();
|
||||
if (asInt == false)
|
||||
if (userRole != null)
|
||||
{
|
||||
throw new InvalidOperationException("The user id must be an integer to work with the Umbraco");
|
||||
}
|
||||
|
||||
var found = _userService.GetUserById(asInt.Result);
|
||||
|
||||
if (found != null)
|
||||
{
|
||||
found.RemoveAllowedSection(roleName);
|
||||
user.Roles.Remove(userRole);
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the roles for this user
|
||||
/// Returns the roles (user groups) for this user
|
||||
/// </summary>
|
||||
/// <param name="user"/>
|
||||
/// <returns/>
|
||||
@@ -455,7 +447,7 @@ namespace Umbraco.Core.Security
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
return Task.FromResult((IList<string>)user.AllowedSections.ToList());
|
||||
return Task.FromResult((IList<string>)user.Roles.Select(x => x.RoleId).ToList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -467,7 +459,7 @@ namespace Umbraco.Core.Security
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
if (user == null) throw new ArgumentNullException("user");
|
||||
return Task.FromResult(user.AllowedSections.InvariantContains(roleName));
|
||||
return Task.FromResult(user.Roles.Select(x => x.RoleId).InvariantContains(roleName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -496,7 +488,7 @@ namespace Umbraco.Core.Security
|
||||
|
||||
//the stamp cannot be null, so if it is currently null then we'll just return a hash of the password
|
||||
return Task.FromResult(user.SecurityStamp.IsNullOrWhiteSpace()
|
||||
? user.PasswordHash.ToMd5()
|
||||
? user.PasswordHash.GenerateHash()
|
||||
: user.SecurityStamp);
|
||||
}
|
||||
|
||||
@@ -622,28 +614,40 @@ namespace Umbraco.Core.Security
|
||||
}
|
||||
#endregion
|
||||
|
||||
private bool UpdateMemberProperties(Models.Membership.IUser user, BackOfficeIdentityUser identityUser)
|
||||
private bool UpdateMemberProperties(IUser user, BackOfficeIdentityUser identityUser)
|
||||
{
|
||||
var anythingChanged = false;
|
||||
//don't assign anything if nothing has changed as this will trigger
|
||||
//the track changes of the model
|
||||
if ((user.LastLoginDate != default(DateTime) && identityUser.LastLoginDateUtc.HasValue == false)
|
||||
|
||||
//don't assign anything if nothing has changed as this will trigger the track changes of the model
|
||||
|
||||
if (identityUser.IsPropertyDirty("LastLoginDateUtc")
|
||||
|| (user.LastLoginDate != default(DateTime) && identityUser.LastLoginDateUtc.HasValue == false)
|
||||
|| identityUser.LastLoginDateUtc.HasValue && user.LastLoginDate.ToUniversalTime() != identityUser.LastLoginDateUtc.Value)
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.LastLoginDate = identityUser.LastLoginDateUtc.Value.ToLocalTime();
|
||||
}
|
||||
if (user.Name != identityUser.Name && identityUser.Name.IsNullOrWhiteSpace() == false)
|
||||
if (identityUser.IsPropertyDirty("EmailConfirmed")
|
||||
|| (user.EmailConfirmedDate.HasValue && user.EmailConfirmedDate.Value != default(DateTime) && identityUser.EmailConfirmed == false)
|
||||
|| ((user.EmailConfirmedDate.HasValue == false || user.EmailConfirmedDate.Value == default(DateTime)) && identityUser.EmailConfirmed))
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.EmailConfirmedDate = identityUser.EmailConfirmed ? (DateTime?)DateTime.Now : null;
|
||||
}
|
||||
if (identityUser.IsPropertyDirty("Name")
|
||||
&& user.Name != identityUser.Name && identityUser.Name.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.Name = identityUser.Name;
|
||||
}
|
||||
if (user.Email != identityUser.Email && identityUser.Email.IsNullOrWhiteSpace() == false)
|
||||
if (identityUser.IsPropertyDirty("Email")
|
||||
&& user.Email != identityUser.Email && identityUser.Email.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.Email = identityUser.Email;
|
||||
}
|
||||
if (user.FailedPasswordAttempts != identityUser.AccessFailedCount)
|
||||
if (identityUser.IsPropertyDirty("AccessFailedCount")
|
||||
&& user.FailedPasswordAttempts != identityUser.AccessFailedCount)
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.FailedPasswordAttempts = identityUser.AccessFailedCount;
|
||||
@@ -660,31 +664,36 @@ namespace Umbraco.Core.Security
|
||||
}
|
||||
|
||||
}
|
||||
if (user.Username != identityUser.UserName && identityUser.UserName.IsNullOrWhiteSpace() == false)
|
||||
if (identityUser.IsPropertyDirty("UserName")
|
||||
&& user.Username != identityUser.UserName && identityUser.UserName.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.Username = identityUser.UserName;
|
||||
}
|
||||
if (user.RawPasswordValue != identityUser.PasswordHash && identityUser.PasswordHash.IsNullOrWhiteSpace() == false)
|
||||
if (identityUser.IsPropertyDirty("PasswordHash")
|
||||
&& user.RawPasswordValue != identityUser.PasswordHash && identityUser.PasswordHash.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.RawPasswordValue = identityUser.PasswordHash;
|
||||
}
|
||||
|
||||
if (user.Language != identityUser.Culture && identityUser.Culture.IsNullOrWhiteSpace() == false)
|
||||
if (identityUser.IsPropertyDirty("Culture")
|
||||
&& user.Language != identityUser.Culture && identityUser.Culture.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.Language = identityUser.Culture;
|
||||
}
|
||||
if (user.StartMediaId != identityUser.StartMediaId)
|
||||
if (identityUser.IsPropertyDirty("StartMediaIds")
|
||||
&& user.StartMediaIds.UnsortedSequenceEqual(identityUser.StartMediaIds) == false)
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.StartMediaId = identityUser.StartMediaId;
|
||||
user.StartMediaIds = identityUser.StartMediaIds;
|
||||
}
|
||||
if (user.StartContentId != identityUser.StartContentId)
|
||||
if (identityUser.IsPropertyDirty("StartContentIds")
|
||||
&& user.StartContentIds.UnsortedSequenceEqual(identityUser.StartContentIds) == false)
|
||||
{
|
||||
anythingChanged = true;
|
||||
user.StartContentId = identityUser.StartContentId;
|
||||
user.StartContentIds = identityUser.StartContentIds;
|
||||
}
|
||||
if (user.SecurityStamp != identityUser.SecurityStamp)
|
||||
{
|
||||
@@ -692,19 +701,44 @@ namespace Umbraco.Core.Security
|
||||
user.SecurityStamp = identityUser.SecurityStamp;
|
||||
}
|
||||
|
||||
if (user.AllowedSections.ContainsAll(identityUser.AllowedSections) == false
|
||||
|| identityUser.AllowedSections.ContainsAll(user.AllowedSections) == false)
|
||||
//TODO: Fix this for Groups too
|
||||
if (identityUser.IsPropertyDirty("Roles") || identityUser.IsPropertyDirty("Groups"))
|
||||
{
|
||||
anythingChanged = true;
|
||||
foreach (var allowedSection in user.AllowedSections)
|
||||
var userGroupAliases = user.Groups.Select(x => x.Alias).ToArray();
|
||||
|
||||
var identityUserRoles = identityUser.Roles.Select(x => x.RoleId).ToArray();
|
||||
var identityUserGroups = identityUser.Groups.Select(x => x.Alias).ToArray();
|
||||
|
||||
var combinedAliases = identityUserRoles.Union(identityUserGroups).ToArray();
|
||||
|
||||
if (userGroupAliases.ContainsAll(combinedAliases) == false
|
||||
|| combinedAliases.ContainsAll(userGroupAliases) == false)
|
||||
{
|
||||
user.RemoveAllowedSection(allowedSection);
|
||||
}
|
||||
foreach (var allowedApplication in identityUser.AllowedSections)
|
||||
{
|
||||
user.AddAllowedSection(allowedApplication);
|
||||
anythingChanged = true;
|
||||
|
||||
//clear out the current groups (need to ToArray since we are modifying the iterator)
|
||||
user.ClearGroups();
|
||||
|
||||
//go lookup all these groups
|
||||
var groups = _userService.GetUserGroupsByAlias(combinedAliases).Select(x => x.ToReadOnlyGroup()).ToArray();
|
||||
|
||||
//use all of the ones assigned and add them
|
||||
foreach (var group in groups)
|
||||
{
|
||||
user.AddGroup(group);
|
||||
}
|
||||
|
||||
//re-assign
|
||||
identityUser.Groups = groups;
|
||||
}
|
||||
}
|
||||
|
||||
//we should re-set the calculated start nodes
|
||||
identityUser.CalculatedMediaStartNodeIds = user.CalculateMediaStartNodeIds(_entityService);
|
||||
identityUser.CalculatedContentStartNodeIds = user.CalculateContentStartNodeIds(_entityService);
|
||||
|
||||
//reset all changes
|
||||
identityUser.ResetDirtyProperties(false);
|
||||
|
||||
return anythingChanged;
|
||||
}
|
||||
@@ -715,7 +749,5 @@ namespace Umbraco.Core.Security
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException(GetType().Name);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user