Adds notes for external login service/repo, changes IdentityUserLogin user id to string for now so it can be shared with members/users

This commit is contained in:
Shannon
2020-12-04 00:20:48 +11:00
parent 90b7ee3f37
commit c51ed88d56
11 changed files with 87 additions and 38 deletions

View File

@@ -21,7 +21,7 @@ namespace Umbraco.Core.Models.Identity
/// <summary>
/// Gets or sets user Id for the user who owns this login
/// </summary>
int UserId { get; set; }
string UserId { get; set; } // TODO: This should be able to be used by both users and members
/// <summary>
/// Gets or sets any arbitrary data for the user and external provider - like user tokens returned from the provider

View File

@@ -9,14 +9,14 @@ namespace Umbraco.Core.Models.Identity
/// </summary>
public class IdentityUserLogin : EntityBase, IIdentityUserLogin
{
public IdentityUserLogin(string loginProvider, string providerKey, int userId)
public IdentityUserLogin(string loginProvider, string providerKey, string userId)
{
LoginProvider = loginProvider;
ProviderKey = providerKey;
UserId = userId;
}
public IdentityUserLogin(int id, string loginProvider, string providerKey, int userId, DateTime createDate)
public IdentityUserLogin(int id, string loginProvider, string providerKey, string userId, DateTime createDate)
{
Id = id;
LoginProvider = loginProvider;
@@ -32,7 +32,7 @@ namespace Umbraco.Core.Models.Identity
public string ProviderKey { get; set; }
/// <inheritdoc />
public int UserId { get; set; }
public string UserId { get; set; }
/// <inheritdoc />
public string UserData { get; set; }

View File

@@ -9,7 +9,7 @@ namespace Umbraco.Core.Models.Identity
/// <summary>
/// Gets or sets userId for the user that is in the role
/// </summary>
public virtual string UserId { get; set; }
public virtual int UserId { get; set; }
/// <summary>
/// Gets or sets roleId for the role

View File

@@ -239,18 +239,18 @@ namespace Umbraco.Core.Security
get => _groups;
set
{
//so they recalculate
// so they recalculate
_allowedSections = null;
_groups = value;
//now clear all roles and re-add them
// now clear all roles and re-add them
_roles.CollectionChanged -= _roles_CollectionChanged;
_roles.Clear();
foreach (var identityUserRole in _groups.Select(x => new IdentityUserRole
{
RoleId = x.Alias,
UserId = Id.ToString()
UserId = Id
}))
{
_roles.Add(identityUserRole);
@@ -342,7 +342,7 @@ namespace Umbraco.Core.Security
{
Roles.Add(new IdentityUserRole
{
UserId = Id.ToString(),
UserId = Id,
RoleId = role
});
}

View File

@@ -417,7 +417,7 @@ namespace Umbraco.Core.BackOffice
if (login == null) throw new ArgumentNullException(nameof(login));
var logins = user.Logins;
var instance = new IdentityUserLogin(login.LoginProvider, login.ProviderKey, user.Id);
var instance = new IdentityUserLogin(login.LoginProvider, login.ProviderKey, user.Id.ToString());
var userLogin = instance;
logins.Add(userLogin);
@@ -462,25 +462,29 @@ namespace Umbraco.Core.BackOffice
/// <summary>
/// Returns the user associated with this login
/// </summary>
/// <returns/>
public Task<BackOfficeIdentityUser> FindByLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
//get all logins associated with the login id
var result = _externalLoginService.Find(loginProvider, providerKey).ToArray();
// get all logins associated with the login id
IIdentityUserLogin[] result = _externalLoginService.Find(loginProvider, providerKey).ToArray();
if (result.Any())
{
//return the first user that matches the result
// return the first user that matches the result
BackOfficeIdentityUser output = null;
foreach (var l in result)
foreach (IIdentityUserLogin l in result)
{
var user = _userService.GetUserById(l.UserId);
if (user != null)
// TODO: This won't be necessary once we add GUID support for users and make the external login
// table uses GUIDs without referential integrity
if (int.TryParse(l.UserId, out int userId))
{
output = _mapper.Map<BackOfficeIdentityUser>(user);
break;
IUser user = _userService.GetUserById(userId);
if (user != null)
{
output = _mapper.Map<BackOfficeIdentityUser>(user);
break;
}
}
}

View File

@@ -18,16 +18,47 @@ namespace Umbraco.Core.BackOffice
public interface IUmbracoUserManager<TUser> : IDisposable
where TUser : BackOfficeIdentityUser
{
/// <summary>
/// Gets the user id of a user
/// </summary>
/// <param name="user">The user</param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
Task<string> GetUserIdAsync(TUser user);
/// <summary>
/// Get the <see cref="TUser"/> from a <see cref="ClaimsPrincipal"/>
/// </summary>
/// <param name="principal">The <see cref="ClaimsPrincipal"/></param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
Task<TUser> GetUserAsync(ClaimsPrincipal principal);
/// <summary>
/// Get the user id from the <see cref="ClaimsPrincipal"/>
/// </summary>
/// <param name="principal">the <see cref="ClaimsPrincipal"/></param>
/// <returns>Returns the user id from the <see cref="ClaimsPrincipal"/></returns>
string GetUserId(ClaimsPrincipal principal);
/// <summary>
/// Gets the external logins for the user
/// </summary>
/// <param name="user"></param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
Task<IList<UserLoginInfo>> GetLoginsAsync(TUser user);
/// <summary>
/// Deletes a user
/// </summary>
/// <param name="user"></param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
Task<IdentityResult> DeleteAsync(TUser user);
/// <summary>
/// Finds a user by the external login provider
/// </summary>
/// <param name="loginProvider"></param>
/// <param name="providerKey"></param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
Task<TUser> FindByLoginAsync(string loginProvider, string providerKey);
/// <summary>
@@ -147,8 +178,7 @@ namespace Umbraco.Core.BackOffice
/// The <see cref="Task"/> that represents the asynchronous operation, returning true if the <paramref name="token"/>
/// is valid, otherwise false.
/// </returns>
Task<bool> VerifyUserTokenAsync(TUser user, string tokenProvider, string purpose,
string token);
Task<bool> VerifyUserTokenAsync(TUser user, string tokenProvider, string purpose, string token);
/// <summary>
/// Adds the <paramref name="password"/> to the specified <paramref name="user"/> only if the user
@@ -185,15 +215,14 @@ namespace Umbraco.Core.BackOffice
/// The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/>
/// of the operation.
/// </returns>
Task<IdentityResult> ChangePasswordAsync(TUser user, string currentPassword,
string newPassword);
Task<IdentityResult> ChangePasswordAsync(TUser user, string currentPassword, string newPassword);
/// <summary>
/// Used to validate a user's session
/// </summary>
/// <param name="userId"></param>
/// <param name="sessionId"></param>
/// <returns></returns>
/// <returns>Returns true if the session is valid, otherwise false</returns>
Task<bool> ValidateSessionIdAsync(string userId, string sessionId);
/// <summary>
@@ -208,12 +237,11 @@ namespace Umbraco.Core.BackOffice
Task<IdentityResult> CreateAsync(TUser user);
/// <summary>
/// Helper method to generate a password for a user based on the current password validator
/// Generate a password for a user based on the current password validator
/// </summary>
/// <returns></returns>
/// <returns>A generated password</returns>
string GeneratePassword();
/// <summary>
/// Generates an email confirmation token for the specified user.
/// </summary>
@@ -292,8 +320,19 @@ namespace Umbraco.Core.BackOffice
/// <returns>The System.Threading.Tasks.Task that represents the asynchronous operation, containing the Microsoft.AspNetCore.Identity.IdentityResult of the operation.</returns>
Task<IdentityResult> RemoveLoginAsync(TUser user, string loginProvider, string providerKey);
/// <summary>
/// Resets the access failed count for the user
/// </summary>
/// <param name="user"></param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
Task<IdentityResult> ResetAccessFailedCountAsync(TUser user);
/// <summary>
/// Generates a two factor token for the user
/// </summary>
/// <param name="user"></param>
/// <param name="tokenProvider"></param>
/// <returns>A <see cref="Task{TResult}"/> representing the result of the asynchronous operation.</returns>
Task<string> GenerateTwoFactorTokenAsync(TUser user, string tokenProvider);
/// <summary>
@@ -316,6 +355,7 @@ namespace Umbraco.Core.BackOffice
// TODO: These are raised from outside the signinmanager and usermanager in the auth and user controllers,
// let's see if there's a way to avoid that and only have these called within signinmanager and usermanager
// which means we can remove these from the interface (things like invite seems like they cannot be moved)
// TODO: When we change to not having the crappy static events this will need to be revisited
void RaiseForgotPasswordRequestedEvent(IPrincipal currentUser, int userId);
void RaiseForgotPasswordChangedSuccessEvent(IPrincipal currentUser, int userId);
SignOutAuditEventArgs RaiseLogoutSuccessEvent(IPrincipal currentUser, int userId);

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using NPoco;
using Umbraco.Core.Persistence.DatabaseAnnotations;
@@ -19,6 +19,8 @@ namespace Umbraco.Core.Persistence.Dtos
UserStartNodeDtos = new HashSet<UserStartNodeDto>();
}
// TODO: We need to add a GUID for users and track external logins with that instead of the INT
[Column("id")]
[PrimaryKeyColumn(Name = "PK_user")]
public int Id { get; set; }

View File

@@ -1,4 +1,4 @@
using System;
using System;
using Umbraco.Core.Models.Identity;
using Umbraco.Core.Persistence.Dtos;
@@ -8,7 +8,7 @@ namespace Umbraco.Core.Persistence.Factories
{
public static IIdentityUserLogin 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.ToString(), dto.CreateDate)
{
UserData = dto.UserData
};
@@ -26,7 +26,7 @@ namespace Umbraco.Core.Persistence.Factories
CreateDate = entity.CreateDate,
LoginProvider = entity.LoginProvider,
ProviderKey = entity.ProviderKey,
UserId = entity.UserId,
UserId = int.Parse(entity.UserId), // TODO: This is temp until we change the ext logins to use GUIDs
UserData = entity.UserData
};

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
@@ -13,6 +13,8 @@ using Umbraco.Core.Scoping;
namespace Umbraco.Core.Persistence.Repositories.Implement
{
// TODO: We should update this to support both users and members. It means we would remove referential integrity from users
// and the user/member key would be a GUID (we also need to add a GUID to users)
internal class ExternalLoginRepository : NPocoRepositoryBase<int, IIdentityUserLogin>, IExternalLoginRepository
{
public ExternalLoginRepository(IScopeAccessor scopeAccessor, AppCaches cache, ILogger<ExternalLoginRepository> logger)

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
@@ -25,7 +25,8 @@ namespace Umbraco.Core.Services.Implement
{
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
{
return _externalLoginRepository.Get(Query<IIdentityUserLogin>().Where(x => x.UserId == userId))
var asString = userId.ToString(); // TODO: This is temp until we update the external service to support guids for both users and members
return _externalLoginRepository.Get(Query<IIdentityUserLogin>().Where(x => x.UserId == asString))
.ToList();
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Linq;
using System.Threading;
using NUnit.Framework;
@@ -93,7 +93,7 @@ namespace Umbraco.Tests.Services
var user = new User(GlobalSettings, "Test", "test@test.com", "test", "helloworldtest");
UserService.Save(user);
var extLogin = new IdentityUserLogin("test1", Guid.NewGuid().ToString("N"), user.Id)
var extLogin = new IdentityUserLogin("test1", Guid.NewGuid().ToString("N"), user.Id.ToString())
{
UserData = "hello"
};
@@ -112,7 +112,7 @@ namespace Umbraco.Tests.Services
var user = new User(GlobalSettings, "Test", "test@test.com", "test", "helloworldtest");
UserService.Save(user);
var extLogin = new IdentityUserLogin("test1", Guid.NewGuid().ToString("N"), user.Id)
var extLogin = new IdentityUserLogin("test1", Guid.NewGuid().ToString("N"), user.Id.ToString())
{
UserData = "hello"
};
@@ -218,7 +218,7 @@ namespace Umbraco.Tests.Services
var logins = ExternalLoginService.GetAll(user.Id).OrderBy(x => x.LoginProvider).ToList();
logins.RemoveAt(0); // remove the first one
logins.Add(new IdentityUserLogin("test5", Guid.NewGuid().ToString("N"), user.Id)); // add a new one
logins.Add(new IdentityUserLogin("test5", Guid.NewGuid().ToString("N"), user.Id.ToString())); // add a new one
// save new list
ExternalLoginService.Save(user.Id, logins.Select(x => new ExternalLogin(x.LoginProvider, x.ProviderKey)));