// Copyright (c) Microsoft Open Technologies, Inc. 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.Globalization;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Framework.OptionsModel;
namespace Microsoft.AspNet.Identity
{
///
/// Exposes user related api which will automatically save changes to the UserStore
///
///
public class UserManager : IDisposable where TUser : class
{
private readonly Dictionary> _tokenProviders =
new Dictionary>();
private TimeSpan _defaultLockout = TimeSpan.Zero;
private bool _disposed;
private IPasswordHasher _passwordHasher;
private IdentityOptions _options;
///
/// Constructor which takes a service provider and user store
///
///
///
///
///
///
///
public UserManager(IUserStore store, IOptions optionsAccessor,
IPasswordHasher passwordHasher, IUserValidator userValidator,
IPasswordValidator passwordValidator, IUserNameNormalizer userNameNormalizer,
IEnumerable> tokenProviders)
{
if (store == null)
{
throw new ArgumentNullException(nameof(store));
}
if (optionsAccessor == null || optionsAccessor.Options == null)
{
throw new ArgumentNullException(nameof(optionsAccessor));
}
if (passwordHasher == null)
{
throw new ArgumentNullException(nameof(passwordHasher));
}
Store = store;
Options = optionsAccessor.Options;
PasswordHasher = passwordHasher;
UserValidator = userValidator;
PasswordValidator = passwordValidator;
UserNameNormalizer = userNameNormalizer;
// TODO: Email/Sms/Token services
if (tokenProviders != null)
{
foreach (var tokenProvider in tokenProviders)
{
RegisterTokenProvider(tokenProvider);
}
}
}
///
/// Persistence abstraction that the Manager operates against
///
protected internal IUserStore Store { get; set; }
///
/// Used to hash/verify passwords
///
public IPasswordHasher PasswordHasher
{
get
{
ThrowIfDisposed();
return _passwordHasher;
}
set
{
ThrowIfDisposed();
if (value == null)
{
throw new ArgumentNullException("value");
}
_passwordHasher = value;
}
}
///
/// Used to validate users before persisting changes
///
public IUserValidator UserValidator { get; set; }
///
/// Used to validate passwords before persisting changes
///
public IPasswordValidator PasswordValidator { get; set; }
///
/// Used to normalize user names for uniqueness
///
public IUserNameNormalizer UserNameNormalizer { get; set; }
///
/// Used to send email
///
public IIdentityMessageService EmailService { get; set; }
///
/// Used to send a sms message
///
public IIdentityMessageService SmsService { get; set; }
public IdentityOptions Options
{
get
{
ThrowIfDisposed();
return _options;
}
set
{
ThrowIfDisposed();
if (value == null)
{
throw new ArgumentNullException("value");
}
_options = value;
}
}
///
/// Returns true if the store is an IUserTwoFactorStore
///
public virtual bool SupportsUserTwoFactor
{
get
{
ThrowIfDisposed();
return Store is IUserTwoFactorStore;
}
}
///
/// Returns true if the store is an IUserPasswordStore
///
public virtual bool SupportsUserPassword
{
get
{
ThrowIfDisposed();
return Store is IUserPasswordStore;
}
}
///
/// Returns true if the store is an IUserSecurityStore
///
public virtual bool SupportsUserSecurityStamp
{
get
{
ThrowIfDisposed();
return Store is IUserSecurityStampStore;
}
}
///
/// Returns true if the store is an IUserRoleStore
///
public virtual bool SupportsUserRole
{
get
{
ThrowIfDisposed();
return Store is IUserRoleStore;
}
}
///
/// Returns true if the store is an IUserLoginStore
///
public virtual bool SupportsUserLogin
{
get
{
ThrowIfDisposed();
return Store is IUserLoginStore;
}
}
///
/// Returns true if the store is an IUserEmailStore
///
public virtual bool SupportsUserEmail
{
get
{
ThrowIfDisposed();
return Store is IUserEmailStore;
}
}
///
/// Returns true if the store is an IUserPhoneNumberStore
///
public virtual bool SupportsUserPhoneNumber
{
get
{
ThrowIfDisposed();
return Store is IUserPhoneNumberStore;
}
}
///
/// Returns true if the store is an IUserClaimStore
///
public virtual bool SupportsUserClaim
{
get
{
ThrowIfDisposed();
return Store is IUserClaimStore;
}
}
///
/// Returns true if the store is an IUserLockoutStore
///
public virtual bool SupportsUserLockout
{
get
{
ThrowIfDisposed();
return Store is IUserLockoutStore;
}
}
///
/// Returns true if the store is an IQueryableUserStore
///
public virtual bool SupportsQueryableUsers
{
get
{
ThrowIfDisposed();
return Store is IQueryableUserStore;
}
}
///
/// Returns an IQueryable of users if the store is an IQueryableUserStore
///
public virtual IQueryable Users
{
get
{
var queryableStore = Store as IQueryableUserStore;
if (queryableStore == null)
{
throw new NotSupportedException(Resources.StoreNotIQueryableUserStore);
}
return queryableStore.Users;
}
}
///
/// Dispose the store context
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private async Task ValidateUserInternal(TUser user, CancellationToken cancellationToken)
{
return (UserValidator == null)
? IdentityResult.Success
: await UserValidator.ValidateAsync(this, user, cancellationToken);
}
///
/// Create a user with no password
///
///
///
///
public virtual async Task CreateAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
await UpdateSecurityStampInternal(user, cancellationToken);
var result = await ValidateUserInternal(user, cancellationToken);
if (!result.Succeeded)
{
return result;
}
if (Options.Lockout.EnabledByDefault && SupportsUserLockout)
{
await GetUserLockoutStore().SetLockoutEnabledAsync(user, true, cancellationToken);
}
await UpdateNormalizedUserNameAsync(user, cancellationToken);
await Store.CreateAsync(user, cancellationToken);
return IdentityResult.Success;
}
///
/// Update a user
///
///
///
///
public virtual async Task UpdateAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
var result = await ValidateUserInternal(user, cancellationToken);
if (!result.Succeeded)
{
return result;
}
await UpdateNormalizedUserNameAsync(user, cancellationToken);
await Store.UpdateAsync(user, cancellationToken);
return IdentityResult.Success;
}
///
/// Delete a user
///
///
///
///
public virtual async Task DeleteAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
await Store.DeleteAsync(user, cancellationToken);
return IdentityResult.Success;
}
///
/// Find a user by id
///
///
///
///
public virtual Task FindByIdAsync(string userId,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
return Store.FindByIdAsync(userId, cancellationToken);
}
///
/// Find a user by name
///
///
///
///
public virtual Task FindByNameAsync(string userName,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (userName == null)
{
throw new ArgumentNullException("userName");
}
userName = NormalizeUserName(userName);
return Store.FindByNameAsync(userName, cancellationToken);
}
// IUserPasswordStore methods
private IUserPasswordStore GetPasswordStore()
{
var cast = Store as IUserPasswordStore;
if (cast == null)
{
throw new NotSupportedException(Resources.StoreNotIUserPasswordStore);
}
return cast;
}
///
/// Create a user and associates it with the given password
///
///
///
///
///
public virtual async Task CreateAsync(TUser user, string password,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var passwordStore = GetPasswordStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (password == null)
{
throw new ArgumentNullException("password");
}
var result = await UpdatePasswordInternal(passwordStore, user, password, cancellationToken);
if (!result.Succeeded)
{
return result;
}
return await CreateAsync(user, cancellationToken);
}
///
/// Normalize a user name for uniqueness comparisons
///
///
///
public virtual string NormalizeUserName(string userName)
{
return (UserNameNormalizer == null) ? userName : UserNameNormalizer.Normalize(userName);
}
///
/// Update the user's normalized user name
///
///
///
///
public virtual async Task UpdateNormalizedUserNameAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
var userName = await GetUserNameAsync(user, cancellationToken);
await Store.SetNormalizedUserNameAsync(user, NormalizeUserName(userName), cancellationToken);
}
///
/// Get the user's name
///
///
///
///
public virtual async Task GetUserNameAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
return await Store.GetUserNameAsync(user, cancellationToken);
}
///
/// Set the user's name
///
///
///
///
///
public virtual async Task SetUserNameAsync(TUser user, string userName,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
await UpdateUserName(user, userName, cancellationToken);
return await UpdateAsync(user, cancellationToken);
}
private async Task UpdateUserName(TUser user, string userName, CancellationToken cancellationToken)
{
await Store.SetUserNameAsync(user, userName, cancellationToken);
await UpdateNormalizedUserNameAsync(user, cancellationToken);
}
///
/// Get the user's id
///
///
///
///
public virtual async Task GetUserIdAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
return await Store.GetUserIdAsync(user, cancellationToken);
}
///
/// Return a user with the specified username and password or null if there is no match.
///
///
///
///
///
public virtual async Task FindByUserNamePasswordAsync(string userName, string password,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
userName = NormalizeUserName(userName);
var user = await FindByNameAsync(userName, cancellationToken);
if (user == null)
{
return null;
}
return await CheckPasswordAsync(user, password, cancellationToken) ? user : null;
}
///
/// Returns true if the password combination is valid for the user
///
///
///
///
///
public virtual async Task CheckPasswordAsync(TUser user, string password,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var passwordStore = GetPasswordStore();
if (user == null)
{
return false;
}
return await VerifyPasswordAsync(passwordStore, user, password, cancellationToken);
}
///
/// Returns true if the user has a password
///
///
///
///
public virtual async Task HasPasswordAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var passwordStore = GetPasswordStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
return await passwordStore.HasPasswordAsync(user, cancellationToken);
}
///
/// Add a user password only if one does not already exist
///
///
///
///
///
public virtual async Task AddPasswordAsync(TUser user, string password,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var passwordStore = GetPasswordStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
var hash = await passwordStore.GetPasswordHashAsync(user, cancellationToken);
if (hash != null)
{
return new IdentityResult(Resources.UserAlreadyHasPassword);
}
var result = await UpdatePasswordInternal(passwordStore, user, password, cancellationToken);
if (!result.Succeeded)
{
return result;
}
return await UpdateAsync(user, cancellationToken);
}
///
/// Change a user password
///
///
///
///
///
///
public virtual async Task ChangePasswordAsync(TUser user, string currentPassword,
string newPassword, CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var passwordStore = GetPasswordStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (await VerifyPasswordAsync(passwordStore, user, currentPassword, cancellationToken))
{
var result = await UpdatePasswordInternal(passwordStore, user, newPassword, cancellationToken);
if (!result.Succeeded)
{
return result;
}
return await UpdateAsync(user, cancellationToken);
}
return IdentityResult.Failed(Resources.PasswordMismatch);
}
///
/// Remove a user's password
///
///
///
///
public virtual async Task RemovePasswordAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var passwordStore = GetPasswordStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
await passwordStore.SetPasswordHashAsync(user, null, cancellationToken);
await UpdateSecurityStampInternal(user, cancellationToken);
return await UpdateAsync(user, cancellationToken);
}
internal async Task UpdatePasswordInternal(IUserPasswordStore passwordStore,
TUser user, string newPassword, CancellationToken cancellationToken)
{
if (PasswordValidator != null)
{
var result = await PasswordValidator.ValidateAsync(user, newPassword, this, cancellationToken);
if (!result.Succeeded)
{
return result;
}
}
await
passwordStore.SetPasswordHashAsync(user, PasswordHasher.HashPassword(user, newPassword), cancellationToken);
await UpdateSecurityStampInternal(user, cancellationToken);
return IdentityResult.Success;
}
///
/// By default, retrieves the hashed password from the user store and calls PasswordHasher.VerifyHashPassword
///
///
///
///
///
///
protected virtual async Task VerifyPasswordAsync(IUserPasswordStore store, TUser user,
string password, CancellationToken cancellationToken = default(CancellationToken))
{
var hash = await store.GetPasswordHashAsync(user, cancellationToken);
return PasswordHasher.VerifyHashedPassword(user, hash, password) != PasswordVerificationResult.Failed;
}
// IUserSecurityStampStore methods
private IUserSecurityStampStore GetSecurityStore()
{
var cast = Store as IUserSecurityStampStore;
if (cast == null)
{
throw new NotSupportedException(Resources.StoreNotIUserSecurityStampStore);
}
return cast;
}
///
/// Returns the current security stamp for a user
///
///
///
///
public virtual async Task GetSecurityStampAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var securityStore = GetSecurityStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
return await securityStore.GetSecurityStampAsync(user, cancellationToken);
}
///
/// GenerateAsync a new security stamp for a user, used for SignOutEverywhere functionality
///
///
///
///
public virtual async Task UpdateSecurityStampAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
GetSecurityStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
await UpdateSecurityStampInternal(user, cancellationToken);
return await UpdateAsync(user, cancellationToken);
}
///
/// GenerateAsync a password reset token for the user using the UserTokenProvider
///
///
///
///
public virtual async Task GeneratePasswordResetTokenAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
return await GenerateUserTokenAsync(user, Options.PasswordResetTokenProvider, "ResetPassword", cancellationToken);
}
///
/// Reset a user's password using a reset password token
///
///
///
///
///
///
public virtual async Task ResetPasswordAsync(TUser user, string token, string newPassword,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
// Make sure the token is valid and the stamp matches
if (!await VerifyUserTokenAsync(user, Options.PasswordResetTokenProvider, "ResetPassword", token, cancellationToken))
{
return IdentityResult.Failed(Resources.InvalidToken);
}
var passwordStore = GetPasswordStore();
var result = await UpdatePasswordInternal(passwordStore, user, newPassword, cancellationToken);
if (!result.Succeeded)
{
return result;
}
return await UpdateAsync(user, cancellationToken);
}
// Update the security stamp if the store supports it
internal async Task UpdateSecurityStampInternal(TUser user, CancellationToken cancellationToken)
{
if (SupportsUserSecurityStamp)
{
await GetSecurityStore().SetSecurityStampAsync(user, NewSecurityStamp(), cancellationToken);
}
}
private static string NewSecurityStamp()
{
return Guid.NewGuid().ToString();
}
// IUserLoginStore methods
private IUserLoginStore GetLoginStore()
{
var cast = Store as IUserLoginStore;
if (cast == null)
{
throw new NotSupportedException(Resources.StoreNotIUserLoginStore);
}
return cast;
}
///
/// Returns the user associated with this login
///
///
///
///
///
public virtual Task FindByLoginAsync(string loginProvider, string providerKey,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var loginStore = GetLoginStore();
if (loginProvider == null)
{
throw new ArgumentNullException("loginProvider");
}
if (providerKey == null)
{
throw new ArgumentNullException("providerKey");
}
return loginStore.FindByLoginAsync(loginProvider, providerKey, cancellationToken);
}
///
/// Remove a user login
///
///
///
///
///
public virtual async Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var loginStore = GetLoginStore();
if (loginProvider == null)
{
throw new ArgumentNullException("loginProvider");
}
if (providerKey == null)
{
throw new ArgumentNullException("providerKey");
}
if (user == null)
{
throw new ArgumentNullException("user");
}
await loginStore.RemoveLoginAsync(user, loginProvider, providerKey, cancellationToken);
await UpdateSecurityStampInternal(user, cancellationToken);
return await UpdateAsync(user, cancellationToken);
}
///
/// Associate a login with a user
///
///
///
///
///
public virtual async Task AddLoginAsync(TUser user, UserLoginInfo login,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var loginStore = GetLoginStore();
if (login == null)
{
throw new ArgumentNullException("login");
}
if (user == null)
{
throw new ArgumentNullException("user");
}
var existingUser = await FindByLoginAsync(login.LoginProvider, login.ProviderKey, cancellationToken);
if (existingUser != null)
{
return IdentityResult.Failed(Resources.ExternalLoginExists);
}
await loginStore.AddLoginAsync(user, login, cancellationToken);
return await UpdateAsync(user, cancellationToken);
}
///
/// Gets the logins for a user.
///
///
///
///
public virtual async Task> GetLoginsAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var loginStore = GetLoginStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
return await loginStore.GetLoginsAsync(user, cancellationToken);
}
// IUserClaimStore methods
private IUserClaimStore GetClaimStore()
{
var cast = Store as IUserClaimStore;
if (cast == null)
{
throw new NotSupportedException(Resources.StoreNotIUserClaimStore);
}
return cast;
}
///
/// Add a user claim
///
///
///
///
///
public virtual Task AddClaimAsync(TUser user, Claim claim,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var claimStore = GetClaimStore();
if (claim == null)
{
throw new ArgumentNullException("claim");
}
if (user == null)
{
throw new ArgumentNullException("user");
}
return AddClaimsAsync(user, new Claim[] { claim }, cancellationToken);
}
///
/// Add a user claim
///
///
///
///
///
public virtual async Task AddClaimsAsync(TUser user, IEnumerable claims,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var claimStore = GetClaimStore();
if (claims == null)
{
throw new ArgumentNullException("claims");
}
if (user == null)
{
throw new ArgumentNullException("user");
}
await claimStore.AddClaimsAsync(user, claims, cancellationToken);
return await UpdateAsync(user, cancellationToken);
}
///
/// Updates the give claim information with the given new claim information
///
///
///
///
///
///
public virtual async Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var claimStore = GetClaimStore();
if (claim == null)
{
throw new ArgumentNullException("claim");
}
if (newClaim == null)
{
throw new ArgumentNullException("newClaim");
}
if (user == null)
{
throw new ArgumentNullException("user");
}
await claimStore.ReplaceClaimAsync(user, claim, newClaim, cancellationToken);
return await UpdateAsync(user, cancellationToken);
}
///
/// Remove a user claim
///
///
///
///
///
public virtual Task RemoveClaimAsync(TUser user, Claim claim,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var claimStore = GetClaimStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (claim == null)
{
throw new ArgumentNullException("claim");
}
return RemoveClaimsAsync(user, new Claim[] { claim }, cancellationToken);
}
///
/// Remove a user claim
///
///
///
///
///
public virtual async Task RemoveClaimsAsync(TUser user, IEnumerable claims,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var claimStore = GetClaimStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (claims == null)
{
throw new ArgumentNullException("claims");
}
await claimStore.RemoveClaimsAsync(user, claims, cancellationToken);
return await UpdateAsync(user, cancellationToken);
}
///
/// Get a users's claims
///
///
///
///
public virtual async Task> GetClaimsAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var claimStore = GetClaimStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
return await claimStore.GetClaimsAsync(user, cancellationToken);
}
private IUserRoleStore GetUserRoleStore()
{
var cast = Store as IUserRoleStore;
if (cast == null)
{
throw new NotSupportedException(Resources.StoreNotIUserRoleStore);
}
return cast;
}
///
/// Add a user to a role
///
///
///
///
///
public virtual async Task AddToRoleAsync(TUser user, string role,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var userRoleStore = GetUserRoleStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
var userRoles = await userRoleStore.GetRolesAsync(user, cancellationToken);
if (userRoles.Contains(role))
{
return new IdentityResult(Resources.UserAlreadyInRole);
}
await userRoleStore.AddToRoleAsync(user, role, cancellationToken);
return await UpdateAsync(user, cancellationToken);
}
///
/// Add a user to roles
///
///
///
///
///
public virtual async Task AddToRolesAsync(TUser user, IEnumerable roles,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var userRoleStore = GetUserRoleStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (roles == null)
{
throw new ArgumentNullException("roles");
}
var userRoles = await userRoleStore.GetRolesAsync(user, cancellationToken);
foreach (var role in roles)
{
if (userRoles.Contains(role))
{
return new IdentityResult(Resources.UserAlreadyInRole);
}
await userRoleStore.AddToRoleAsync(user, role, cancellationToken);
}
return await UpdateAsync(user, cancellationToken);
}
///
/// Remove a user from a role.
///
///
///
///
///
public virtual async Task RemoveFromRoleAsync(TUser user, string role,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var userRoleStore = GetUserRoleStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (!await userRoleStore.IsInRoleAsync(user, role, cancellationToken))
{
return new IdentityResult(Resources.UserNotInRole);
}
await userRoleStore.RemoveFromRoleAsync(user, role, cancellationToken);
return await UpdateAsync(user, cancellationToken);
}
///
/// Remove a user from a specified roles.
///
///
///
///
///
public virtual async Task RemoveFromRolesAsync(TUser user, IEnumerable roles,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var userRoleStore = GetUserRoleStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (roles == null)
{
throw new ArgumentNullException("roles");
}
foreach (var role in roles)
{
if (!await userRoleStore.IsInRoleAsync(user, role, cancellationToken))
{
return new IdentityResult(Resources.UserNotInRole);
}
await userRoleStore.RemoveFromRoleAsync(user, role, cancellationToken);
}
return await UpdateAsync(user, cancellationToken);
}
///
/// Returns the roles for the user
///
///
///
///
public virtual async Task> GetRolesAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var userRoleStore = GetUserRoleStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
return await userRoleStore.GetRolesAsync(user, cancellationToken);
}
///
/// Returns true if the user is in the specified role
///
///
///
///
///
public virtual async Task IsInRoleAsync(TUser user, string role,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var userRoleStore = GetUserRoleStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
return await userRoleStore.IsInRoleAsync(user, role, cancellationToken);
}
// IUserEmailStore methods
internal IUserEmailStore GetEmailStore()
{
var cast = Store as IUserEmailStore;
if (cast == null)
{
throw new NotSupportedException(Resources.StoreNotIUserEmailStore);
}
return cast;
}
///
/// Get a user's email
///
///
///
///
public virtual async Task GetEmailAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var store = GetEmailStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
return await store.GetEmailAsync(user, cancellationToken);
}
///
/// Set a user's email
///
///
///
///
///
public virtual async Task SetEmailAsync(TUser user, string email,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var store = GetEmailStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
await store.SetEmailAsync(user, email, cancellationToken);
await store.SetEmailConfirmedAsync(user, false, cancellationToken);
await UpdateSecurityStampInternal(user, cancellationToken);
return await UpdateAsync(user, cancellationToken);
}
///
/// FindByLoginAsync a user by his email
///
///
///
///
public virtual Task FindByEmailAsync(string email,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var store = GetEmailStore();
if (email == null)
{
throw new ArgumentNullException("email");
}
return store.FindByEmailAsync(email, cancellationToken);
}
///
/// Get the confirmation token for the user
///
///
///
///
public virtual Task GenerateEmailConfirmationTokenAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
return GenerateUserTokenAsync(user, Options.EmailConfirmationTokenProvider, "Confirmation", cancellationToken);
}
///
/// Confirm the user with confirmation token
///
///
///
///
///
public virtual async Task ConfirmEmailAsync(TUser user, string token,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var store = GetEmailStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (!await VerifyUserTokenAsync(user, Options.EmailConfirmationTokenProvider, "Confirmation", token, cancellationToken))
{
return IdentityResult.Failed(Resources.InvalidToken);
}
await store.SetEmailConfirmedAsync(user, true, cancellationToken);
return await UpdateAsync(user, cancellationToken);
}
///
/// Returns true if the user's email has been confirmed
///
///
///
///
public virtual async Task IsEmailConfirmedAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var store = GetEmailStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
return await store.GetEmailConfirmedAsync(user, cancellationToken);
}
// IUserPhoneNumberStore methods
internal IUserPhoneNumberStore GetPhoneNumberStore()
{
var cast = Store as IUserPhoneNumberStore;
if (cast == null)
{
throw new NotSupportedException(Resources.StoreNotIUserPhoneNumberStore);
}
return cast;
}
///
/// Get a user's phoneNumber
///
///
///
///
public virtual async Task GetPhoneNumberAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var store = GetPhoneNumberStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
return await store.GetPhoneNumberAsync(user, cancellationToken);
}
///
/// Set a user's phoneNumber
///
///
///
///
///
public virtual async Task SetPhoneNumberAsync(TUser user, string phoneNumber,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var store = GetPhoneNumberStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
await store.SetPhoneNumberAsync(user, phoneNumber, cancellationToken);
await store.SetPhoneNumberConfirmedAsync(user, false, cancellationToken);
await UpdateSecurityStampInternal(user, cancellationToken);
return await UpdateAsync(user, cancellationToken);
}
///
/// Set a user's phoneNumber with the verification token
///
///
///
///
///
///
public virtual async Task ChangePhoneNumberAsync(TUser user, string phoneNumber, string token,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var store = GetPhoneNumberStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (!await VerifyChangePhoneNumberTokenAsync(user, token, phoneNumber, cancellationToken))
{
return IdentityResult.Failed(Resources.InvalidToken);
}
await store.SetPhoneNumberAsync(user, phoneNumber, cancellationToken);
await store.SetPhoneNumberConfirmedAsync(user, true, cancellationToken);
await UpdateSecurityStampInternal(user, cancellationToken);
return await UpdateAsync(user, cancellationToken);
}
///
/// Returns true if the user's phone number has been confirmed
///
///
///
///
public virtual async Task IsPhoneNumberConfirmedAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var store = GetPhoneNumberStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
return await store.GetPhoneNumberConfirmedAsync(user, cancellationToken);
}
// Two factor APIS
internal async Task CreateSecurityTokenAsync(TUser user, CancellationToken cancellationToken)
{
return
new SecurityToken(Encoding.Unicode.GetBytes(await GetSecurityStampAsync(user, cancellationToken)));
}
///
/// Get a phone number code for a user and phone number
///
///
///
///
public virtual async Task GenerateChangePhoneNumberTokenAsync(TUser user, string phoneNumber,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
return Rfc6238AuthenticationService.GenerateCode(
await CreateSecurityTokenAsync(user, cancellationToken), phoneNumber)
.ToString(CultureInfo.InvariantCulture);
}
///
/// Verify a phone number code for a specific user and phone number
///
///
///
///
///
public virtual async Task VerifyChangePhoneNumberTokenAsync(TUser user, string token, string phoneNumber,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var securityToken = await CreateSecurityTokenAsync(user, cancellationToken);
int code;
if (securityToken != null && Int32.TryParse(token, out code))
{
return Rfc6238AuthenticationService.ValidateCode(securityToken, code, phoneNumber);
}
return false;
}
///
/// Verify a user token with the specified purpose
///
///
///
///
///
///
public virtual async Task VerifyUserTokenAsync(TUser user, string tokenProvider, string purpose, string token,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (tokenProvider == null)
{
throw new ArgumentNullException(nameof(tokenProvider));
}
if (!_tokenProviders.ContainsKey(tokenProvider))
{
throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, Resources.NoTokenProvider, tokenProvider));
}
// Make sure the token is valid
return await _tokenProviders[tokenProvider].ValidateAsync(purpose, token, this, user, cancellationToken);
}
///
/// Get a user token for a specific purpose
///
///
///
///
///
public virtual async Task GenerateUserTokenAsync(TUser user, string tokenProvider, string purpose,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (tokenProvider == null)
{
throw new ArgumentNullException(nameof(tokenProvider));
}
if (!_tokenProviders.ContainsKey(tokenProvider))
{
throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, Resources.NoTokenProvider, tokenProvider));
}
return await _tokenProviders[tokenProvider].GenerateAsync(purpose, this, user, cancellationToken);
}
///
/// Register a user token provider
///
///
///
public virtual void RegisterTokenProvider(IUserTokenProvider provider)
{
ThrowIfDisposed();
if (provider == null)
{
throw new ArgumentNullException("provider");
}
_tokenProviders[provider.Name] = provider;
}
///
/// Returns a list of valid two factor providers for a user
///
///
///
///
public virtual async Task> GetValidTwoFactorProvidersAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
var results = new List();
foreach (var f in _tokenProviders)
{
if (await f.Value.CanGenerateTwoFactorTokenAsync(this, user, cancellationToken))
{
results.Add(f.Key);
}
}
return results;
}
///
/// Verify a user token with the specified provider
///
///
///
///
///
///
public virtual async Task VerifyTwoFactorTokenAsync(TUser user, string tokenProvider, string token,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (!_tokenProviders.ContainsKey(tokenProvider))
{
throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture,
Resources.NoTokenProvider, tokenProvider));
}
// Make sure the token is valid
return await _tokenProviders[tokenProvider].ValidateAsync("TwoFactor", token, this, user, cancellationToken);
}
///
/// Get a user token for a specific user factor provider
///
///
///
///
///
public virtual async Task GenerateTwoFactorTokenAsync(TUser user, string tokenProvider,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (!_tokenProviders.ContainsKey(tokenProvider))
{
throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture,
Resources.NoTokenProvider, tokenProvider));
}
return await _tokenProviders[tokenProvider].GenerateAsync("TwoFactor", this, user, cancellationToken);
}
///
/// Notify a user with a token from a specific user factor provider
///
///
///
///
///
///
public virtual async Task NotifyTwoFactorTokenAsync(TUser user, string tokenProvider,
string token, CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (tokenProvider == null)
{
throw new ArgumentNullException(nameof(tokenProvider));
}
if (!_tokenProviders.ContainsKey(tokenProvider))
{
throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture,
Resources.NoTokenProvider, tokenProvider));
}
await _tokenProviders[tokenProvider].NotifyAsync(token, this, user, cancellationToken);
return IdentityResult.Success;
}
// IUserFactorStore methods
internal IUserTwoFactorStore GetUserTwoFactorStore()
{
var cast = Store as IUserTwoFactorStore;
if (cast == null)
{
throw new NotSupportedException(Resources.StoreNotIUserTwoFactorStore);
}
return cast;
}
///
/// Get a user's two factor provider
///
///
///
///
public virtual async Task GetTwoFactorEnabledAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var store = GetUserTwoFactorStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
return await store.GetTwoFactorEnabledAsync(user, cancellationToken);
}
///
/// Set whether a user has two factor enabled or not
///
///
///
///
///
public virtual async Task SetTwoFactorEnabledAsync(TUser user, bool enabled,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var store = GetUserTwoFactorStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
await store.SetTwoFactorEnabledAsync(user, enabled, cancellationToken);
await UpdateSecurityStampInternal(user, cancellationToken);
return await UpdateAsync(user, cancellationToken);
}
// SMS/Email methods
///
/// Send an email to the user
///
///
///
///
///
///
public virtual async Task SendEmailAsync(TUser user, string subject, string body,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (EmailService != null)
{
var msg = new IdentityMessage
{
Destination = await GetEmailAsync(user, cancellationToken),
Subject = subject,
Body = body,
};
await EmailService.SendAsync(msg, cancellationToken);
}
}
///
/// Send a user a sms message
///
///
///
///
///
public virtual async Task SendSmsAsync(TUser user, string message,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (SmsService != null)
{
var msg = new IdentityMessage
{
Destination = await GetPhoneNumberAsync(user, cancellationToken),
Body = message
};
await SmsService.SendAsync(msg, cancellationToken);
}
}
// IUserLockoutStore methods
internal IUserLockoutStore GetUserLockoutStore()
{
var cast = Store as IUserLockoutStore;
if (cast == null)
{
throw new NotSupportedException(Resources.StoreNotIUserLockoutStore);
}
return cast;
}
///
/// Returns true if the user is locked out
///
///
///
///
public virtual async Task IsLockedOutAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var store = GetUserLockoutStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (!await store.GetLockoutEnabledAsync(user, cancellationToken))
{
return false;
}
var lockoutTime = await store.GetLockoutEndDateAsync(user, cancellationToken).ConfigureAwait((false));
return lockoutTime >= DateTimeOffset.UtcNow;
}
///
/// Sets whether the user allows lockout
///
///
///
///
///
public virtual async Task SetLockoutEnabledAsync(TUser user, bool enabled,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var store = GetUserLockoutStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
await store.SetLockoutEnabledAsync(user, enabled, cancellationToken);
return await UpdateAsync(user, cancellationToken);
}
///
/// Returns whether the user allows lockout
///
///
///
///
public virtual async Task GetLockoutEnabledAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var store = GetUserLockoutStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
return await store.GetLockoutEnabledAsync(user, cancellationToken);
}
///
/// Returns the user lockout end date
///
///
///
///
public virtual async Task GetLockoutEndDateAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var store = GetUserLockoutStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
return await store.GetLockoutEndDateAsync(user, cancellationToken);
}
///
/// Sets the user lockout end date
///
///
///
///
///
public virtual async Task SetLockoutEndDateAsync(TUser user, DateTimeOffset lockoutEnd,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var store = GetUserLockoutStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (!await store.GetLockoutEnabledAsync(user, cancellationToken).ConfigureAwait((false)))
{
return IdentityResult.Failed(Resources.LockoutNotEnabled);
}
await store.SetLockoutEndDateAsync(user, lockoutEnd, cancellationToken);
return await UpdateAsync(user, cancellationToken);
}
///
/// Increments the access failed count for the user and if the failed access account is greater than or equal
/// to the MaxFailedAccessAttempsBeforeLockout, the user will be locked out for the next
/// DefaultAccountLockoutTimeSpan and the AccessFailedCount will be reset to 0.
///
///
///
///
public virtual async Task AccessFailedAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var store = GetUserLockoutStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
// If this puts the user over the threshold for lockout, lock them out and reset the access failed count
var count = await store.IncrementAccessFailedCountAsync(user, cancellationToken);
if (count < Options.Lockout.MaxFailedAccessAttempts)
{
return await UpdateAsync(user, cancellationToken);
}
await store.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.Add(Options.Lockout.DefaultLockoutTimeSpan),
cancellationToken);
await store.ResetAccessFailedCountAsync(user, cancellationToken);
return await UpdateAsync(user, cancellationToken);
}
///
/// Resets the access failed count for the user to 0
///
///
///
///
public virtual async Task ResetAccessFailedCountAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var store = GetUserLockoutStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
await store.ResetAccessFailedCountAsync(user, cancellationToken);
return await UpdateAsync(user, cancellationToken);
}
///
/// Returns the number of failed access attempts for the user
///
///
///
///
public virtual async Task GetAccessFailedCountAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var store = GetUserLockoutStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
return await store.GetAccessFailedCountAsync(user, cancellationToken);
}
private void ThrowIfDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
}
///
/// When disposing, actually dipose the store context
///
///
protected virtual void Dispose(bool disposing)
{
if (disposing && !_disposed)
{
Store.Dispose();
_disposed = true;
}
}
}
}