From 06f9b90aea07265ca030cce11da63e95de41edaf Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 3 Mar 2014 13:59:23 -0800 Subject: [PATCH] Initial in memory implementation InMemory implementation for Identity --- Identity.sln | 14 + .../InMemoryRole.cs | 19 ++ .../InMemoryRoleStore.cs | 54 ++++ .../InMemoryUser.cs | 97 ++++++ .../InMemoryUserStore.cs | 275 ++++++++++++++++++ .../project.json | 11 + 6 files changed, 470 insertions(+) create mode 100644 src/Microsoft.AspNet.Identity.InMemory/InMemoryRole.cs create mode 100644 src/Microsoft.AspNet.Identity.InMemory/InMemoryRoleStore.cs create mode 100644 src/Microsoft.AspNet.Identity.InMemory/InMemoryUser.cs create mode 100644 src/Microsoft.AspNet.Identity.InMemory/InMemoryUserStore.cs create mode 100644 src/Microsoft.AspNet.Identity.InMemory/project.json diff --git a/Identity.sln b/Identity.sln index dd2aeb9d02..6b25553d56 100644 --- a/Identity.sln +++ b/Identity.sln @@ -19,6 +19,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Identity.E EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Identity.Entity.k10", "src\Microsoft.AspNet.Identity.Entity\Microsoft.AspNet.Identity.Entity.k10.csproj", "{D32483A4-B617-480C-81E6-49CD596B9A34}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Identity.InMemory.net45", "src\Microsoft.AspNet.Identity.InMemory\Microsoft.AspNet.Identity.InMemory.net45.csproj", "{054B3FFA-7196-466F-9A8A-593FFE037A69}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Identity.InMemory.k10", "src\Microsoft.AspNet.Identity.InMemory\Microsoft.AspNet.Identity.InMemory.k10.csproj", "{D2E7A146-C39F-4302-8EA3-BFA8C1082939}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -41,6 +45,14 @@ Global {D32483A4-B617-480C-81E6-49CD596B9A34}.Debug|Any CPU.Build.0 = Debug|Any CPU {D32483A4-B617-480C-81E6-49CD596B9A34}.Release|Any CPU.ActiveCfg = Release|Any CPU {D32483A4-B617-480C-81E6-49CD596B9A34}.Release|Any CPU.Build.0 = Release|Any CPU + {054B3FFA-7196-466F-9A8A-593FFE037A69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {054B3FFA-7196-466F-9A8A-593FFE037A69}.Debug|Any CPU.Build.0 = Debug|Any CPU + {054B3FFA-7196-466F-9A8A-593FFE037A69}.Release|Any CPU.ActiveCfg = Release|Any CPU + {054B3FFA-7196-466F-9A8A-593FFE037A69}.Release|Any CPU.Build.0 = Release|Any CPU + {D2E7A146-C39F-4302-8EA3-BFA8C1082939}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D2E7A146-C39F-4302-8EA3-BFA8C1082939}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D2E7A146-C39F-4302-8EA3-BFA8C1082939}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D2E7A146-C39F-4302-8EA3-BFA8C1082939}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -50,7 +62,9 @@ Global {77CEDA6C-A833-455D-8357-649BFD944724} = {0F647068-6602-4E24-B1DC-8ED91481A50A} {B72401D7-47F6-4A98-89D5-CCBFEFC5B2B8} = {F6B0C0E9-C346-49D0-B583-95B6CE04BB1B} {E52361C9-1F0B-4229-86A0-E5C7C12A5429} = {F6B0C0E9-C346-49D0-B583-95B6CE04BB1B} + {054B3FFA-7196-466F-9A8A-593FFE037A69} = {F6B0C0E9-C346-49D0-B583-95B6CE04BB1B} {6211450F-FFB8-431F-84E2-9A7620875260} = {77CEDA6C-A833-455D-8357-649BFD944724} {D32483A4-B617-480C-81E6-49CD596B9A34} = {77CEDA6C-A833-455D-8357-649BFD944724} + {D2E7A146-C39F-4302-8EA3-BFA8C1082939} = {77CEDA6C-A833-455D-8357-649BFD944724} EndGlobalSection EndGlobal diff --git a/src/Microsoft.AspNet.Identity.InMemory/InMemoryRole.cs b/src/Microsoft.AspNet.Identity.InMemory/InMemoryRole.cs new file mode 100644 index 0000000000..aff560f8b0 --- /dev/null +++ b/src/Microsoft.AspNet.Identity.InMemory/InMemoryRole.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNet.Identity; + +namespace Microsoft.AspNet.Identity.InMemory +{ + public class InMemoryRole : IRole + { + public InMemoryRole(string roleName) + { + Id = Guid.NewGuid().ToString(); + Name = roleName; + } + + public virtual string Id { get; set; } + public virtual string Name { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity.InMemory/InMemoryRoleStore.cs b/src/Microsoft.AspNet.Identity.InMemory/InMemoryRoleStore.cs new file mode 100644 index 0000000000..891f6c2d34 --- /dev/null +++ b/src/Microsoft.AspNet.Identity.InMemory/InMemoryRoleStore.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Identity.InMemory +{ + public class InMemoryRoleStore : IQueryableRoleStore + { + private readonly Dictionary _roles = new Dictionary(); + + public Task Create(InMemoryRole role) + { + _roles[role.Id] = role; + return Task.FromResult(0); + } + + public Task Delete(InMemoryRole role) + { + if (role == null || !_roles.ContainsKey(role.Id)) + { + throw new InvalidOperationException("Unknown role"); + } + _roles.Remove(role.Id); + return Task.FromResult(0); + } + + public Task Update(InMemoryRole role) + { + _roles[role.Id] = role; + return Task.FromResult(0); + } + + public Task FindById(string roleId) + { + if (_roles.ContainsKey(roleId)) + { + return Task.FromResult(_roles[roleId]); + } + return Task.FromResult(null); + } + + public Task FindByName(string roleName) + { + return Task.FromResult(Roles.SingleOrDefault(r => String.Equals(r.Name, roleName, StringComparison.OrdinalIgnoreCase))); + } + + public void Dispose() + { + } + + public IQueryable Roles { get { return _roles.Values.AsQueryable(); } } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity.InMemory/InMemoryUser.cs b/src/Microsoft.AspNet.Identity.InMemory/InMemoryUser.cs new file mode 100644 index 0000000000..b90af5346c --- /dev/null +++ b/src/Microsoft.AspNet.Identity.InMemory/InMemoryUser.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Linq; +#if NET45 +using System.Security.Claims; +#else +using System.Security.ClaimsK; +#endif +using System.Threading.Tasks; +using Microsoft.AspNet.Identity; + +namespace Microsoft.AspNet.Identity.InMemory +{ + public class InMemoryUser : IUser + { + private readonly IList _claims; + private readonly IList _logins; + private readonly IList _roles; + + public InMemoryUser(string name) + { + Id = Guid.NewGuid().ToString(); + _logins = new List(); + _claims = new List(); + _roles = new List(); + UserName = name; + } + + /// + /// Email + /// + public virtual string Email { get; set; } + + /// + /// True if the email is confirmed, default is false + /// + public virtual bool EmailConfirmed { get; set; } + + /// + /// The salted/hashed form of the user password + /// + public virtual string PasswordHash { get; set; } + + /// + /// A random value that should change whenever a users credentials have changed (password changed, login removed) + /// + public virtual string SecurityStamp { get; set; } + + /// + /// PhoneNumber for the user + /// + public virtual string PhoneNumber { get; set; } + + /// + /// True if the phone number is confirmed, default is false + /// + public virtual bool PhoneNumberConfirmed { get; set; } + + /// + /// Is two factor enabled for the user + /// + public virtual bool TwoFactorEnabled { get; set; } + + /// + /// DateTime in UTC when lockout ends, any time in the past is considered not locked out. + /// + public virtual DateTimeOffset LockoutEnd { get; set; } + + /// + /// Is lockout enabled for this user + /// + public virtual bool LockoutEnabled { get; set; } + + /// + /// Used to record failures for the purposes of lockout + /// + public virtual int AccessFailedCount { get; set; } + + public IList Logins + { + get { return _logins; } + } + + public IList Claims + { + get { return _claims; } + } + + public IList Roles + { + get { return _roles; } + } + + public virtual string Id { get; set; } + public virtual string UserName { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity.InMemory/InMemoryUserStore.cs b/src/Microsoft.AspNet.Identity.InMemory/InMemoryUserStore.cs new file mode 100644 index 0000000000..9385246b7a --- /dev/null +++ b/src/Microsoft.AspNet.Identity.InMemory/InMemoryUserStore.cs @@ -0,0 +1,275 @@ +using System; +using System.Collections.Generic; +using System.Linq; +#if NET45 +using System.Security.Claims; +#else +using System.Security.ClaimsK; +#endif +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Identity.InMemory +{ + public class InMemoryUserStore : + IUserStore, + IUserLoginStore, + IUserRoleStore, + IUserClaimStore, + IUserPasswordStore, + IUserSecurityStampStore, + IUserEmailStore, + IUserLockoutStore, + IUserPhoneNumberStore + { + private readonly Dictionary _logins = + new Dictionary(new LoginComparer()); + + private readonly Dictionary _users = new Dictionary(); + + public IQueryable Users + { + get { return _users.Values.AsQueryable(); } + } + + public Task> GetClaims(InMemoryUser user) + { + return Task.FromResult(user.Claims); + } + + public Task AddClaim(InMemoryUser user, Claim claim) + { + user.Claims.Add(claim); + return Task.FromResult(0); + } + + public Task RemoveClaim(InMemoryUser user, Claim claim) + { + user.Claims.Remove(claim); + return Task.FromResult(0); + } + + public Task AddLogin(InMemoryUser user, UserLoginInfo login) + { + user.Logins.Add(login); + _logins[login] = user; + return Task.FromResult(0); + } + + public Task RemoveLogin(InMemoryUser user, UserLoginInfo login) + { + var logs = + user.Logins.Where(l => l.ProviderKey == login.ProviderKey && l.LoginProvider == login.LoginProvider); + foreach (var l in logs) + { + user.Logins.Remove(l); + _logins[l] = null; + } + return Task.FromResult(0); + } + + public Task> GetLogins(InMemoryUser user) + { + return Task.FromResult(user.Logins); + } + + public Task Find(UserLoginInfo login) + { + if (_logins.ContainsKey(login)) + { + return Task.FromResult(_logins[login]); + } + return Task.FromResult(null); + } + + public Task SetPasswordHash(InMemoryUser user, string passwordHash) + { + user.PasswordHash = passwordHash; + return Task.FromResult(0); + } + + public Task GetPasswordHash(InMemoryUser user) + { + return Task.FromResult(user.PasswordHash); + } + + public Task HasPassword(InMemoryUser user) + { + return Task.FromResult(user.PasswordHash != null); + } + + public Task AddToRole(InMemoryUser user, string role) + { + user.Roles.Add(role); + return Task.FromResult(0); + } + + public Task RemoveFromRole(InMemoryUser user, string role) + { + user.Roles.Remove(role); + return Task.FromResult(0); + } + + public Task> GetRoles(InMemoryUser user) + { + return Task.FromResult(user.Roles); + } + + public Task IsInRole(InMemoryUser user, string role) + { + return Task.FromResult(user.Roles.Contains(role)); + } + + public Task SetSecurityStamp(InMemoryUser user, string stamp) + { + user.SecurityStamp = stamp; + return Task.FromResult(0); + } + + public Task GetSecurityStamp(InMemoryUser user) + { + return Task.FromResult(user.SecurityStamp); + } + + public Task Create(InMemoryUser user) + { + _users[user.Id] = user; + return Task.FromResult(0); + } + + public Task Update(InMemoryUser user) + { + _users[user.Id] = user; + return Task.FromResult(0); + } + + public Task FindById(string userId) + { + if (_users.ContainsKey(userId)) + { + return Task.FromResult(_users[userId]); + } + return Task.FromResult(null); + } + + public void Dispose() + { + } + + public Task FindByName(string userName) + { + return Task.FromResult(Users.FirstOrDefault(u => String.Equals(u.UserName, userName, StringComparison.OrdinalIgnoreCase))); + } + + public Task Delete(InMemoryUser user) + { + if (user == null || !_users.ContainsKey(user.Id)) + { + throw new InvalidOperationException("Unknown user"); + } + _users.Remove(user.Id); + return Task.FromResult(0); + } + + public Task SetEmail(InMemoryUser user, string email) + { + user.Email = email; + return Task.FromResult(0); + } + + public Task GetEmail(InMemoryUser user) + { + return Task.FromResult(user.Email); + } + + public Task GetEmailConfirmed(InMemoryUser user) + { + return Task.FromResult(user.EmailConfirmed); + } + + public Task SetEmailConfirmed(InMemoryUser user, bool confirmed) + { + user.EmailConfirmed = confirmed; + return Task.FromResult(0); + } + + public Task FindByEmail(string email) + { + return Task.FromResult(Users.FirstOrDefault(u => String.Equals(u.Email, email, StringComparison.OrdinalIgnoreCase))); + } + + public Task GetLockoutEndDate(InMemoryUser user) + { + return Task.FromResult(user.LockoutEnd); + } + + public Task SetLockoutEndDate(InMemoryUser user, DateTimeOffset lockoutEnd) + { + user.LockoutEnd = lockoutEnd; + return Task.FromResult(0); + } + + public Task IncrementAccessFailedCount(InMemoryUser user) + { + user.AccessFailedCount++; + return Task.FromResult(user.AccessFailedCount); + } + + public Task ResetAccessFailedCount(InMemoryUser user) + { + user.AccessFailedCount = 0; + return Task.FromResult(0); + } + + public Task GetAccessFailedCount(InMemoryUser user) + { + return Task.FromResult(user.AccessFailedCount); + } + + public Task GetLockoutEnabled(InMemoryUser user) + { + return Task.FromResult(user.LockoutEnabled); + } + + public Task SetLockoutEnabled(InMemoryUser user, bool enabled) + { + user.LockoutEnabled = enabled; + return Task.FromResult(0); + } + + public Task SetPhoneNumber(InMemoryUser user, string phoneNumber) + { + user.PhoneNumber = phoneNumber; + return Task.FromResult(0); + } + + public Task GetPhoneNumber(InMemoryUser user) + { + return Task.FromResult(user.PhoneNumber); + } + + public Task GetPhoneNumberConfirmed(InMemoryUser user) + { + return Task.FromResult(user.PhoneNumberConfirmed); + } + + public Task SetPhoneNumberConfirmed(InMemoryUser user, bool confirmed) + { + user.PhoneNumberConfirmed = confirmed; + return Task.FromResult(0); + } + + private class LoginComparer : IEqualityComparer + { + public bool Equals(UserLoginInfo x, UserLoginInfo y) + { + return x.LoginProvider == y.LoginProvider && x.ProviderKey == y.ProviderKey; + } + + public int GetHashCode(UserLoginInfo obj) + { + return (obj.ProviderKey + "--" + obj.LoginProvider).GetHashCode(); + } + } + + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity.InMemory/project.json b/src/Microsoft.AspNet.Identity.InMemory/project.json new file mode 100644 index 0000000000..1927908a1d --- /dev/null +++ b/src/Microsoft.AspNet.Identity.InMemory/project.json @@ -0,0 +1,11 @@ +{ + "version" : "0.1-alpha-*", + "dependencies": { + "Microsoft.AspNet.Identity" : "0.1-alpha-*", + "System.Security.Claims" : "0.1-alpha-*" + }, + "configurations": { + "net45": {}, + "k10": {} + } +} \ No newline at end of file