1114 lines
57 KiB
C#
1114 lines
57 KiB
C#
// Copyright (c) .NET Foundation. All rights reserved.
|
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Security.Claims;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using MongoDbGenericRepository;
|
|
using MongoDB.Driver;
|
|
using AspNetCore.Identity.MongoDbCore.Extensions;
|
|
using AspNetCore.Identity.MongoDbCore.Models;
|
|
using AspNetCore.Identity.MongoDbCore.Infrastructure;
|
|
using Microsoft.AspNetCore.Identity;
|
|
|
|
namespace AspNetCore.Identity.MongoDbCore
|
|
{
|
|
/// <summary>
|
|
/// Creates a new instance of a persistence store for the specified user type.
|
|
/// </summary>
|
|
/// <typeparam name="TUser">The type representing a user.</typeparam>
|
|
public class MongoUserOnlyStore<TUser> : MongoUserOnlyStore<TUser, IMongoDbContext, string>
|
|
where TUser : MongoIdentityUser<string>, new()
|
|
{
|
|
/// <summary>
|
|
/// Constructs a new instance of <see cref="MongoUserOnlyStore{TUser}"/>.
|
|
/// </summary>
|
|
/// <param name="context">The <see cref="IMongoDbContext"/>.</param>
|
|
/// <param name="describer">The <see cref="IdentityErrorDescriber"/>.</param>
|
|
public MongoUserOnlyStore(IMongoDbContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
|
|
}
|
|
|
|
/// <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="TContext">The type of the data context class used to access the store.</typeparam>
|
|
public class MongoUserOnlyStore<TUser, TContext> : MongoUserOnlyStore<TUser, TContext, string>
|
|
where TUser : MongoIdentityUser<string>
|
|
where TContext : IMongoDbContext
|
|
{
|
|
/// <summary>
|
|
/// Constructs a new instance of <see cref="MongoUserOnlyStore{TUser, TContext}"/>.
|
|
/// </summary>
|
|
/// <param name="context">The <see cref="IMongoDbContext"/>.</param>
|
|
/// <param name="describer">The <see cref="IdentityErrorDescriber"/>.</param>
|
|
public MongoUserOnlyStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
|
|
}
|
|
|
|
/// <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="TContext">The type of the data context class used to access the store.</typeparam>
|
|
/// <typeparam name="TKey">The type of the primary key for a role.</typeparam>
|
|
public class MongoUserOnlyStore<TUser, TContext, TKey> : MongoUserOnlyStore<TUser, TContext, TKey, IdentityUserClaim<TKey>, IdentityUserLogin<TKey>, IdentityUserToken<TKey>>
|
|
where TUser : MongoIdentityUser<TKey>
|
|
where TContext : IMongoDbContext
|
|
where TKey : IEquatable<TKey>
|
|
{
|
|
/// <summary>
|
|
/// Constructs a new instance of <see cref="MongoUserOnlyStore{TUser, TContext, TKey}"/>.
|
|
/// </summary>
|
|
/// <param name="context">The <see cref="IMongoDbContext"/>.</param>
|
|
/// <param name="describer">The <see cref="IdentityErrorDescriber"/>.</param>
|
|
public MongoUserOnlyStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
|
|
}
|
|
|
|
/// <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="TContext">The type of the data context class used to access the store.</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="TUserLogin">The type representing a user external login.</typeparam>
|
|
/// <typeparam name="TUserToken">The type representing a user token.</typeparam>
|
|
public class MongoUserOnlyStore<TUser, TContext, TKey, TUserClaim, TUserLogin, TUserToken> :
|
|
UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>,
|
|
IUserAuthenticationTokenStore<TUser>
|
|
where TUser : MongoIdentityUser<TKey>
|
|
where TContext : IMongoDbContext
|
|
where TKey : IEquatable<TKey>
|
|
where TUserClaim : IdentityUserClaim<TKey>, new()
|
|
where TUserLogin : IdentityUserLogin<TKey>, new()
|
|
where TUserToken : IdentityUserToken<TKey>, new()
|
|
{
|
|
/// <summary>
|
|
/// Creates a new instance of the store.
|
|
/// </summary>
|
|
/// <param name="context">The context used to access the store.</param>
|
|
/// <param name="describer">The <see cref="IdentityErrorDescriber"/> used to describe store errors.</param>
|
|
public MongoUserOnlyStore(TContext context, IdentityErrorDescriber describer = null) : base(describer ?? new IdentityErrorDescriber())
|
|
{
|
|
if (context == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(context));
|
|
}
|
|
Context = context;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the database context for this store.
|
|
/// </summary>
|
|
private static TContext Context { get; set; }
|
|
|
|
private static IMongoRepository _mongoRepository;
|
|
private static IMongoRepository MongoRepository
|
|
{
|
|
get
|
|
{
|
|
if (_mongoRepository == null)
|
|
{
|
|
_mongoRepository = new MongoRepository(Context);
|
|
}
|
|
return _mongoRepository;
|
|
}
|
|
}
|
|
|
|
private IMongoCollection<TUser> UsersCollection { get { return Context.GetCollection<TUser>(); } }
|
|
|
|
/// <summary>
|
|
/// Gets or sets a flag indicating if changes should be persisted after CreateAsync, UpdateAsync and DeleteAsync are called.
|
|
/// </summary>
|
|
/// <value>
|
|
/// True if changes should be automatically persisted, otherwise false.
|
|
/// </value>
|
|
public bool AutoSaveChanges { get; set; } = true;
|
|
|
|
/// <summary>Saves the current store.</summary>
|
|
/// <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>
|
|
protected Task SaveChanges(CancellationToken cancellationToken)
|
|
{
|
|
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 async override Task<IdentityResult> CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
await UsersCollection.InsertOneAsync(user);
|
|
await SaveChanges(cancellationToken);
|
|
return IdentityResult.Success;
|
|
}
|
|
|
|
/// <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 async override Task<IdentityResult> UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
var oldStamp = user.ConcurrencyStamp;
|
|
user.ConcurrencyStamp = Guid.NewGuid().ToString();
|
|
var updateRes = await UsersCollection.ReplaceOneAsync(x => x.Id.Equals(user.Id)
|
|
&& x.ConcurrencyStamp.Equals(oldStamp),
|
|
user);
|
|
if(updateRes.ModifiedCount == 0)
|
|
{
|
|
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
|
|
}
|
|
return IdentityResult.Success;
|
|
}
|
|
|
|
/// <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 async override Task<IdentityResult> DeleteAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
user.Claims.Clear();
|
|
user.Roles.Clear();
|
|
user.Logins.Clear();
|
|
user.Tokens.Clear();
|
|
var oldStamp = user.ConcurrencyStamp;
|
|
user.ConcurrencyStamp = Guid.NewGuid().ToString();
|
|
var deleteRes = await UsersCollection.DeleteOneAsync(x => x.Id.Equals(user.Id)
|
|
&& x.ConcurrencyStamp.Equals(oldStamp));
|
|
if (deleteRes.DeletedCount == 0)
|
|
{
|
|
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
|
|
}
|
|
return IdentityResult.Success;
|
|
}
|
|
|
|
/// <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 override Task<TUser> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
var id = ConvertIdFromString(userId);
|
|
return MongoRepository.GetByIdAsync<TUser, TKey>(id);
|
|
}
|
|
|
|
/// <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 override Task<TUser> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
return MongoRepository.GetOneAsync<TUser, TKey>(u => u.NormalizedUserName == normalizedUserName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// A navigation property for the users the store contains.
|
|
/// </summary>
|
|
public override IQueryable<TUser> Users
|
|
{
|
|
get { return UsersCollection.AsQueryable(); }
|
|
}
|
|
|
|
/// <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 override Task<TUser> FindUserAsync(TKey userId, CancellationToken cancellationToken)
|
|
{
|
|
return MongoRepository.GetOneAsync<TUser, TKey>(u => u.Id.Equals(userId));
|
|
}
|
|
|
|
/// <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 override Task<TUserLogin> FindUserLoginAsync(TKey userId, string loginProvider, string providerKey, CancellationToken cancellationToken)
|
|
{
|
|
var user = MongoRepository.GetOne<TUser, TKey>(x => x.Id.Equals(userId) && x.Logins.Any(e => e.LoginProvider == loginProvider && e.ProviderKey == providerKey));
|
|
if (user != null)
|
|
{
|
|
return Task.FromResult((TUserLogin)user.GetUserLogin(loginProvider, providerKey));
|
|
}
|
|
return Task.FromResult(default(TUserLogin));
|
|
}
|
|
|
|
/// <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 override Task<TUserLogin> FindUserLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken)
|
|
{
|
|
var user = MongoRepository.GetOne<TUser, TKey>(x => x.Logins.Any(e => e.LoginProvider == loginProvider && e.ProviderKey == providerKey));
|
|
if (user != null)
|
|
{
|
|
return Task.FromResult((TUserLogin)user.GetUserLogin(loginProvider, providerKey));
|
|
}
|
|
return Task.FromResult(default(TUserLogin));
|
|
}
|
|
|
|
#pragma warning disable CS1998 // Cette méthode async n'a pas d'opérateur 'await' et elle s'exécutera de façon synchrone
|
|
/// <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 async override Task<IList<Claim>> GetClaimsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
|
#pragma warning restore CS1998 // Cette méthode async n'a pas d'opérateur 'await' et elle s'exécutera de façon synchrone
|
|
{
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
return user.Claims.Select(e => e.ToClaim()).ToList();
|
|
}
|
|
|
|
/// <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 override Task AddClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
if (claims == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(claims));
|
|
}
|
|
var addedSome = false;
|
|
foreach (var claim in claims)
|
|
{
|
|
if (user.AddClaim(claim))
|
|
{
|
|
addedSome |= true;
|
|
}
|
|
}
|
|
if (addedSome)
|
|
{
|
|
var success = MongoRepository.UpdateOne<TUser, TKey, List<MongoClaim>>(user, p => p.Claims, user.Claims);
|
|
if (!success)
|
|
{
|
|
throw new Exception($"Failed to add claims to user {user.Id.ToString()}");
|
|
}
|
|
}
|
|
return Task.FromResult(false);
|
|
}
|
|
|
|
/// <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 async override Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
if (claim == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(claim));
|
|
}
|
|
if (newClaim == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(newClaim));
|
|
}
|
|
|
|
if (user.ReplaceClaim(claim, newClaim))
|
|
{
|
|
await MongoRepository.UpdateOneAsync<TUser, TKey, List<MongoClaim>>(user, e => e.Claims, user.Claims);
|
|
}
|
|
}
|
|
|
|
/// <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 async override Task RemoveClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
if (claims == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(claims));
|
|
}
|
|
if (user.RemoveClaims(claims))
|
|
{
|
|
await MongoRepository.UpdateOneAsync<TUser, TKey, List<MongoClaim>>(user, e => e.Claims, user.Claims);
|
|
}
|
|
}
|
|
|
|
/// <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 override Task AddLoginAsync(TUser user, UserLoginInfo login,
|
|
CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
|
|
if (login == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(login));
|
|
}
|
|
|
|
if (user.AddLogin(login))
|
|
{
|
|
MongoRepository.UpdateOne<TUser, TKey, List<UserLoginInfo>>(user, e => e.Logins, user.Logins);
|
|
}
|
|
|
|
return Task.FromResult(false);
|
|
}
|
|
|
|
#pragma warning disable CS1998 // Cette méthode async n'a pas d'opérateur 'await' et elle s'exécutera de façon synchrone
|
|
/// <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 override async Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey,
|
|
#pragma warning restore CS1998 // Cette méthode async n'a pas d'opérateur 'await' et elle s'exécutera de façon synchrone
|
|
CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
var entry = user.Logins.FirstOrDefault(e => e.LoginProvider == loginProvider && e.ProviderKey == providerKey);
|
|
if (entry != null)
|
|
{
|
|
user.RemoveLogin(entry);
|
|
}
|
|
}
|
|
|
|
#pragma warning disable CS1998 // Cette méthode async n'a pas d'opérateur 'await' et elle s'exécutera de façon synchrone
|
|
/// <summary>
|
|
/// Retrieves the associated logins for the specified <param ref="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 async override Task<IList<UserLoginInfo>> GetLoginsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
|
#pragma warning restore CS1998 // Cette méthode async n'a pas d'opérateur 'await' et elle s'exécutera de façon synchrone
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
return user.Logins.ToList();
|
|
}
|
|
|
|
/// <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 async override Task<TUser> FindByLoginAsync(string loginProvider, string providerKey,
|
|
CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
var userLogin = await FindUserLoginAsync(loginProvider, providerKey, cancellationToken);
|
|
if (userLogin != null)
|
|
{
|
|
return await FindUserAsync(userLogin.UserId, cancellationToken);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <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 override Task<TUser> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
return MongoRepository.GetOneAsync<TUser, TKey>(u => u.NormalizedEmail == normalizedEmail);
|
|
}
|
|
|
|
/// <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 async override Task<IList<TUser>> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
if (claim == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(claim));
|
|
}
|
|
var filter = Builders<TUser>.Filter.ElemMatch(x => x.Claims, userClaims => userClaims.Value.Equals(claim.Value)
|
|
&& userClaims.Type.Equals(claim.Type));
|
|
var cursor = UsersCollection.Find(filter);
|
|
var res = await cursor.ToListAsync();
|
|
return res;
|
|
}
|
|
|
|
#region Token Management
|
|
|
|
/// <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 override Task<TUserToken> FindTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken)
|
|
{
|
|
return Task.FromResult((TUserToken)user.GetToken(loginProvider, name));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a new user token.
|
|
/// </summary>
|
|
/// <param name="token">The token to be added.</param>
|
|
/// <returns></returns>
|
|
protected override Task AddUserTokenAsync(TUserToken token)
|
|
{
|
|
var user = MongoRepository.GetById<TUser, TKey>(token.UserId);
|
|
if (user != null)
|
|
{
|
|
if (user.AddUserToken(token))
|
|
{
|
|
MongoRepository.UpdateOne<TUser, TKey, List<Token>>(user, e => e.Tokens, user.Tokens);
|
|
}
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove a new user token.
|
|
/// </summary>
|
|
/// <param name="token">The token to be removed.</param>
|
|
/// <returns></returns>
|
|
protected override Task RemoveUserTokenAsync(TUserToken token)
|
|
{
|
|
var user = MongoRepository.GetById<TUser, TKey>(token.UserId);
|
|
if (user != null)
|
|
{
|
|
if (user.RemoveUserToken(token))
|
|
{
|
|
MongoRepository.UpdateOne<TUser, TKey, List<Token>>(user, e => e.Tokens, user.Tokens);
|
|
}
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
#endregion Token Management
|
|
|
|
#region UserStoreBase overrides
|
|
|
|
/// <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 override Task SetUserNameAsync(TUser user, string userName, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
|
|
if (user.UserName != userName)
|
|
{
|
|
user.UserName = userName;
|
|
MongoRepository.UpdateOne<TUser, TKey, string>(user, e => e.UserName, user.UserName);
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <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 override Task SetNormalizedUserNameAsync(TUser user, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
|
|
if (user.NormalizedUserName != normalizedName)
|
|
{
|
|
user.NormalizedUserName = normalizedName;
|
|
MongoRepository.UpdateOne<TUser, TKey, string>(user, e => e.NormalizedUserName, user.NormalizedUserName);
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <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 override Task SetPasswordHashAsync(TUser user, string passwordHash, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
|
|
if (user.PasswordHash != passwordHash)
|
|
{
|
|
user.PasswordHash = passwordHash;
|
|
MongoRepository.UpdateOne<TUser, TKey, string>(user, e => e.PasswordHash, user.PasswordHash);
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <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 override Task SetEmailConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
if (user.EmailConfirmed != confirmed)
|
|
{
|
|
user.EmailConfirmed = confirmed;
|
|
MongoRepository.UpdateOne<TUser, TKey, bool>(user, e => e.EmailConfirmed, user.EmailConfirmed);
|
|
}
|
|
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 override Task SetEmailAsync(TUser user, string email, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
|
|
if (user.Email != email)
|
|
{
|
|
user.Email = email;
|
|
MongoRepository.UpdateOne<TUser, TKey, string>(user, e => e.Email, user.Email);
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <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 override Task SetNormalizedEmailAsync(TUser user, string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
|
|
if (user.NormalizedEmail != normalizedEmail)
|
|
{
|
|
user.NormalizedEmail = normalizedEmail;
|
|
MongoRepository.UpdateOne<TUser, TKey, string>(user, e => e.NormalizedEmail, user.NormalizedEmail);
|
|
}
|
|
user.NormalizedEmail = normalizedEmail;
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <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 override Task SetLockoutEndDateAsync(TUser user, DateTimeOffset? lockoutEnd, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
|
|
if (user.LockoutEnd != lockoutEnd)
|
|
{
|
|
user.LockoutEnd = lockoutEnd;
|
|
MongoRepository.UpdateOne<TUser, TKey, DateTimeOffset?>(user, e => e.LockoutEnd, user.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 override Task<int> IncrementAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
user.AccessFailedCount++;
|
|
MongoRepository.UpdateOne<TUser, TKey, int>(user, e => e.AccessFailedCount, 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 override Task ResetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
|
|
if (user.AccessFailedCount != 0)
|
|
{
|
|
user.AccessFailedCount = 0;
|
|
MongoRepository.UpdateOne<TUser, TKey, int>(user, e => e.AccessFailedCount, user.AccessFailedCount);
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <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 override Task SetLockoutEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
|
|
if (user.LockoutEnabled != enabled)
|
|
{
|
|
user.LockoutEnabled = enabled;
|
|
MongoRepository.UpdateOne<TUser, TKey, bool>(user, e => e.LockoutEnabled, user.LockoutEnabled);
|
|
}
|
|
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 override Task SetPhoneNumberAsync(TUser user, string phoneNumber, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
if (user.PhoneNumber != phoneNumber)
|
|
{
|
|
user.PhoneNumber = phoneNumber;
|
|
MongoRepository.UpdateOne<TUser, TKey, string>(user, e => e.PhoneNumber, user.PhoneNumber);
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <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 override Task SetPhoneNumberConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
|
|
if (user.PhoneNumberConfirmed != confirmed)
|
|
{
|
|
user.PhoneNumberConfirmed = confirmed;
|
|
MongoRepository.UpdateOne<TUser, TKey, bool>(user, e => e.PhoneNumberConfirmed, user.PhoneNumberConfirmed);
|
|
}
|
|
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 override Task SetSecurityStampAsync(TUser user, string stamp, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
if (stamp == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(stamp));
|
|
}
|
|
|
|
if (user.SecurityStamp != stamp)
|
|
{
|
|
user.SecurityStamp = stamp;
|
|
MongoRepository.UpdateOne<TUser, TKey, string>(user, e => e.SecurityStamp, user.SecurityStamp);
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <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 override Task SetTwoFactorEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken))
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
if (user.TwoFactorEnabled != enabled)
|
|
{
|
|
user.TwoFactorEnabled = enabled;
|
|
MongoRepository.UpdateOne<TUser, TKey, bool>(user, e => e.TwoFactorEnabled, user.TwoFactorEnabled);
|
|
}
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <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 override async Task SetTokenAsync(TUser user, string loginProvider, string name, string value, CancellationToken cancellationToken)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
|
|
var token = await FindTokenAsync(user, loginProvider, name, cancellationToken);
|
|
if (token == null)
|
|
{
|
|
if (user.AddUserToken(CreateUserToken(user, loginProvider, name, value)))
|
|
{
|
|
MongoRepository.UpdateOne<TUser, TKey, List<Token>>(user, e => e.Tokens, user.Tokens);
|
|
}
|
|
//await AddUserTokenAsync(CreateUserToken(user, loginProvider, name, value));
|
|
}
|
|
else
|
|
{
|
|
if (user.SetToken(token, value))
|
|
{
|
|
MongoRepository.UpdateOne<TUser, TKey, List<Token>>(user, e => e.Tokens, user.Tokens);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <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 override async Task RemoveTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
var entry = await FindTokenAsync(user, loginProvider, name, cancellationToken);
|
|
if (entry != null)
|
|
{
|
|
if (user.RemoveUserToken(entry))
|
|
{
|
|
MongoRepository.UpdateOne<TUser, TKey, List<Token>>(user, e => e.Tokens, user.Tokens);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <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 override async Task<string> GetTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
var entry = await FindTokenAsync(user, loginProvider, name, cancellationToken);
|
|
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 override 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 override 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 override async Task<int> CountCodesAsync(TUser user, CancellationToken cancellationToken)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
var mergedCodes = await GetTokenAsync(user, InternalLoginProvider, RecoveryCodeTokenName, cancellationToken) ?? "";
|
|
if (mergedCodes.Length > 0)
|
|
{
|
|
return mergedCodes.Split(';').Length;
|
|
}
|
|
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 override 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 override async Task<bool> RedeemCodeAsync(TUser user, string code, CancellationToken cancellationToken)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
ThrowIfDisposed();
|
|
|
|
if (user == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(user));
|
|
}
|
|
if (code == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(code));
|
|
}
|
|
|
|
var mergedCodes = await GetTokenAsync(user, InternalLoginProvider, RecoveryCodeTokenName, cancellationToken) ?? "";
|
|
var splitCodes = mergedCodes.Split(';');
|
|
if (splitCodes.Contains(code))
|
|
{
|
|
var updatedCodes = new List<string>(splitCodes.Where(s => s != code));
|
|
await ReplaceCodesAsync(user, updatedCodes, cancellationToken);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |