2020-12-03 23:49:32 +11:00
using System ;
2020-05-13 18:30:57 +01:00
using System.Collections.Generic ;
2020-12-04 12:44:27 +11:00
using System.ComponentModel ;
2020-05-13 18:30:57 +01:00
using System.Data ;
2021-09-20 11:22:51 +02:00
using System.Globalization ;
2020-05-13 18:30:57 +01:00
using System.Linq ;
2020-12-04 12:44:27 +11:00
using System.Security.Claims ;
2020-05-13 18:30:57 +01:00
using System.Threading ;
using System.Threading.Tasks ;
using Microsoft.AspNetCore.Identity ;
2020-08-21 14:52:47 +01:00
using Microsoft.Extensions.Options ;
2021-03-05 15:36:27 +01:00
using Umbraco.Cms.Core.Cache ;
2021-02-09 10:22:42 +01:00
using Umbraco.Cms.Core.Configuration.Models ;
using Umbraco.Cms.Core.Mapping ;
using Umbraco.Cms.Core.Models ;
using Umbraco.Cms.Core.Models.Membership ;
2021-02-15 11:41:12 +01:00
using Umbraco.Cms.Core.Scoping ;
2021-02-09 10:22:42 +01:00
using Umbraco.Cms.Core.Services ;
2021-02-09 11:26:22 +01:00
using Umbraco.Extensions ;
2020-05-13 18:30:57 +01:00
2021-02-15 12:01:12 +01:00
namespace Umbraco.Cms.Core.Security
2020-05-13 18:30:57 +01:00
{
2020-12-04 01:38:36 +11:00
2020-12-04 12:44:27 +11:00
/// <summary>
/// The user store for back office users
/// </summary>
2021-10-11 14:51:49 +01:00
public class BackOfficeUserStore : UmbracoUserStore < BackOfficeIdentityUser , IdentityRole < string > > , IUserSessionStore < BackOfficeIdentityUser >
2020-05-13 18:30:57 +01:00
{
2020-12-01 18:14:37 +11:00
private readonly IScopeProvider _scopeProvider ;
2020-05-13 18:30:57 +01:00
private readonly IUserService _userService ;
private readonly IEntityService _entityService ;
private readonly IExternalLoginService _externalLoginService ;
2020-08-21 14:52:47 +01:00
private readonly GlobalSettings _globalSettings ;
2021-04-20 19:34:18 +02:00
private readonly IUmbracoMapper _mapper ;
2021-03-05 15:36:27 +01:00
private readonly AppCaches _appCaches ;
2020-05-13 18:30:57 +01:00
2020-12-04 01:38:36 +11:00
/// <summary>
/// Initializes a new instance of the <see cref="BackOfficeUserStore"/> class.
/// </summary>
2020-12-04 12:44:27 +11:00
public BackOfficeUserStore (
IScopeProvider scopeProvider ,
IUserService userService ,
IEntityService entityService ,
IExternalLoginService externalLoginService ,
IOptions < GlobalSettings > globalSettings ,
2021-04-20 19:34:18 +02:00
IUmbracoMapper mapper ,
Implements Public Access in netcore (#10137)
* Getting new netcore PublicAccessChecker in place
* Adds full test coverage for PublicAccessChecker
* remove PublicAccessComposer
* adjust namespaces, ensure RoleManager works, separate public access controller, reduce content controller
* Implements the required methods on IMemberManager, removes old migrated code
* Updates routing to be able to re-route, Fixes middleware ordering ensuring endpoints are last, refactors pipeline options, adds public access middleware, ensures public access follows all hops
* adds note
* adds note
* Cleans up ext methods, ensures that members identity is added on both front-end and back ends. updates how UmbracoApplicationBuilder works in that it explicitly starts endpoints at the time of calling.
* Changes name to IUmbracoEndpointBuilder
* adds note
* Fixing tests, fixing error describers so there's 2x one for back office, one for members, fixes TryConvertTo, fixes login redirect
* fixing build
* Fixes keepalive, fixes PublicAccessMiddleware to not throw, updates startup code to be more clear and removes magic that registers middleware.
* adds note
* removes unused filter, fixes build
* fixes WebPath and tests
* Looks up entities in one query
* remove usings
* Fix test, remove stylesheet
* Set status code before we write to response to avoid error
* Ensures that users and members are validated when logging in. Shares more code between users and members.
* Fixes RepositoryCacheKeys to ensure the keys are normalized
* oops didn't mean to commit this
* Fix casing issues with caching, stop boxing value types for all cache operations, stop re-creating string keys in DefaultRepositoryCachePolicy
* bah, far out this keeps getting recommitted. sorry
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2021-04-20 15:11:45 +10:00
BackOfficeErrorDescriber describer ,
2021-03-05 15:36:27 +01:00
AppCaches appCaches )
2020-12-04 12:44:27 +11:00
: base ( describer )
2020-05-13 18:30:57 +01:00
{
2020-12-01 18:14:37 +11:00
_scopeProvider = scopeProvider ;
2020-12-04 01:38:36 +11:00
_userService = userService ? ? throw new ArgumentNullException ( nameof ( userService ) ) ;
2020-05-13 18:30:57 +01:00
_entityService = entityService ;
2020-12-04 01:38:36 +11:00
_externalLoginService = externalLoginService ? ? throw new ArgumentNullException ( nameof ( externalLoginService ) ) ;
2020-08-26 11:58:44 +02:00
_globalSettings = globalSettings . Value ;
2020-05-13 18:30:57 +01:00
_mapper = mapper ;
2021-03-05 15:36:27 +01:00
_appCaches = appCaches ;
2020-05-13 18:30:57 +01:00
_userService = userService ;
_externalLoginService = externalLoginService ;
}
2020-12-04 12:44:27 +11:00
/// <inheritdoc />
public override Task < IdentityResult > CreateAsync ( BackOfficeIdentityUser user , CancellationToken cancellationToken = default )
2020-05-13 18:30:57 +01:00
{
cancellationToken . ThrowIfCancellationRequested ( ) ;
ThrowIfDisposed ( ) ;
2020-12-04 12:44:27 +11:00
if ( user = = null )
{
throw new ArgumentNullException ( nameof ( user ) ) ;
}
2020-05-13 18:30:57 +01:00
2020-12-04 12:44:27 +11:00
// the password must be 'something' it could be empty if authenticating
2020-05-13 18:30:57 +01:00
// 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.
2020-12-04 12:44:27 +11:00
// this will hash the guid with a salt so should be nicely random
2020-05-13 18:30:57 +01:00
var aspHasher = new PasswordHasher < BackOfficeIdentityUser > ( ) ;
2021-02-09 10:22:42 +01:00
var emptyPasswordValue = Cms . Core . Constants . Security . EmptyPasswordPrefix +
2020-05-13 18:30:57 +01:00
aspHasher . HashPassword ( user , Guid . NewGuid ( ) . ToString ( "N" ) ) ;
var userEntity = new User ( _globalSettings , user . Name , user . Email , user . UserName , emptyPasswordValue )
{
Language = user . Culture ? ? _globalSettings . DefaultUILanguage ,
StartContentIds = user . StartContentIds ? ? new int [ ] { } ,
StartMediaIds = user . StartMediaIds ? ? new int [ ] { } ,
IsLockedOut = user . IsLockedOut ,
} ;
2020-10-23 10:10:02 +11:00
// we have to remember whether Logins property is dirty, since the UpdateMemberProperties will reset it.
var isLoginsPropertyDirty = user . IsPropertyDirty ( nameof ( BackOfficeIdentityUser . Logins ) ) ;
2021-03-11 19:35:43 +11:00
var isTokensPropertyDirty = user . IsPropertyDirty ( nameof ( BackOfficeIdentityUser . LoginTokens ) ) ;
2020-05-13 18:30:57 +01:00
2020-10-23 10:10:02 +11:00
UpdateMemberProperties ( userEntity , user ) ;
2020-05-13 18:30:57 +01:00
_userService . Save ( userEntity ) ;
2020-12-04 12:44:27 +11:00
if ( ! userEntity . HasIdentity )
{
throw new DataException ( "Could not create the user, check logs for details" ) ;
}
2020-05-13 18:30:57 +01:00
2020-12-04 12:44:27 +11:00
// re-assign id
user . Id = UserIdToString ( userEntity . Id ) ;
2020-05-13 18:30:57 +01:00
2020-10-23 10:10:02 +11:00
if ( isLoginsPropertyDirty )
{
_externalLoginService . Save (
2020-12-04 12:44:27 +11:00
userEntity . Id ,
2020-10-23 10:10:02 +11:00
user . Logins . Select ( x = > new ExternalLogin (
x . LoginProvider ,
x . ProviderKey ,
2020-10-23 14:18:53 +11:00
x . UserData ) ) ) ;
2020-10-23 10:10:02 +11:00
}
2021-03-11 19:35:43 +11:00
if ( isTokensPropertyDirty )
{
_externalLoginService . Save (
userEntity . Id ,
user . LoginTokens . Select ( x = > new ExternalLoginToken (
x . LoginProvider ,
x . Name ,
x . Value ) ) ) ;
}
2020-05-13 18:30:57 +01:00
return Task . FromResult ( IdentityResult . Success ) ;
}
2020-12-04 12:44:27 +11:00
/// <inheritdoc />
public override Task < IdentityResult > UpdateAsync ( BackOfficeIdentityUser user , CancellationToken cancellationToken = default )
2020-05-13 18:30:57 +01:00
{
cancellationToken . ThrowIfCancellationRequested ( ) ;
ThrowIfDisposed ( ) ;
2020-12-04 12:44:27 +11:00
if ( user = = null )
{
throw new ArgumentNullException ( nameof ( user ) ) ;
}
2020-05-13 18:30:57 +01:00
2021-09-20 11:22:51 +02:00
if ( ! int . TryParse ( user . Id , NumberStyles . Integer , CultureInfo . InvariantCulture , out var asInt ) )
2020-10-23 10:10:02 +11:00
{
throw new InvalidOperationException ( "The user id must be an integer to work with the Umbraco" ) ;
}
2020-12-04 12:44:27 +11:00
using ( IScope scope = _scopeProvider . CreateScope ( ) )
2020-05-13 18:30:57 +01:00
{
2021-09-20 11:22:51 +02:00
IUser found = _userService . GetUserById ( asInt ) ;
2020-12-01 18:14:37 +11:00
if ( found ! = null )
2020-05-13 18:30:57 +01:00
{
2020-12-01 18:14:37 +11:00
// we have to remember whether Logins property is dirty, since the UpdateMemberProperties will reset it.
var isLoginsPropertyDirty = user . IsPropertyDirty ( nameof ( BackOfficeIdentityUser . Logins ) ) ;
2021-03-11 19:35:43 +11:00
var isTokensPropertyDirty = user . IsPropertyDirty ( nameof ( BackOfficeIdentityUser . LoginTokens ) ) ;
2020-05-13 18:30:57 +01:00
2020-12-01 18:14:37 +11:00
if ( UpdateMemberProperties ( found , user ) )
{
_userService . Save ( found ) ;
}
if ( isLoginsPropertyDirty )
{
_externalLoginService . Save (
found . Id ,
user . Logins . Select ( x = > new ExternalLogin (
x . LoginProvider ,
x . ProviderKey ,
x . UserData ) ) ) ;
}
2021-03-11 19:35:43 +11:00
if ( isTokensPropertyDirty )
{
_externalLoginService . Save (
found . Id ,
user . LoginTokens . Select ( x = > new ExternalLoginToken (
x . LoginProvider ,
x . Name ,
x . Value ) ) ) ;
}
2020-05-13 18:30:57 +01:00
}
2020-12-01 18:14:37 +11:00
scope . Complete ( ) ;
2020-05-13 18:30:57 +01:00
}
2020-12-01 17:24:23 +11:00
return Task . FromResult ( IdentityResult . Success ) ;
2020-05-13 18:30:57 +01:00
}
2020-12-04 12:44:27 +11:00
/// <inheritdoc />
public override Task < IdentityResult > DeleteAsync ( BackOfficeIdentityUser user , CancellationToken cancellationToken = default )
2020-05-13 18:30:57 +01:00
{
cancellationToken . ThrowIfCancellationRequested ( ) ;
ThrowIfDisposed ( ) ;
2020-12-04 12:44:27 +11:00
if ( user = = null )
{
throw new ArgumentNullException ( nameof ( user ) ) ;
}
2020-05-13 18:30:57 +01:00
2020-12-04 12:44:27 +11:00
IUser found = _userService . GetUserById ( UserIdToInt ( user . Id ) ) ;
2020-05-13 18:30:57 +01:00
if ( found ! = null )
{
_userService . Delete ( found ) ;
}
2020-12-04 12:44:27 +11:00
_externalLoginService . DeleteUserLogins ( UserIdToInt ( user . Id ) ) ;
2020-05-13 18:30:57 +01:00
return Task . FromResult ( IdentityResult . Success ) ;
}
2020-12-04 12:44:27 +11:00
/// <inheritdoc />
protected override Task < BackOfficeIdentityUser > FindUserAsync ( string userId , CancellationToken cancellationToken )
2020-05-13 18:30:57 +01:00
{
cancellationToken . ThrowIfCancellationRequested ( ) ;
ThrowIfDisposed ( ) ;
2020-12-04 12:44:27 +11:00
IUser user = _userService . GetUserById ( UserIdToInt ( userId ) ) ;
if ( user = = null )
{
2020-12-04 15:05:47 +11:00
return Task . FromResult ( ( BackOfficeIdentityUser ) null ) ;
2020-12-04 12:44:27 +11:00
}
2020-05-13 18:30:57 +01:00
2020-12-04 12:44:27 +11:00
return Task . FromResult ( AssignLoginsCallback ( _mapper . Map < BackOfficeIdentityUser > ( user ) ) ) ;
2020-05-13 18:30:57 +01:00
}
2020-12-04 12:44:27 +11:00
/// <inheritdoc />
2020-12-04 15:05:47 +11:00
public override Task < BackOfficeIdentityUser > FindByNameAsync ( string userName , CancellationToken cancellationToken = default )
2020-05-13 18:30:57 +01:00
{
cancellationToken . ThrowIfCancellationRequested ( ) ;
ThrowIfDisposed ( ) ;
2020-12-04 12:44:27 +11:00
IUser user = _userService . GetByUsername ( userName ) ;
2020-05-13 18:30:57 +01:00
if ( user = = null )
{
2020-12-04 15:05:47 +11:00
return Task . FromResult ( ( BackOfficeIdentityUser ) null ) ;
2020-05-13 18:30:57 +01:00
}
2020-12-04 12:44:27 +11:00
BackOfficeIdentityUser result = AssignLoginsCallback ( _mapper . Map < BackOfficeIdentityUser > ( user ) ) ;
2020-05-13 18:30:57 +01:00
2020-12-04 15:05:47 +11:00
return Task . FromResult ( result ) ;
2020-05-13 18:30:57 +01:00
}
2020-12-04 12:44:27 +11:00
/// <inheritdoc />
public override Task < BackOfficeIdentityUser > FindByEmailAsync ( string email , CancellationToken cancellationToken = default )
2020-05-13 18:30:57 +01:00
{
cancellationToken . ThrowIfCancellationRequested ( ) ;
ThrowIfDisposed ( ) ;
2020-12-04 12:44:27 +11:00
IUser user = _userService . GetByEmail ( email ) ;
BackOfficeIdentityUser result = user = = null
2020-05-13 18:30:57 +01:00
? null
: _mapper . Map < BackOfficeIdentityUser > ( user ) ;
return Task . FromResult ( AssignLoginsCallback ( result ) ) ;
}
2020-12-04 12:44:27 +11:00
/// <inheritdoc />
Security stamp implementation for members (#10140)
* Getting new netcore PublicAccessChecker in place
* Adds full test coverage for PublicAccessChecker
* remove PublicAccessComposer
* adjust namespaces, ensure RoleManager works, separate public access controller, reduce content controller
* Implements the required methods on IMemberManager, removes old migrated code
* Updates routing to be able to re-route, Fixes middleware ordering ensuring endpoints are last, refactors pipeline options, adds public access middleware, ensures public access follows all hops
* adds note
* adds note
* Cleans up ext methods, ensures that members identity is added on both front-end and back ends. updates how UmbracoApplicationBuilder works in that it explicitly starts endpoints at the time of calling.
* Changes name to IUmbracoEndpointBuilder
* adds note
* Fixing tests, fixing error describers so there's 2x one for back office, one for members, fixes TryConvertTo, fixes login redirect
* fixing build
* Updates user manager to correctly validate password hashing and injects the IBackOfficeUserPasswordChecker
* Merges PR
* Fixes up build and notes
* Implements security stamp and email confirmed for members, cleans up a bunch of repo/service level member groups stuff, shares user store code between members and users and fixes the user identity object so we arent' tracking both groups and roles.
* Security stamp for members is now working
* Fixes keepalive, fixes PublicAccessMiddleware to not throw, updates startup code to be more clear and removes magic that registers middleware.
* adds note
* removes unused filter, fixes build
* fixes WebPath and tests
* Looks up entities in one query
* remove usings
* Fix test, remove stylesheet
* Set status code before we write to response to avoid error
* Ensures that users and members are validated when logging in. Shares more code between users and members.
* merge changes
* oops
* Fixes RepositoryCacheKeys to ensure the keys are normalized
* oops didn't mean to commit this
* Fix casing issues with caching, stop boxing value types for all cache operations, stop re-creating string keys in DefaultRepositoryCachePolicy
* oops didn't mean to comit this
* bah, far out this keeps getting recommitted. sorry
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2021-04-20 17:13:40 +10:00
public override async Task SetPasswordHashAsync ( BackOfficeIdentityUser user , string passwordHash , CancellationToken cancellationToken = default )
{
await base . SetPasswordHashAsync ( user , passwordHash , cancellationToken ) ;
2020-05-13 18:30:57 +01:00
Security stamp implementation for members (#10140)
* Getting new netcore PublicAccessChecker in place
* Adds full test coverage for PublicAccessChecker
* remove PublicAccessComposer
* adjust namespaces, ensure RoleManager works, separate public access controller, reduce content controller
* Implements the required methods on IMemberManager, removes old migrated code
* Updates routing to be able to re-route, Fixes middleware ordering ensuring endpoints are last, refactors pipeline options, adds public access middleware, ensures public access follows all hops
* adds note
* adds note
* Cleans up ext methods, ensures that members identity is added on both front-end and back ends. updates how UmbracoApplicationBuilder works in that it explicitly starts endpoints at the time of calling.
* Changes name to IUmbracoEndpointBuilder
* adds note
* Fixing tests, fixing error describers so there's 2x one for back office, one for members, fixes TryConvertTo, fixes login redirect
* fixing build
* Updates user manager to correctly validate password hashing and injects the IBackOfficeUserPasswordChecker
* Merges PR
* Fixes up build and notes
* Implements security stamp and email confirmed for members, cleans up a bunch of repo/service level member groups stuff, shares user store code between members and users and fixes the user identity object so we arent' tracking both groups and roles.
* Security stamp for members is now working
* Fixes keepalive, fixes PublicAccessMiddleware to not throw, updates startup code to be more clear and removes magic that registers middleware.
* adds note
* removes unused filter, fixes build
* fixes WebPath and tests
* Looks up entities in one query
* remove usings
* Fix test, remove stylesheet
* Set status code before we write to response to avoid error
* Ensures that users and members are validated when logging in. Shares more code between users and members.
* merge changes
* oops
* Fixes RepositoryCacheKeys to ensure the keys are normalized
* oops didn't mean to commit this
* Fix casing issues with caching, stop boxing value types for all cache operations, stop re-creating string keys in DefaultRepositoryCachePolicy
* oops didn't mean to comit this
* bah, far out this keeps getting recommitted. sorry
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2021-04-20 17:13:40 +10:00
// Clear this so that it's reset at the repository level
user . PasswordConfig = null ;
}
2020-05-13 18:30:57 +01:00
2020-12-04 12:44:27 +11:00
/// <inheritdoc />
public override Task AddLoginAsync ( BackOfficeIdentityUser user , UserLoginInfo login , CancellationToken cancellationToken = default )
2020-05-13 18:30:57 +01:00
{
cancellationToken . ThrowIfCancellationRequested ( ) ;
ThrowIfDisposed ( ) ;
2020-12-04 12:44:27 +11:00
if ( user = = null )
{
throw new ArgumentNullException ( nameof ( user ) ) ;
}
2020-05-13 18:30:57 +01:00
2020-12-04 12:44:27 +11:00
if ( login = = null )
{
throw new ArgumentNullException ( nameof ( login ) ) ;
}
ICollection < IIdentityUserLogin > logins = user . Logins ;
2020-12-04 00:20:48 +11:00
var instance = new IdentityUserLogin ( login . LoginProvider , login . ProviderKey , user . Id . ToString ( ) ) ;
2020-12-04 12:44:27 +11:00
IdentityUserLogin userLogin = instance ;
2020-05-13 18:30:57 +01:00
logins . Add ( userLogin ) ;
return Task . CompletedTask ;
}
2020-12-04 12:44:27 +11:00
/// <inheritdoc />
public override Task RemoveLoginAsync ( BackOfficeIdentityUser user , string loginProvider , string providerKey , CancellationToken cancellationToken = default )
2020-05-13 18:30:57 +01:00
{
cancellationToken . ThrowIfCancellationRequested ( ) ;
ThrowIfDisposed ( ) ;
2020-12-04 12:44:27 +11:00
if ( user = = null )
{
throw new ArgumentNullException ( nameof ( user ) ) ;
}
2020-05-13 18:30:57 +01:00
2020-12-04 12:44:27 +11:00
IIdentityUserLogin userLogin = user . Logins . SingleOrDefault ( l = > l . LoginProvider = = loginProvider & & l . ProviderKey = = providerKey ) ;
if ( userLogin ! = null )
{
user . Logins . Remove ( userLogin ) ;
}
2020-05-13 18:30:57 +01:00
return Task . CompletedTask ;
}
2020-12-04 12:44:27 +11:00
/// <inheritdoc />
public override Task < IList < UserLoginInfo > > GetLoginsAsync ( BackOfficeIdentityUser user , CancellationToken cancellationToken = default )
2020-05-13 18:30:57 +01:00
{
cancellationToken . ThrowIfCancellationRequested ( ) ;
ThrowIfDisposed ( ) ;
2020-12-04 12:44:27 +11:00
if ( user = = null )
{
throw new ArgumentNullException ( nameof ( user ) ) ;
}
return Task . FromResult ( ( IList < UserLoginInfo > ) user . Logins . Select ( l = > new UserLoginInfo ( l . LoginProvider , l . ProviderKey , l . LoginProvider ) ) . ToList ( ) ) ;
2020-05-13 18:30:57 +01:00
}
2020-12-04 12:44:27 +11:00
/// <inheritdoc />
protected override async Task < IdentityUserLogin < string > > FindUserLoginAsync ( string userId , string loginProvider , string providerKey , CancellationToken cancellationToken )
2020-05-13 18:30:57 +01:00
{
cancellationToken . ThrowIfCancellationRequested ( ) ;
ThrowIfDisposed ( ) ;
2020-12-04 12:44:27 +11:00
BackOfficeIdentityUser user = await FindUserAsync ( userId , cancellationToken ) ;
if ( user = = null )
2020-05-13 18:30:57 +01:00
{
2020-12-04 12:44:27 +11:00
return null ;
}
2020-05-13 18:30:57 +01:00
2020-12-04 12:44:27 +11:00
IList < UserLoginInfo > logins = await GetLoginsAsync ( user , cancellationToken ) ;
UserLoginInfo found = logins . FirstOrDefault ( x = > x . ProviderKey = = providerKey & & x . LoginProvider = = loginProvider ) ;
if ( found = = null )
{
return null ;
2020-05-13 18:30:57 +01:00
}
2020-12-04 12:44:27 +11:00
return new IdentityUserLogin < string >
{
LoginProvider = found . LoginProvider ,
ProviderKey = found . ProviderKey ,
ProviderDisplayName = found . ProviderDisplayName , // TODO: We don't store this value so it will be null
UserId = user . Id
} ;
2020-05-13 18:30:57 +01:00
}
2020-12-04 12:44:27 +11:00
/// <inheritdoc />
protected override Task < IdentityUserLogin < string > > FindUserLoginAsync ( string loginProvider , string providerKey , CancellationToken cancellationToken )
{
cancellationToken . ThrowIfCancellationRequested ( ) ;
ThrowIfDisposed ( ) ;
var logins = _externalLoginService . Find ( loginProvider , providerKey ) . ToList ( ) ;
if ( logins . Count = = 0 )
{
return Task . FromResult ( ( IdentityUserLogin < string > ) null ) ;
}
IIdentityUserLogin found = logins [ 0 ] ;
return Task . FromResult ( new IdentityUserLogin < string >
{
LoginProvider = found . LoginProvider ,
ProviderKey = found . ProviderKey ,
ProviderDisplayName = null , // TODO: We don't store this value so it will be null
UserId = found . UserId
} ) ;
}
2020-05-13 18:30:57 +01:00
/// <summary>
/// Lists all users of a given role.
/// </summary>
/// <remarks>
/// Identity Role names are equal to Umbraco UserGroup alias.
/// </remarks>
2020-12-04 12:44:27 +11:00
public override Task < IList < BackOfficeIdentityUser > > GetUsersInRoleAsync ( string normalizedRoleName , CancellationToken cancellationToken = default )
2020-05-13 18:30:57 +01:00
{
cancellationToken . ThrowIfCancellationRequested ( ) ;
ThrowIfDisposed ( ) ;
2020-12-04 12:44:27 +11:00
if ( normalizedRoleName = = null )
{
throw new ArgumentNullException ( nameof ( normalizedRoleName ) ) ;
}
2020-05-13 18:30:57 +01:00
2020-12-04 12:44:27 +11:00
IUserGroup userGroup = _userService . GetUserGroupByAlias ( normalizedRoleName ) ;
2020-05-13 18:30:57 +01:00
2020-12-04 12:44:27 +11:00
IEnumerable < IUser > users = _userService . GetAllInGroup ( userGroup . Id ) ;
2020-05-13 18:30:57 +01:00
IList < BackOfficeIdentityUser > backOfficeIdentityUsers = users . Select ( x = > _mapper . Map < BackOfficeIdentityUser > ( x ) ) . ToList ( ) ;
return Task . FromResult ( backOfficeIdentityUsers ) ;
}
2020-12-04 12:44:27 +11:00
/// <inheritdoc/>
protected override Task < IdentityRole < string > > FindRoleAsync ( string normalizedRoleName , CancellationToken cancellationToken )
2020-05-13 18:30:57 +01:00
{
2020-12-04 12:44:27 +11:00
IUserGroup group = _userService . GetUserGroupByAlias ( normalizedRoleName ) ;
if ( group = = null )
{
return Task . FromResult ( ( IdentityRole < string > ) null ) ;
}
2020-05-13 18:30:57 +01:00
2020-12-04 12:44:27 +11:00
return Task . FromResult ( new IdentityRole < string > ( group . Name )
{
Id = group . Alias
} ) ;
2020-05-13 18:30:57 +01:00
}
2020-12-04 12:44:27 +11:00
/// <inheritdoc/>
protected override async Task < IdentityUserRole < string > > FindUserRoleAsync ( string userId , string roleId , CancellationToken cancellationToken )
{
BackOfficeIdentityUser user = await FindUserAsync ( userId , cancellationToken ) ;
if ( user = = null )
{
return null ;
}
IdentityUserRole < string > found = user . Roles . FirstOrDefault ( x = > x . RoleId . InvariantEquals ( roleId ) ) ;
return found ;
}
2020-05-13 18:30:57 +01:00
private BackOfficeIdentityUser AssignLoginsCallback ( BackOfficeIdentityUser user )
{
if ( user ! = null )
{
2021-03-11 19:35:43 +11:00
var userId = UserIdToInt ( user . Id ) ;
user . SetLoginsCallback ( new Lazy < IEnumerable < IIdentityUserLogin > > ( ( ) = > _externalLoginService . GetExternalLogins ( userId ) ) ) ;
user . SetTokensCallback ( new Lazy < IEnumerable < IIdentityUserToken > > ( ( ) = > _externalLoginService . GetExternalLoginTokens ( userId ) ) ) ;
2020-05-13 18:30:57 +01:00
}
2020-12-04 12:44:27 +11:00
return user ;
2020-05-13 18:30:57 +01:00
}
private bool UpdateMemberProperties ( IUser user , BackOfficeIdentityUser identityUser )
{
var anythingChanged = false ;
2020-12-04 12:44:27 +11:00
// don't assign anything if nothing has changed as this will trigger the track changes of the model
2020-10-23 10:10:02 +11:00
if ( identityUser . IsPropertyDirty ( nameof ( BackOfficeIdentityUser . LastLoginDateUtc ) )
2020-12-04 12:44:27 +11:00
| | ( user . LastLoginDate ! = default & & identityUser . LastLoginDateUtc . HasValue = = false )
| | ( identityUser . LastLoginDateUtc . HasValue & & user . LastLoginDate . ToUniversalTime ( ) ! = identityUser . LastLoginDateUtc . Value ) )
2020-05-13 18:30:57 +01:00
{
anythingChanged = true ;
2020-12-04 12:44:27 +11:00
// 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 ( ) ;
2020-05-13 18:30:57 +01:00
user . LastLoginDate = dt ;
}
2020-12-04 12:44:27 +11:00
2021-07-26 11:51:54 -06:00
if ( identityUser . IsPropertyDirty ( nameof ( BackOfficeIdentityUser . InviteDateUtc ) )
| | ( user . InvitedDate ? . ToUniversalTime ( ) ! = identityUser . InviteDateUtc ) )
{
2021-09-20 11:22:51 +02:00
anythingChanged = true ;
2021-07-26 11:51:54 -06:00
user . InvitedDate = identityUser . InviteDateUtc ? . ToLocalTime ( ) ;
}
2020-10-23 10:10:02 +11:00
if ( identityUser . IsPropertyDirty ( nameof ( BackOfficeIdentityUser . LastPasswordChangeDateUtc ) )
2020-12-04 12:44:27 +11:00
| | ( user . LastPasswordChangeDate ! = default & & identityUser . LastPasswordChangeDateUtc . HasValue = = false )
| | ( identityUser . LastPasswordChangeDateUtc . HasValue & & user . LastPasswordChangeDate . ToUniversalTime ( ) ! = identityUser . LastPasswordChangeDateUtc . Value ) )
2020-05-13 18:30:57 +01:00
{
anythingChanged = true ;
user . LastPasswordChangeDate = identityUser . LastPasswordChangeDateUtc . Value . ToLocalTime ( ) ;
}
2020-12-04 12:44:27 +11:00
2020-10-23 10:10:02 +11:00
if ( identityUser . IsPropertyDirty ( nameof ( BackOfficeIdentityUser . EmailConfirmed ) )
2020-12-04 12:44:27 +11:00
| | ( user . EmailConfirmedDate . HasValue & & user . EmailConfirmedDate . Value ! = default & & identityUser . EmailConfirmed = = false )
| | ( ( user . EmailConfirmedDate . HasValue = = false | | user . EmailConfirmedDate . Value = = default ) & & identityUser . EmailConfirmed ) )
2020-05-13 18:30:57 +01:00
{
anythingChanged = true ;
user . EmailConfirmedDate = identityUser . EmailConfirmed ? ( DateTime ? ) DateTime . Now : null ;
}
2020-12-04 12:44:27 +11:00
2020-10-23 10:10:02 +11:00
if ( identityUser . IsPropertyDirty ( nameof ( BackOfficeIdentityUser . Name ) )
2020-05-13 18:30:57 +01:00
& & user . Name ! = identityUser . Name & & identityUser . Name . IsNullOrWhiteSpace ( ) = = false )
{
anythingChanged = true ;
user . Name = identityUser . Name ;
}
2020-12-04 12:44:27 +11:00
2020-10-23 10:10:02 +11:00
if ( identityUser . IsPropertyDirty ( nameof ( BackOfficeIdentityUser . Email ) )
2020-05-13 18:30:57 +01:00
& & user . Email ! = identityUser . Email & & identityUser . Email . IsNullOrWhiteSpace ( ) = = false )
{
anythingChanged = true ;
user . Email = identityUser . Email ;
}
2020-12-04 12:44:27 +11:00
2020-10-23 10:10:02 +11:00
if ( identityUser . IsPropertyDirty ( nameof ( BackOfficeIdentityUser . AccessFailedCount ) )
2020-05-13 18:30:57 +01:00
& & user . FailedPasswordAttempts ! = identityUser . AccessFailedCount )
{
anythingChanged = true ;
user . FailedPasswordAttempts = identityUser . AccessFailedCount ;
}
2020-12-04 12:44:27 +11:00
2021-09-21 08:56:10 -06:00
if ( user . IsApproved ! = identityUser . IsApproved )
{
anythingChanged = true ;
user . IsApproved = identityUser . IsApproved ;
}
2020-05-13 18:30:57 +01:00
if ( user . IsLockedOut ! = identityUser . IsLockedOut )
{
anythingChanged = true ;
user . IsLockedOut = identityUser . IsLockedOut ;
if ( user . IsLockedOut )
{
2020-12-04 12:44:27 +11:00
// need to set the last lockout date
2020-05-13 18:30:57 +01:00
user . LastLockoutDate = DateTime . Now ;
}
}
2020-12-04 12:44:27 +11:00
2020-10-23 10:10:02 +11:00
if ( identityUser . IsPropertyDirty ( nameof ( BackOfficeIdentityUser . UserName ) )
2020-05-13 18:30:57 +01:00
& & user . Username ! = identityUser . UserName & & identityUser . UserName . IsNullOrWhiteSpace ( ) = = false )
{
anythingChanged = true ;
user . Username = identityUser . UserName ;
}
2020-12-04 12:44:27 +11:00
2020-10-23 10:10:02 +11:00
if ( identityUser . IsPropertyDirty ( nameof ( BackOfficeIdentityUser . PasswordHash ) )
2020-05-13 18:30:57 +01:00
& & user . RawPasswordValue ! = identityUser . PasswordHash & & identityUser . PasswordHash . IsNullOrWhiteSpace ( ) = = false )
{
anythingChanged = true ;
user . RawPasswordValue = identityUser . PasswordHash ;
2020-05-27 13:48:26 +10:00
user . PasswordConfiguration = identityUser . PasswordConfig ;
2020-05-13 18:30:57 +01:00
}
2020-10-23 10:10:02 +11:00
if ( identityUser . IsPropertyDirty ( nameof ( BackOfficeIdentityUser . Culture ) )
2020-05-13 18:30:57 +01:00
& & user . Language ! = identityUser . Culture & & identityUser . Culture . IsNullOrWhiteSpace ( ) = = false )
{
anythingChanged = true ;
user . Language = identityUser . Culture ;
}
2020-12-04 12:44:27 +11:00
2020-10-23 10:10:02 +11:00
if ( identityUser . IsPropertyDirty ( nameof ( BackOfficeIdentityUser . StartMediaIds ) )
2020-05-13 18:30:57 +01:00
& & user . StartMediaIds . UnsortedSequenceEqual ( identityUser . StartMediaIds ) = = false )
{
anythingChanged = true ;
user . StartMediaIds = identityUser . StartMediaIds ;
}
2020-12-04 12:44:27 +11:00
2020-10-23 10:10:02 +11:00
if ( identityUser . IsPropertyDirty ( nameof ( BackOfficeIdentityUser . StartContentIds ) )
2020-05-13 18:30:57 +01:00
& & user . StartContentIds . UnsortedSequenceEqual ( identityUser . StartContentIds ) = = false )
{
anythingChanged = true ;
user . StartContentIds = identityUser . StartContentIds ;
}
2020-12-04 12:44:27 +11:00
2020-05-13 18:30:57 +01:00
if ( user . SecurityStamp ! = identityUser . SecurityStamp )
{
anythingChanged = true ;
user . SecurityStamp = identityUser . SecurityStamp ;
}
Security stamp implementation for members (#10140)
* Getting new netcore PublicAccessChecker in place
* Adds full test coverage for PublicAccessChecker
* remove PublicAccessComposer
* adjust namespaces, ensure RoleManager works, separate public access controller, reduce content controller
* Implements the required methods on IMemberManager, removes old migrated code
* Updates routing to be able to re-route, Fixes middleware ordering ensuring endpoints are last, refactors pipeline options, adds public access middleware, ensures public access follows all hops
* adds note
* adds note
* Cleans up ext methods, ensures that members identity is added on both front-end and back ends. updates how UmbracoApplicationBuilder works in that it explicitly starts endpoints at the time of calling.
* Changes name to IUmbracoEndpointBuilder
* adds note
* Fixing tests, fixing error describers so there's 2x one for back office, one for members, fixes TryConvertTo, fixes login redirect
* fixing build
* Updates user manager to correctly validate password hashing and injects the IBackOfficeUserPasswordChecker
* Merges PR
* Fixes up build and notes
* Implements security stamp and email confirmed for members, cleans up a bunch of repo/service level member groups stuff, shares user store code between members and users and fixes the user identity object so we arent' tracking both groups and roles.
* Security stamp for members is now working
* Fixes keepalive, fixes PublicAccessMiddleware to not throw, updates startup code to be more clear and removes magic that registers middleware.
* adds note
* removes unused filter, fixes build
* fixes WebPath and tests
* Looks up entities in one query
* remove usings
* Fix test, remove stylesheet
* Set status code before we write to response to avoid error
* Ensures that users and members are validated when logging in. Shares more code between users and members.
* merge changes
* oops
* Fixes RepositoryCacheKeys to ensure the keys are normalized
* oops didn't mean to commit this
* Fix casing issues with caching, stop boxing value types for all cache operations, stop re-creating string keys in DefaultRepositoryCachePolicy
* oops didn't mean to comit this
* bah, far out this keeps getting recommitted. sorry
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2021-04-20 17:13:40 +10:00
if ( identityUser . IsPropertyDirty ( nameof ( BackOfficeIdentityUser . Roles ) ) )
2020-05-13 18:30:57 +01:00
{
var identityUserRoles = identityUser . Roles . Select ( x = > x . RoleId ) . ToArray ( ) ;
Security stamp implementation for members (#10140)
* Getting new netcore PublicAccessChecker in place
* Adds full test coverage for PublicAccessChecker
* remove PublicAccessComposer
* adjust namespaces, ensure RoleManager works, separate public access controller, reduce content controller
* Implements the required methods on IMemberManager, removes old migrated code
* Updates routing to be able to re-route, Fixes middleware ordering ensuring endpoints are last, refactors pipeline options, adds public access middleware, ensures public access follows all hops
* adds note
* adds note
* Cleans up ext methods, ensures that members identity is added on both front-end and back ends. updates how UmbracoApplicationBuilder works in that it explicitly starts endpoints at the time of calling.
* Changes name to IUmbracoEndpointBuilder
* adds note
* Fixing tests, fixing error describers so there's 2x one for back office, one for members, fixes TryConvertTo, fixes login redirect
* fixing build
* Updates user manager to correctly validate password hashing and injects the IBackOfficeUserPasswordChecker
* Merges PR
* Fixes up build and notes
* Implements security stamp and email confirmed for members, cleans up a bunch of repo/service level member groups stuff, shares user store code between members and users and fixes the user identity object so we arent' tracking both groups and roles.
* Security stamp for members is now working
* Fixes keepalive, fixes PublicAccessMiddleware to not throw, updates startup code to be more clear and removes magic that registers middleware.
* adds note
* removes unused filter, fixes build
* fixes WebPath and tests
* Looks up entities in one query
* remove usings
* Fix test, remove stylesheet
* Set status code before we write to response to avoid error
* Ensures that users and members are validated when logging in. Shares more code between users and members.
* merge changes
* oops
* Fixes RepositoryCacheKeys to ensure the keys are normalized
* oops didn't mean to commit this
* Fix casing issues with caching, stop boxing value types for all cache operations, stop re-creating string keys in DefaultRepositoryCachePolicy
* oops didn't mean to comit this
* bah, far out this keeps getting recommitted. sorry
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2021-04-20 17:13:40 +10:00
anythingChanged = true ;
2020-05-13 18:30:57 +01:00
Security stamp implementation for members (#10140)
* Getting new netcore PublicAccessChecker in place
* Adds full test coverage for PublicAccessChecker
* remove PublicAccessComposer
* adjust namespaces, ensure RoleManager works, separate public access controller, reduce content controller
* Implements the required methods on IMemberManager, removes old migrated code
* Updates routing to be able to re-route, Fixes middleware ordering ensuring endpoints are last, refactors pipeline options, adds public access middleware, ensures public access follows all hops
* adds note
* adds note
* Cleans up ext methods, ensures that members identity is added on both front-end and back ends. updates how UmbracoApplicationBuilder works in that it explicitly starts endpoints at the time of calling.
* Changes name to IUmbracoEndpointBuilder
* adds note
* Fixing tests, fixing error describers so there's 2x one for back office, one for members, fixes TryConvertTo, fixes login redirect
* fixing build
* Updates user manager to correctly validate password hashing and injects the IBackOfficeUserPasswordChecker
* Merges PR
* Fixes up build and notes
* Implements security stamp and email confirmed for members, cleans up a bunch of repo/service level member groups stuff, shares user store code between members and users and fixes the user identity object so we arent' tracking both groups and roles.
* Security stamp for members is now working
* Fixes keepalive, fixes PublicAccessMiddleware to not throw, updates startup code to be more clear and removes magic that registers middleware.
* adds note
* removes unused filter, fixes build
* fixes WebPath and tests
* Looks up entities in one query
* remove usings
* Fix test, remove stylesheet
* Set status code before we write to response to avoid error
* Ensures that users and members are validated when logging in. Shares more code between users and members.
* merge changes
* oops
* Fixes RepositoryCacheKeys to ensure the keys are normalized
* oops didn't mean to commit this
* Fix casing issues with caching, stop boxing value types for all cache operations, stop re-creating string keys in DefaultRepositoryCachePolicy
* oops didn't mean to comit this
* bah, far out this keeps getting recommitted. sorry
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2021-04-20 17:13:40 +10:00
// clear out the current groups (need to ToArray since we are modifying the iterator)
user . ClearGroups ( ) ;
2020-05-13 18:30:57 +01:00
Security stamp implementation for members (#10140)
* Getting new netcore PublicAccessChecker in place
* Adds full test coverage for PublicAccessChecker
* remove PublicAccessComposer
* adjust namespaces, ensure RoleManager works, separate public access controller, reduce content controller
* Implements the required methods on IMemberManager, removes old migrated code
* Updates routing to be able to re-route, Fixes middleware ordering ensuring endpoints are last, refactors pipeline options, adds public access middleware, ensures public access follows all hops
* adds note
* adds note
* Cleans up ext methods, ensures that members identity is added on both front-end and back ends. updates how UmbracoApplicationBuilder works in that it explicitly starts endpoints at the time of calling.
* Changes name to IUmbracoEndpointBuilder
* adds note
* Fixing tests, fixing error describers so there's 2x one for back office, one for members, fixes TryConvertTo, fixes login redirect
* fixing build
* Updates user manager to correctly validate password hashing and injects the IBackOfficeUserPasswordChecker
* Merges PR
* Fixes up build and notes
* Implements security stamp and email confirmed for members, cleans up a bunch of repo/service level member groups stuff, shares user store code between members and users and fixes the user identity object so we arent' tracking both groups and roles.
* Security stamp for members is now working
* Fixes keepalive, fixes PublicAccessMiddleware to not throw, updates startup code to be more clear and removes magic that registers middleware.
* adds note
* removes unused filter, fixes build
* fixes WebPath and tests
* Looks up entities in one query
* remove usings
* Fix test, remove stylesheet
* Set status code before we write to response to avoid error
* Ensures that users and members are validated when logging in. Shares more code between users and members.
* merge changes
* oops
* Fixes RepositoryCacheKeys to ensure the keys are normalized
* oops didn't mean to commit this
* Fix casing issues with caching, stop boxing value types for all cache operations, stop re-creating string keys in DefaultRepositoryCachePolicy
* oops didn't mean to comit this
* bah, far out this keeps getting recommitted. sorry
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2021-04-20 17:13:40 +10:00
// go lookup all these groups
IReadOnlyUserGroup [ ] groups = _userService . GetUserGroupsByAlias ( identityUserRoles ) . Select ( x = > x . ToReadOnlyGroup ( ) ) . ToArray ( ) ;
2020-05-13 18:30:57 +01:00
Security stamp implementation for members (#10140)
* Getting new netcore PublicAccessChecker in place
* Adds full test coverage for PublicAccessChecker
* remove PublicAccessComposer
* adjust namespaces, ensure RoleManager works, separate public access controller, reduce content controller
* Implements the required methods on IMemberManager, removes old migrated code
* Updates routing to be able to re-route, Fixes middleware ordering ensuring endpoints are last, refactors pipeline options, adds public access middleware, ensures public access follows all hops
* adds note
* adds note
* Cleans up ext methods, ensures that members identity is added on both front-end and back ends. updates how UmbracoApplicationBuilder works in that it explicitly starts endpoints at the time of calling.
* Changes name to IUmbracoEndpointBuilder
* adds note
* Fixing tests, fixing error describers so there's 2x one for back office, one for members, fixes TryConvertTo, fixes login redirect
* fixing build
* Updates user manager to correctly validate password hashing and injects the IBackOfficeUserPasswordChecker
* Merges PR
* Fixes up build and notes
* Implements security stamp and email confirmed for members, cleans up a bunch of repo/service level member groups stuff, shares user store code between members and users and fixes the user identity object so we arent' tracking both groups and roles.
* Security stamp for members is now working
* Fixes keepalive, fixes PublicAccessMiddleware to not throw, updates startup code to be more clear and removes magic that registers middleware.
* adds note
* removes unused filter, fixes build
* fixes WebPath and tests
* Looks up entities in one query
* remove usings
* Fix test, remove stylesheet
* Set status code before we write to response to avoid error
* Ensures that users and members are validated when logging in. Shares more code between users and members.
* merge changes
* oops
* Fixes RepositoryCacheKeys to ensure the keys are normalized
* oops didn't mean to commit this
* Fix casing issues with caching, stop boxing value types for all cache operations, stop re-creating string keys in DefaultRepositoryCachePolicy
* oops didn't mean to comit this
* bah, far out this keeps getting recommitted. sorry
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2021-04-20 17:13:40 +10:00
// use all of the ones assigned and add them
foreach ( IReadOnlyUserGroup group in groups )
{
user . AddGroup ( group ) ;
2020-05-13 18:30:57 +01:00
}
Security stamp implementation for members (#10140)
* Getting new netcore PublicAccessChecker in place
* Adds full test coverage for PublicAccessChecker
* remove PublicAccessComposer
* adjust namespaces, ensure RoleManager works, separate public access controller, reduce content controller
* Implements the required methods on IMemberManager, removes old migrated code
* Updates routing to be able to re-route, Fixes middleware ordering ensuring endpoints are last, refactors pipeline options, adds public access middleware, ensures public access follows all hops
* adds note
* adds note
* Cleans up ext methods, ensures that members identity is added on both front-end and back ends. updates how UmbracoApplicationBuilder works in that it explicitly starts endpoints at the time of calling.
* Changes name to IUmbracoEndpointBuilder
* adds note
* Fixing tests, fixing error describers so there's 2x one for back office, one for members, fixes TryConvertTo, fixes login redirect
* fixing build
* Updates user manager to correctly validate password hashing and injects the IBackOfficeUserPasswordChecker
* Merges PR
* Fixes up build and notes
* Implements security stamp and email confirmed for members, cleans up a bunch of repo/service level member groups stuff, shares user store code between members and users and fixes the user identity object so we arent' tracking both groups and roles.
* Security stamp for members is now working
* Fixes keepalive, fixes PublicAccessMiddleware to not throw, updates startup code to be more clear and removes magic that registers middleware.
* adds note
* removes unused filter, fixes build
* fixes WebPath and tests
* Looks up entities in one query
* remove usings
* Fix test, remove stylesheet
* Set status code before we write to response to avoid error
* Ensures that users and members are validated when logging in. Shares more code between users and members.
* merge changes
* oops
* Fixes RepositoryCacheKeys to ensure the keys are normalized
* oops didn't mean to commit this
* Fix casing issues with caching, stop boxing value types for all cache operations, stop re-creating string keys in DefaultRepositoryCachePolicy
* oops didn't mean to comit this
* bah, far out this keeps getting recommitted. sorry
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
2021-04-20 17:13:40 +10:00
// re-assign
identityUser . SetGroups ( groups ) ;
2020-05-13 18:30:57 +01:00
}
2020-12-04 00:54:28 +11:00
// we should re-set the calculated start nodes
2021-03-05 15:36:27 +01:00
identityUser . CalculatedMediaStartNodeIds = user . CalculateMediaStartNodeIds ( _entityService , _appCaches ) ;
identityUser . CalculatedContentStartNodeIds = user . CalculateContentStartNodeIds ( _entityService , _appCaches ) ;
2020-05-13 18:30:57 +01:00
2020-12-04 00:54:28 +11:00
// reset all changes
2020-05-13 18:30:57 +01:00
identityUser . ResetDirtyProperties ( false ) ;
return anythingChanged ;
}
2020-12-04 12:44:27 +11:00
/// <inheritdoc />
2020-05-13 18:30:57 +01:00
public Task < bool > ValidateSessionIdAsync ( string userId , string sessionId )
{
2020-12-04 01:38:36 +11:00
if ( Guid . TryParse ( sessionId , out Guid guidSessionId ) )
2020-05-13 18:30:57 +01:00
{
return Task . FromResult ( _userService . ValidateLoginSession ( UserIdToInt ( userId ) , guidSessionId ) ) ;
}
return Task . FromResult ( false ) ;
}
2020-12-04 12:44:27 +11:00
/// <summary>
2021-03-11 19:35:43 +11:00
/// Overridden to support Umbraco's own data storage requirements
/// </summary>
/// <remarks>
/// The base class's implementation of this calls into FindTokenAsync and AddUserTokenAsync, both methods will only work with ORMs that are change
/// tracking ORMs like EFCore.
/// </remarks>
/// <inheritdoc />
public override Task SetTokenAsync ( BackOfficeIdentityUser user , string loginProvider , string name , string value , CancellationToken cancellationToken )
{
cancellationToken . ThrowIfCancellationRequested ( ) ;
ThrowIfDisposed ( ) ;
if ( user = = null )
{
throw new ArgumentNullException ( nameof ( user ) ) ;
}
IIdentityUserToken token = user . LoginTokens . FirstOrDefault ( x = > x . LoginProvider . InvariantEquals ( loginProvider ) & & x . Name . InvariantEquals ( name ) ) ;
if ( token = = null )
{
user . LoginTokens . Add ( new IdentityUserToken ( loginProvider , name , value , user . Id ) ) ;
}
else
{
token . Value = value ;
}
return Task . CompletedTask ;
}
/// <summary>
/// Overridden to support Umbraco's own data storage requirements
/// </summary>
/// <remarks>
/// The base class's implementation of this calls into FindTokenAsync, RemoveUserTokenAsync and AddUserTokenAsync, both methods will only work with ORMs that are change
/// tracking ORMs like EFCore.
/// </remarks>
/// <inheritdoc />
public override Task RemoveTokenAsync ( BackOfficeIdentityUser user , string loginProvider , string name , CancellationToken cancellationToken )
{
cancellationToken . ThrowIfCancellationRequested ( ) ;
ThrowIfDisposed ( ) ;
if ( user = = null )
{
throw new ArgumentNullException ( nameof ( user ) ) ;
}
IIdentityUserToken token = user . LoginTokens . FirstOrDefault ( x = > x . LoginProvider . InvariantEquals ( loginProvider ) & & x . Name . InvariantEquals ( name ) ) ;
if ( token ! = null )
{
user . LoginTokens . Remove ( token ) ;
}
return Task . CompletedTask ;
}
/// <summary>
/// Overridden to support Umbraco's own data storage requirements
/// </summary>
/// <remarks>
/// The base class's implementation of this calls into FindTokenAsync, RemoveUserTokenAsync and AddUserTokenAsync, both methods will only work with ORMs that are change
/// tracking ORMs like EFCore.
/// </remarks>
/// <inheritdoc />
public override Task < string > GetTokenAsync ( BackOfficeIdentityUser user , string loginProvider , string name , CancellationToken cancellationToken )
{
cancellationToken . ThrowIfCancellationRequested ( ) ;
ThrowIfDisposed ( ) ;
if ( user = = null )
{
throw new ArgumentNullException ( nameof ( user ) ) ;
}
IIdentityUserToken token = user . LoginTokens . FirstOrDefault ( x = > x . LoginProvider . InvariantEquals ( loginProvider ) & & x . Name . InvariantEquals ( name ) ) ;
return Task . FromResult ( token ? . Value ) ;
}
2020-05-13 18:30:57 +01:00
}
}