|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Shared;
namespace Microsoft.AspNetCore.Identity;
/// <summary>
/// Represents a new instance of a persistence store for the specified user type.
/// </summary>
/// <typeparam name="TUser">The type representing a user.</typeparam>
/// <typeparam name="TKey">The type of the primary key for a user.</typeparam>
/// <typeparam name="TUserClaim">The type representing a claim.</typeparam>
/// <typeparam name="TUserLogin">The type representing a user external login.</typeparam>
/// <typeparam name="TUserToken">The type representing a user token.</typeparam>
public abstract class UserStoreBase<TUser, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TKey, TUserClaim, TUserLogin, TUserToken> :
IUserLoginStore<TUser>,
IUserClaimStore<TUser>,
IUserPasswordStore<TUser>,
IUserSecurityStampStore<TUser>,
IUserEmailStore<TUser>,
IUserLockoutStore<TUser>,
IUserPhoneNumberStore<TUser>,
IQueryableUserStore<TUser>,
IUserTwoFactorStore<TUser>,
IUserAuthenticationTokenStore<TUser>,
IUserAuthenticatorKeyStore<TUser>,
IUserTwoFactorRecoveryCodeStore<TUser>
where TUser : IdentityUser<TKey>
where TKey : IEquatable<TKey>
where TUserClaim : IdentityUserClaim<TKey>, new()
where TUserLogin : IdentityUserLogin<TKey>, new()
where TUserToken : IdentityUserToken<TKey>, new()
{
/// <summary>
/// Creates a new instance.
/// </summary>
/// <param name="describer">The <see cref="IdentityErrorDescriber"/> used to describe store errors.</param>
public UserStoreBase(IdentityErrorDescriber describer)
{
ArgumentNullThrowHelper.ThrowIfNull(describer);
ErrorDescriber = describer;
}
private bool _disposed;
/// <summary>
/// Gets or sets the <see cref="IdentityErrorDescriber"/> for any error that occurred with the current operation.
/// </summary>
public IdentityErrorDescriber ErrorDescriber { get; set; }
/// <summary>
/// Called to create a new instance of a <see cref="IdentityUserClaim{TKey}"/>.
/// </summary>
/// <param name="user">The associated user.</param>
/// <param name="claim">The associated claim.</param>
/// <returns></returns>
protected virtual TUserClaim CreateUserClaim(TUser user, Claim claim)
{
var userClaim = new TUserClaim { UserId = user.Id };
userClaim.InitializeFromClaim(claim);
return userClaim;
}
/// <summary>
/// Called to create a new instance of a <see cref="IdentityUserLogin{TKey}"/>.
/// </summary>
/// <param name="user">The associated user.</param>
/// <param name="login">The associated login.</param>
/// <returns></returns>
protected virtual TUserLogin CreateUserLogin(TUser user, UserLoginInfo login)
{
return new TUserLogin
{
UserId = user.Id,
ProviderKey = login.ProviderKey,
LoginProvider = login.LoginProvider,
ProviderDisplayName = login.ProviderDisplayName
};
}
/// <summary>
/// Called to create a new instance of a <see cref="IdentityUserToken{TKey}"/>.
/// </summary>
/// <param name="user">The associated user.</param>
/// <param name="loginProvider">The associated login provider.</param>
/// <param name="name">The name of the user token.</param>
/// <param name="value">The value of the user token.</param>
/// <returns></returns>
protected virtual TUserToken CreateUserToken(TUser user, string loginProvider, string name, string? value)
{
return new TUserToken
{
UserId = user.Id,
LoginProvider = loginProvider,
Name = name,
Value = value
};
}
/// <summary>
/// Gets the user identifier for the specified <paramref name="user"/>.
/// </summary>
/// <param name="user">The user whose identifier should be retrieved.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the identifier for the specified <paramref name="user"/>.</returns>
public virtual Task<string> GetUserIdAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
return Task.FromResult(ConvertIdToString(user.Id)!);
}
/// <summary>
/// Gets the user name for the specified <paramref name="user"/>.
/// </summary>
/// <param name="user">The user whose name should be retrieved.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the name for the specified <paramref name="user"/>.</returns>
public virtual Task<string?> GetUserNameAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
return Task.FromResult(user.UserName);
}
/// <summary>
/// Sets the given <paramref name="userName" /> for the specified <paramref name="user"/>.
/// </summary>
/// <param name="user">The user whose name should be set.</param>
/// <param name="userName">The user name to set.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public virtual Task SetUserNameAsync(TUser user, string? userName, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
user.UserName = userName;
return Task.CompletedTask;
}
/// <summary>
/// Gets the normalized user name for the specified <paramref name="user"/>.
/// </summary>
/// <param name="user">The user whose normalized name should be retrieved.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the normalized user name for the specified <paramref name="user"/>.</returns>
public virtual Task<string?> GetNormalizedUserNameAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
return Task.FromResult(user.NormalizedUserName);
}
/// <summary>
/// Sets the given normalized name for the specified <paramref name="user"/>.
/// </summary>
/// <param name="user">The user whose name should be set.</param>
/// <param name="normalizedName">The normalized name to set.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public virtual Task SetNormalizedUserNameAsync(TUser user, string? normalizedName, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
user.NormalizedUserName = normalizedName;
return Task.CompletedTask;
}
/// <summary>
/// Creates the specified <paramref name="user"/> in the user store.
/// </summary>
/// <param name="user">The user to create.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> of the creation operation.</returns>
public abstract Task<IdentityResult> CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Updates the specified <paramref name="user"/> in the user store.
/// </summary>
/// <param name="user">The user to update.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> of the update operation.</returns>
public abstract Task<IdentityResult> UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Deletes the specified <paramref name="user"/> from the user store.
/// </summary>
/// <param name="user">The user to delete.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the <see cref="IdentityResult"/> of the update operation.</returns>
public abstract Task<IdentityResult> DeleteAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Finds and returns a user, if any, who has the specified <paramref name="userId"/>.
/// </summary>
/// <param name="userId">The user ID to search for.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>
/// The <see cref="Task"/> that represents the asynchronous operation, containing the user matching the specified <paramref name="userId"/> if it exists.
/// </returns>
public abstract Task<TUser?> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Converts the provided <paramref name="id"/> to a strongly typed key object.
/// </summary>
/// <param name="id">The id to convert.</param>
/// <returns>An instance of <typeparamref name="TKey"/> representing the provided <paramref name="id"/>.</returns>
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code",
Justification = "TKey is annoated with RequiresUnreferencedCodeAttribute.All.")]
public virtual TKey? ConvertIdFromString(string? id)
{
if (id == null)
{
return default(TKey);
}
return (TKey?)TypeDescriptor.GetConverter(typeof(TKey)).ConvertFromInvariantString(id);
}
/// <summary>
/// Converts the provided <paramref name="id"/> to its string representation.
/// </summary>
/// <param name="id">The id to convert.</param>
/// <returns>An <see cref="string"/> representation of the provided <paramref name="id"/>.</returns>
public virtual string? ConvertIdToString(TKey id)
{
if (object.Equals(id, default(TKey)))
{
return null;
}
return id.ToString();
}
/// <summary>
/// Finds and returns a user, if any, who has the specified normalized user name.
/// </summary>
/// <param name="normalizedUserName">The normalized user name to search for.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>
/// The <see cref="Task"/> that represents the asynchronous operation, containing the user matching the specified <paramref name="normalizedUserName"/> if it exists.
/// </returns>
public abstract Task<TUser?> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// A navigation property for the users the store contains.
/// </summary>
public abstract IQueryable<TUser> Users
{
get;
}
/// <summary>
/// Sets the password hash for a user.
/// </summary>
/// <param name="user">The user to set the password hash for.</param>
/// <param name="passwordHash">The password hash to set.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public virtual Task SetPasswordHashAsync(TUser user, string? passwordHash, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
user.PasswordHash = passwordHash;
return Task.CompletedTask;
}
/// <summary>
/// Gets the password hash for a user.
/// </summary>
/// <param name="user">The user to retrieve the password hash for.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>A <see cref="Task{TResult}"/> that contains the password hash for the user.</returns>
public virtual Task<string?> GetPasswordHashAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
return Task.FromResult(user.PasswordHash);
}
/// <summary>
/// Returns a flag indicating if the specified user has a password.
/// </summary>
/// <param name="user">The user to retrieve the password hash for.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>A <see cref="Task{TResult}"/> containing a flag indicating if the specified user has a password. If the
/// user has a password the returned value with be true, otherwise it will be false.</returns>
public virtual Task<bool> HasPasswordAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult(user.PasswordHash != null);
}
/// <summary>
/// Return a user with the matching userId if it exists.
/// </summary>
/// <param name="userId">The user's id.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The user if it exists.</returns>
protected abstract Task<TUser?> FindUserAsync(TKey userId, CancellationToken cancellationToken);
/// <summary>
/// Return a user login with the matching userId, provider, providerKey if it exists.
/// </summary>
/// <param name="userId">The user's id.</param>
/// <param name="loginProvider">The login provider name.</param>
/// <param name="providerKey">The key provided by the <paramref name="loginProvider"/> to identify a user.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The user login if it exists.</returns>
protected abstract Task<TUserLogin?> FindUserLoginAsync(TKey userId, string loginProvider, string providerKey, CancellationToken cancellationToken);
/// <summary>
/// Return a user login with provider, providerKey if it exists.
/// </summary>
/// <param name="loginProvider">The login provider name.</param>
/// <param name="providerKey">The key provided by the <paramref name="loginProvider"/> to identify a user.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The user login if it exists.</returns>
protected abstract Task<TUserLogin?> FindUserLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken);
/// <summary>
/// Throws if this class has been disposed.
/// </summary>
protected void ThrowIfDisposed()
{
ObjectDisposedThrowHelper.ThrowIf(_disposed, this);
}
/// <summary>
/// Dispose the store
/// </summary>
public void Dispose()
{
_disposed = true;
}
/// <summary>
/// Get the claims associated with the specified <paramref name="user"/> as an asynchronous operation.
/// </summary>
/// <param name="user">The user whose claims should be retrieved.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>A <see cref="Task{TResult}"/> that contains the claims granted to a user.</returns>
public abstract Task<IList<Claim>> GetClaimsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Adds the <paramref name="claims"/> given to the specified <paramref name="user"/>.
/// </summary>
/// <param name="user">The user to add the claim to.</param>
/// <param name="claims">The claim to add to the user.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public abstract Task AddClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Replaces the <paramref name="claim"/> on the specified <paramref name="user"/>, with the <paramref name="newClaim"/>.
/// </summary>
/// <param name="user">The user to replace the claim on.</param>
/// <param name="claim">The claim replace.</param>
/// <param name="newClaim">The new claim replacing the <paramref name="claim"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public abstract Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Removes the <paramref name="claims"/> given from the specified <paramref name="user"/>.
/// </summary>
/// <param name="user">The user to remove the claims from.</param>
/// <param name="claims">The claim to remove.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public abstract Task RemoveClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Adds the <paramref name="login"/> given to the specified <paramref name="user"/>.
/// </summary>
/// <param name="user">The user to add the login to.</param>
/// <param name="login">The login to add to the user.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public abstract Task AddLoginAsync(TUser user, UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Removes the <paramref name="loginProvider"/> given from the specified <paramref name="user"/>.
/// </summary>
/// <param name="user">The user to remove the login from.</param>
/// <param name="loginProvider">The login to remove from the user.</param>
/// <param name="providerKey">The key provided by the <paramref name="loginProvider"/> to identify a user.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public abstract Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Retrieves the associated logins for the specified <paramref name="user"/>.
/// </summary>
/// <param name="user">The user whose associated logins to retrieve.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>
/// The <see cref="Task"/> for the asynchronous operation, containing a list of <see cref="UserLoginInfo"/> for the specified <paramref name="user"/>, if any.
/// </returns>
public abstract Task<IList<UserLoginInfo>> GetLoginsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Retrieves the user associated with the specified login provider and login provider key..
/// </summary>
/// <param name="loginProvider">The login provider who provided the <paramref name="providerKey"/>.</param>
/// <param name="providerKey">The key provided by the <paramref name="loginProvider"/> to identify a user.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>
/// The <see cref="Task"/> for the asynchronous operation, containing the user, if any which matched the specified login provider and key.
/// </returns>
public virtual async Task<TUser?> FindByLoginAsync(string loginProvider, string providerKey,
CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
var userLogin = await FindUserLoginAsync(loginProvider, providerKey, cancellationToken).ConfigureAwait(false);
if (userLogin != null)
{
return await FindUserAsync(userLogin.UserId, cancellationToken).ConfigureAwait(false);
}
return null;
}
/// <summary>
/// Gets a flag indicating whether the email address for the specified <paramref name="user"/> has been verified, true if the email address is verified otherwise
/// false.
/// </summary>
/// <param name="user">The user whose email confirmation status should be returned.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>
/// The task object containing the results of the asynchronous operation, a flag indicating whether the email address for the specified <paramref name="user"/>
/// has been confirmed or not.
/// </returns>
public virtual Task<bool> GetEmailConfirmedAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
return Task.FromResult(user.EmailConfirmed);
}
/// <summary>
/// Sets the flag indicating whether the specified <paramref name="user"/>'s email address has been confirmed or not.
/// </summary>
/// <param name="user">The user whose email confirmation status should be set.</param>
/// <param name="confirmed">A flag indicating if the email address has been confirmed, true if the address is confirmed otherwise false.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The task object representing the asynchronous operation.</returns>
public virtual Task SetEmailConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
user.EmailConfirmed = confirmed;
return Task.CompletedTask;
}
/// <summary>
/// Sets the <paramref name="email"/> address for a <paramref name="user"/>.
/// </summary>
/// <param name="user">The user whose email should be set.</param>
/// <param name="email">The email to set.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The task object representing the asynchronous operation.</returns>
public virtual Task SetEmailAsync(TUser user, string? email, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
user.Email = email;
return Task.CompletedTask;
}
/// <summary>
/// Gets the email address for the specified <paramref name="user"/>.
/// </summary>
/// <param name="user">The user whose email should be returned.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The task object containing the results of the asynchronous operation, the email address for the specified <paramref name="user"/>.</returns>
public virtual Task<string?> GetEmailAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
return Task.FromResult(user.Email);
}
/// <summary>
/// Returns the normalized email for the specified <paramref name="user"/>.
/// </summary>
/// <param name="user">The user whose email address to retrieve.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>
/// The task object containing the results of the asynchronous lookup operation, the normalized email address if any associated with the specified user.
/// </returns>
public virtual Task<string?> GetNormalizedEmailAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
return Task.FromResult(user.NormalizedEmail);
}
/// <summary>
/// Sets the normalized email for the specified <paramref name="user"/>.
/// </summary>
/// <param name="user">The user whose email address to set.</param>
/// <param name="normalizedEmail">The normalized email to set for the specified <paramref name="user"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The task object representing the asynchronous operation.</returns>
public virtual Task SetNormalizedEmailAsync(TUser user, string? normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
user.NormalizedEmail = normalizedEmail;
return Task.CompletedTask;
}
/// <summary>
/// Gets the user, if any, associated with the specified, normalized email address.
/// </summary>
/// <param name="normalizedEmail">The normalized email address to return the user for.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>
/// The task object containing the results of the asynchronous lookup operation, the user if any associated with the specified normalized email address.
/// </returns>
public abstract Task<TUser?> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Gets the last <see cref="DateTimeOffset"/> a user's last lockout expired, if any.
/// Any time in the past should be indicates a user is not locked out.
/// </summary>
/// <param name="user">The user whose lockout date should be retrieved.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>
/// A <see cref="Task{TResult}"/> that represents the result of the asynchronous query, a <see cref="DateTimeOffset"/> containing the last time
/// a user's lockout expired, if any.
/// </returns>
public virtual Task<DateTimeOffset?> GetLockoutEndDateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
return Task.FromResult(user.LockoutEnd);
}
/// <summary>
/// Locks out a user until the specified end date has passed. Setting a end date in the past immediately unlocks a user.
/// </summary>
/// <param name="user">The user whose lockout date should be set.</param>
/// <param name="lockoutEnd">The <see cref="DateTimeOffset"/> after which the <paramref name="user"/>'s lockout should end.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public virtual Task SetLockoutEndDateAsync(TUser user, DateTimeOffset? lockoutEnd, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
user.LockoutEnd = lockoutEnd;
return Task.CompletedTask;
}
/// <summary>
/// Records that a failed access has occurred, incrementing the failed access count.
/// </summary>
/// <param name="user">The user whose cancellation count should be incremented.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the incremented failed access count.</returns>
public virtual Task<int> IncrementAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
user.AccessFailedCount++;
return Task.FromResult(user.AccessFailedCount);
}
/// <summary>
/// Resets a user's failed access count.
/// </summary>
/// <param name="user">The user whose failed access count should be reset.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
/// <remarks>This is typically called after the account is successfully accessed.</remarks>
public virtual Task ResetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
user.AccessFailedCount = 0;
return Task.CompletedTask;
}
/// <summary>
/// Retrieves the current failed access count for the specified <paramref name="user"/>..
/// </summary>
/// <param name="user">The user whose failed access count should be retrieved.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the failed access count.</returns>
public virtual Task<int> GetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
return Task.FromResult(user.AccessFailedCount);
}
/// <summary>
/// Retrieves a flag indicating whether user lockout can enabled for the specified user.
/// </summary>
/// <param name="user">The user whose ability to be locked out should be returned.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>
/// The <see cref="Task"/> that represents the asynchronous operation, true if a user can be locked out, otherwise false.
/// </returns>
public virtual Task<bool> GetLockoutEnabledAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
return Task.FromResult(user.LockoutEnabled);
}
/// <summary>
/// Set the flag indicating if the specified <paramref name="user"/> can be locked out..
/// </summary>
/// <param name="user">The user whose ability to be locked out should be set.</param>
/// <param name="enabled">A flag indicating if lock out can be enabled for the specified <paramref name="user"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public virtual Task SetLockoutEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
user.LockoutEnabled = enabled;
return Task.CompletedTask;
}
/// <summary>
/// Sets the telephone number for the specified <paramref name="user"/>.
/// </summary>
/// <param name="user">The user whose telephone number should be set.</param>
/// <param name="phoneNumber">The telephone number to set.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public virtual Task SetPhoneNumberAsync(TUser user, string? phoneNumber, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
user.PhoneNumber = phoneNumber;
return Task.CompletedTask;
}
/// <summary>
/// Gets the telephone number, if any, for the specified <paramref name="user"/>.
/// </summary>
/// <param name="user">The user whose telephone number should be retrieved.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the user's telephone number, if any.</returns>
public virtual Task<string?> GetPhoneNumberAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
return Task.FromResult(user.PhoneNumber);
}
/// <summary>
/// Gets a flag indicating whether the specified <paramref name="user"/>'s telephone number has been confirmed.
/// </summary>
/// <param name="user">The user to return a flag for, indicating whether their telephone number is confirmed.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>
/// The <see cref="Task"/> that represents the asynchronous operation, returning true if the specified <paramref name="user"/> has a confirmed
/// telephone number otherwise false.
/// </returns>
public virtual Task<bool> GetPhoneNumberConfirmedAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
return Task.FromResult(user.PhoneNumberConfirmed);
}
/// <summary>
/// Sets a flag indicating if the specified <paramref name="user"/>'s phone number has been confirmed..
/// </summary>
/// <param name="user">The user whose telephone number confirmation status should be set.</param>
/// <param name="confirmed">A flag indicating whether the user's telephone number has been confirmed.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public virtual Task SetPhoneNumberConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
user.PhoneNumberConfirmed = confirmed;
return Task.CompletedTask;
}
/// <summary>
/// Sets the provided security <paramref name="stamp"/> for the specified <paramref name="user"/>.
/// </summary>
/// <param name="user">The user whose security stamp should be set.</param>
/// <param name="stamp">The security stamp to set.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public virtual Task SetSecurityStampAsync(TUser user, string stamp, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
ArgumentNullThrowHelper.ThrowIfNull(stamp);
user.SecurityStamp = stamp;
return Task.CompletedTask;
}
/// <summary>
/// Get the security stamp for the specified <paramref name="user" />.
/// </summary>
/// <param name="user">The user whose security stamp should be set.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the security stamp for the specified <paramref name="user"/>.</returns>
public virtual Task<string?> GetSecurityStampAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
return Task.FromResult(user.SecurityStamp);
}
/// <summary>
/// Sets a flag indicating whether the specified <paramref name="user"/> has two factor authentication enabled or not,
/// as an asynchronous operation.
/// </summary>
/// <param name="user">The user whose two factor authentication enabled status should be set.</param>
/// <param name="enabled">A flag indicating whether the specified <paramref name="user"/> has two factor authentication enabled.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public virtual Task SetTwoFactorEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
user.TwoFactorEnabled = enabled;
return Task.CompletedTask;
}
/// <summary>
/// Returns a flag indicating whether the specified <paramref name="user"/> has two factor authentication enabled or not,
/// as an asynchronous operation.
/// </summary>
/// <param name="user">The user whose two factor authentication enabled status should be set.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>
/// The <see cref="Task"/> that represents the asynchronous operation, containing a flag indicating whether the specified
/// <paramref name="user"/> has two factor authentication enabled or not.
/// </returns>
public virtual Task<bool> GetTwoFactorEnabledAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
return Task.FromResult(user.TwoFactorEnabled);
}
/// <summary>
/// Retrieves all users with the specified claim.
/// </summary>
/// <param name="claim">The claim whose users should be retrieved.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>
/// The <see cref="Task"/> contains a list of users, if any, that contain the specified claim.
/// </returns>
public abstract Task<IList<TUser>> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Find a user token if it exists.
/// </summary>
/// <param name="user">The token owner.</param>
/// <param name="loginProvider">The login provider for the token.</param>
/// <param name="name">The name of the token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The user token if it exists.</returns>
protected abstract Task<TUserToken?> FindTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken);
/// <summary>
/// Add a new user token.
/// </summary>
/// <param name="token">The token to be added.</param>
/// <returns></returns>
protected abstract Task AddUserTokenAsync(TUserToken token);
/// <summary>
/// Remove a new user token.
/// </summary>
/// <param name="token">The token to be removed.</param>
/// <returns></returns>
protected abstract Task RemoveUserTokenAsync(TUserToken token);
/// <summary>
/// Sets the token value for a particular user.
/// </summary>
/// <param name="user">The user.</param>
/// <param name="loginProvider">The authentication provider for the token.</param>
/// <param name="name">The name of the token.</param>
/// <param name="value">The value of the token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public virtual async Task SetTokenAsync(TUser user, string loginProvider, string name, string? value, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
var token = await FindTokenAsync(user, loginProvider, name, cancellationToken).ConfigureAwait(false);
if (token == null)
{
await AddUserTokenAsync(CreateUserToken(user, loginProvider, name, value)).ConfigureAwait(false);
}
else
{
token.Value = value;
}
}
/// <summary>
/// Deletes a token for a user.
/// </summary>
/// <param name="user">The user.</param>
/// <param name="loginProvider">The authentication provider for the token.</param>
/// <param name="name">The name of the token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public virtual async Task RemoveTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
var entry = await FindTokenAsync(user, loginProvider, name, cancellationToken).ConfigureAwait(false);
if (entry != null)
{
await RemoveUserTokenAsync(entry).ConfigureAwait(false);
}
}
/// <summary>
/// Returns the token value.
/// </summary>
/// <param name="user">The user.</param>
/// <param name="loginProvider">The authentication provider for the token.</param>
/// <param name="name">The name of the token.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public virtual async Task<string?> GetTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
var entry = await FindTokenAsync(user, loginProvider, name, cancellationToken).ConfigureAwait(false);
return entry?.Value;
}
private const string InternalLoginProvider = "[AspNetUserStore]";
private const string AuthenticatorKeyTokenName = "AuthenticatorKey";
private const string RecoveryCodeTokenName = "RecoveryCodes";
/// <summary>
/// Sets the authenticator key for the specified <paramref name="user"/>.
/// </summary>
/// <param name="user">The user whose authenticator key should be set.</param>
/// <param name="key">The authenticator key to set.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public virtual Task SetAuthenticatorKeyAsync(TUser user, string key, CancellationToken cancellationToken)
=> SetTokenAsync(user, InternalLoginProvider, AuthenticatorKeyTokenName, key, cancellationToken);
/// <summary>
/// Get the authenticator key for the specified <paramref name="user" />.
/// </summary>
/// <param name="user">The user whose security stamp should be set.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation, containing the security stamp for the specified <paramref name="user"/>.</returns>
public virtual Task<string?> GetAuthenticatorKeyAsync(TUser user, CancellationToken cancellationToken)
=> GetTokenAsync(user, InternalLoginProvider, AuthenticatorKeyTokenName, cancellationToken);
/// <summary>
/// Returns how many recovery code are still valid for a user.
/// </summary>
/// <param name="user">The user who owns the recovery code.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The number of valid recovery codes for the user..</returns>
public virtual async Task<int> CountCodesAsync(TUser user, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
var mergedCodes = await GetTokenAsync(user, InternalLoginProvider, RecoveryCodeTokenName, cancellationToken).ConfigureAwait(false) ?? "";
if (mergedCodes.Length > 0)
{
#if NET8_0_OR_GREATER
return mergedCodes.AsSpan().Count(';') + 1;
#else
// non-allocating version of mergedCodes.Split(';').Length
var count = 1;
var index = 0;
while (index < mergedCodes.Length)
{
var semiColonIndex = mergedCodes.IndexOf(';', index);
if (semiColonIndex < 0)
{
break;
}
count++;
index = semiColonIndex + 1;
}
return count;
#endif
}
return 0;
}
/// <summary>
/// Updates the recovery codes for the user while invalidating any previous recovery codes.
/// </summary>
/// <param name="user">The user to store new recovery codes for.</param>
/// <param name="recoveryCodes">The new recovery codes for the user.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The new recovery codes for the user.</returns>
public virtual Task ReplaceCodesAsync(TUser user, IEnumerable<string> recoveryCodes, CancellationToken cancellationToken)
{
var mergedCodes = string.Join(";", recoveryCodes);
return SetTokenAsync(user, InternalLoginProvider, RecoveryCodeTokenName, mergedCodes, cancellationToken);
}
/// <summary>
/// Returns whether a recovery code is valid for a user. Note: recovery codes are only valid
/// once, and will be invalid after use.
/// </summary>
/// <param name="user">The user who owns the recovery code.</param>
/// <param name="code">The recovery code to use.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>True if the recovery code was found for the user.</returns>
public virtual async Task<bool> RedeemCodeAsync(TUser user, string code, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
ArgumentNullThrowHelper.ThrowIfNull(user);
ArgumentNullThrowHelper.ThrowIfNull(code);
var mergedCodes = await GetTokenAsync(user, InternalLoginProvider, RecoveryCodeTokenName, cancellationToken).ConfigureAwait(false) ?? "";
var splitCodes = mergedCodes.Split(';');
if (splitCodes.Contains(code))
{
var updatedCodes = new List<string>(splitCodes.Where(s => s != code));
await ReplaceCodesAsync(user, updatedCodes, cancellationToken).ConfigureAwait(false);
return true;
}
return false;
}
}
/// <summary>
/// Represents a new instance of a persistence store for the specified user and role types.
/// </summary>
/// <typeparam name="TUser">The type representing a user.</typeparam>
/// <typeparam name="TRole">The type representing a role.</typeparam>
/// <typeparam name="TKey">The type of the primary key for a role.</typeparam>
/// <typeparam name="TUserClaim">The type representing a claim.</typeparam>
/// <typeparam name="TUserRole">The type representing a user role.</typeparam>
/// <typeparam name="TUserLogin">The type representing a user external login.</typeparam>
/// <typeparam name="TUserToken">The type representing a user token.</typeparam>
/// <typeparam name="TRoleClaim">The type representing a role claim.</typeparam>
public abstract class UserStoreBase<TUser, TRole, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim> :
UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>,
IUserRoleStore<TUser>
where TUser : IdentityUser<TKey>
where TRole : IdentityRole<TKey>
where TKey : IEquatable<TKey>
where TUserClaim : IdentityUserClaim<TKey>, new()
where TUserRole : IdentityUserRole<TKey>, new()
where TUserLogin : IdentityUserLogin<TKey>, new()
where TUserToken : IdentityUserToken<TKey>, new()
where TRoleClaim : IdentityRoleClaim<TKey>, new()
{
/// <summary>
/// Creates a new instance.
/// </summary>
/// <param name="describer">The <see cref="IdentityErrorDescriber"/> used to describe store errors.</param>
public UserStoreBase(IdentityErrorDescriber describer) : base(describer) { }
/// <summary>
/// Called to create a new instance of a <see cref="IdentityUserRole{TKey}"/>.
/// </summary>
/// <param name="user">The associated user.</param>
/// <param name="role">The associated role.</param>
/// <returns></returns>
protected virtual TUserRole CreateUserRole(TUser user, TRole role)
{
return new TUserRole()
{
UserId = user.Id,
RoleId = role.Id
};
}
/// <summary>
/// Retrieves all users in the specified role.
/// </summary>
/// <param name="normalizedRoleName">The role whose users should be retrieved.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>
/// The <see cref="Task"/> contains a list of users, if any, that are in the specified role.
/// </returns>
public abstract Task<IList<TUser>> GetUsersInRoleAsync(string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Adds the given <paramref name="normalizedRoleName"/> to the specified <paramref name="user"/>.
/// </summary>
/// <param name="user">The user to add the role to.</param>
/// <param name="normalizedRoleName">The role to add.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public abstract Task AddToRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Removes the given <paramref name="normalizedRoleName"/> from the specified <paramref name="user"/>.
/// </summary>
/// <param name="user">The user to remove the role from.</param>
/// <param name="normalizedRoleName">The role to remove.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
public abstract Task RemoveFromRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Retrieves the roles the specified <paramref name="user"/> is a member of.
/// </summary>
/// <param name="user">The user whose roles should be retrieved.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>A <see cref="Task{TResult}"/> that contains the roles the user is a member of.</returns>
public abstract Task<IList<string>> GetRolesAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Returns a flag indicating if the specified user is a member of the give <paramref name="normalizedRoleName"/>.
/// </summary>
/// <param name="user">The user whose role membership should be checked.</param>
/// <param name="normalizedRoleName">The role to check membership of</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>A <see cref="Task{TResult}"/> containing a flag indicating if the specified user is a member of the given group. If the
/// user is a member of the group the returned value with be true, otherwise it will be false.</returns>
public abstract Task<bool> IsInRoleAsync(TUser user, string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Return a role with the normalized name if it exists.
/// </summary>
/// <param name="normalizedRoleName">The normalized role name.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The role if it exists.</returns>
protected abstract Task<TRole?> FindRoleAsync(string normalizedRoleName, CancellationToken cancellationToken);
/// <summary>
/// Return a user role for the userId and roleId if it exists.
/// </summary>
/// <param name="userId">The user's id.</param>
/// <param name="roleId">The role's id.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>The user role if it exists.</returns>
protected abstract Task<TUserRole?> FindUserRoleAsync(TKey userId, TKey roleId, CancellationToken cancellationToken);
}
|