// 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 Microsoft.AspNetCore.Identity.Test; namespace Microsoft.AspNetCore.Identity.InMemory { public class InMemoryUserStore : IUserLoginStore, IUserClaimStore, IUserPasswordStore, IUserSecurityStampStore, IUserEmailStore, IUserLockoutStore, IUserPhoneNumberStore, IQueryableUserStore, IUserTwoFactorStore, IUserAuthenticationTokenStore, IUserAuthenticatorKeyStore, IUserTwoFactorRecoveryCodeStore where TUser : PocoUser { private readonly Dictionary _logins = new Dictionary(); private readonly Dictionary _users = new Dictionary(); public IQueryable Users { get { return _users.Values.AsQueryable(); } } public Task> GetClaimsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { var claims = user.Claims.Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToList(); return Task.FromResult>(claims); } public Task AddClaimsAsync(TUser user, IEnumerable claims, CancellationToken cancellationToken = default(CancellationToken)) { foreach (var claim in claims) { user.Claims.Add(new PocoUserClaim { ClaimType = claim.Type, ClaimValue = claim.Value, UserId = user.Id }); } return Task.FromResult(0); } public Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken)) { var matchedClaims = user.Claims.Where(uc => uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToList(); foreach (var matchedClaim in matchedClaims) { matchedClaim.ClaimValue = newClaim.Value; matchedClaim.ClaimType = newClaim.Type; } return Task.FromResult(0); } public Task RemoveClaimsAsync(TUser user, IEnumerable claims, CancellationToken cancellationToken = default(CancellationToken)) { foreach (var claim in claims) { var entity = user.Claims.FirstOrDefault( uc => uc.UserId == user.Id && uc.ClaimType == claim.Type && uc.ClaimValue == claim.Value); if (entity != null) { user.Claims.Remove(entity); } } return Task.FromResult(0); } public Task SetEmailAsync(TUser user, string email, CancellationToken cancellationToken = default(CancellationToken)) { user.Email = email; return Task.FromResult(0); } public Task GetEmailAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(user.Email); } public Task GetNormalizedEmailAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(user.NormalizedEmail); } public Task SetNormalizedEmailAsync(TUser user, string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken)) { user.NormalizedEmail = normalizedEmail; return Task.FromResult(0); } public Task GetEmailConfirmedAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(user.EmailConfirmed); } public Task SetEmailConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken)) { user.EmailConfirmed = confirmed; return Task.FromResult(0); } public Task FindByEmailAsync(string email, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult( Users.FirstOrDefault(u => u.NormalizedEmail == email)); } public Task GetLockoutEndDateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(user.LockoutEnd); } public Task SetLockoutEndDateAsync(TUser user, DateTimeOffset? lockoutEnd, CancellationToken cancellationToken = default(CancellationToken)) { user.LockoutEnd = lockoutEnd; return Task.FromResult(0); } public Task IncrementAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { user.AccessFailedCount++; return Task.FromResult(user.AccessFailedCount); } public Task ResetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { user.AccessFailedCount = 0; return Task.FromResult(0); } public Task GetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(user.AccessFailedCount); } public Task GetLockoutEnabledAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(user.LockoutEnabled); } public Task SetLockoutEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken)) { user.LockoutEnabled = enabled; return Task.FromResult(0); } private string GetLoginKey(string loginProvider, string providerKey) { return loginProvider + "|" + providerKey; } public virtual Task AddLoginAsync(TUser user, UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken)) { user.Logins.Add(new PocoUserLogin { UserId = user.Id, ProviderKey = login.ProviderKey, LoginProvider = login.LoginProvider, ProviderDisplayName = login.ProviderDisplayName }); _logins[GetLoginKey(login.LoginProvider, login.ProviderKey)] = user; return Task.FromResult(0); } public Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken)) { var loginEntity = user.Logins.SingleOrDefault( l => l.ProviderKey == providerKey && l.LoginProvider == loginProvider && l.UserId == user.Id); if (loginEntity != null) { user.Logins.Remove(loginEntity); } _logins[GetLoginKey(loginProvider, providerKey)] = null; return Task.FromResult(0); } public Task> GetLoginsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { IList result = user.Logins .Select(l => new UserLoginInfo(l.LoginProvider, l.ProviderKey, l.ProviderDisplayName)).ToList(); return Task.FromResult(result); } public Task FindByLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken)) { string key = GetLoginKey(loginProvider, providerKey); if (_logins.ContainsKey(key)) { return Task.FromResult(_logins[key]); } return Task.FromResult(null); } public Task GetUserIdAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(user.Id); } public Task GetUserNameAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(user.UserName); } public Task SetUserNameAsync(TUser user, string userName, CancellationToken cancellationToken = default(CancellationToken)) { user.UserName = userName; return Task.FromResult(0); } public Task CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { _users[user.Id] = user; return Task.FromResult(IdentityResult.Success); } public Task UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { _users[user.Id] = user; return Task.FromResult(IdentityResult.Success); } public Task FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken)) { if (_users.ContainsKey(userId)) { return Task.FromResult(_users[userId]); } return Task.FromResult(null); } public void Dispose() { } public Task FindByNameAsync(string userName, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult( Users.FirstOrDefault(u => u.NormalizedUserName == userName)); } public Task DeleteAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { if (user == null || !_users.ContainsKey(user.Id)) { throw new InvalidOperationException("Unknown user"); } _users.Remove(user.Id); return Task.FromResult(IdentityResult.Success); } public Task SetPasswordHashAsync(TUser user, string passwordHash, CancellationToken cancellationToken = default(CancellationToken)) { user.PasswordHash = passwordHash; return Task.FromResult(0); } public Task GetPasswordHashAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(user.PasswordHash); } public Task HasPasswordAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(user.PasswordHash != null); } public Task SetPhoneNumberAsync(TUser user, string phoneNumber, CancellationToken cancellationToken = default(CancellationToken)) { user.PhoneNumber = phoneNumber; return Task.FromResult(0); } public Task GetPhoneNumberAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(user.PhoneNumber); } public Task GetPhoneNumberConfirmedAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(user.PhoneNumberConfirmed); } public Task SetPhoneNumberConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken)) { user.PhoneNumberConfirmed = confirmed; return Task.FromResult(0); } public Task SetSecurityStampAsync(TUser user, string stamp, CancellationToken cancellationToken = default(CancellationToken)) { user.SecurityStamp = stamp; return Task.FromResult(0); } public Task GetSecurityStampAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(user.SecurityStamp); } public Task SetTwoFactorEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken)) { user.TwoFactorEnabled = enabled; return Task.FromResult(0); } public Task GetTwoFactorEnabledAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(user.TwoFactorEnabled); } public Task GetNormalizedUserNameAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) { return Task.FromResult(user.NormalizedUserName); } public Task SetNormalizedUserNameAsync(TUser user, string userName, CancellationToken cancellationToken = default(CancellationToken)) { user.NormalizedUserName = userName; return Task.FromResult(0); } public Task> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken)) { if (claim == null) { throw new ArgumentNullException(nameof(claim)); } var query = from user in Users where user.Claims.Where(x => x.ClaimType == claim.Type && x.ClaimValue == claim.Value).FirstOrDefault() != null select user; return Task.FromResult>(query.ToList()); } public Task SetTokenAsync(TUser user, string loginProvider, string name, string value, CancellationToken cancellationToken) { var tokenEntity = user.Tokens.SingleOrDefault( l => l.TokenName == name && l.LoginProvider == loginProvider && l.UserId == user.Id); if (tokenEntity != null) { tokenEntity.TokenValue = value; } else { user.Tokens.Add(new PocoUserToken { UserId = user.Id, LoginProvider = loginProvider, TokenName = name, TokenValue = value }); } return Task.FromResult(0); } public Task RemoveTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken) { var tokenEntity = user.Tokens.SingleOrDefault( l => l.TokenName == name && l.LoginProvider == loginProvider && l.UserId == user.Id); if (tokenEntity != null) { user.Tokens.Remove(tokenEntity); } return Task.FromResult(0); } public Task GetTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken) { var tokenEntity = user.Tokens.SingleOrDefault( l => l.TokenName == name && l.LoginProvider == loginProvider && l.UserId == user.Id); return Task.FromResult(tokenEntity?.TokenValue); } private const string AuthenticatorStoreLoginProvider = "[AspNetAuthenticatorStore]"; private const string AuthenticatorKeyTokenName = "AuthenticatorKey"; private const string RecoveryCodeTokenName = "RecoveryCodes"; public Task SetAuthenticatorKeyAsync(TUser user, string key, CancellationToken cancellationToken) { return SetTokenAsync(user, AuthenticatorStoreLoginProvider, AuthenticatorKeyTokenName, key, cancellationToken); } public Task GetAuthenticatorKeyAsync(TUser user, CancellationToken cancellationToken) { return GetTokenAsync(user, AuthenticatorStoreLoginProvider, AuthenticatorKeyTokenName, cancellationToken); } public Task ReplaceCodesAsync(TUser user, IEnumerable recoveryCodes, CancellationToken cancellationToken) { var mergedCodes = string.Join(";", recoveryCodes); return SetTokenAsync(user, AuthenticatorStoreLoginProvider, RecoveryCodeTokenName, mergedCodes, cancellationToken); } public async Task RedeemCodeAsync(TUser user, string code, CancellationToken cancellationToken) { var mergedCodes = await GetTokenAsync(user, AuthenticatorStoreLoginProvider, RecoveryCodeTokenName, cancellationToken) ?? ""; var splitCodes = mergedCodes.Split(';'); if (splitCodes.Contains(code)) { var updatedCodes = new List(splitCodes.Where(s => s != code)); await ReplaceCodesAsync(user, updatedCodes, cancellationToken); return true; } return false; } public async Task CountCodesAsync(TUser user, CancellationToken cancellationToken) { var mergedCodes = await GetTokenAsync(user, AuthenticatorStoreLoginProvider, RecoveryCodeTokenName, cancellationToken) ?? ""; if (mergedCodes.Length > 0) { return mergedCodes.Split(';').Length; } return 0; } } }