From 523873616a229b680a7c0433eb2e63861a9db080 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Tue, 6 Sep 2016 23:16:03 -0400 Subject: [PATCH 01/56] Move to dotnet core --- .gitignore | 4 + .../AspNet.Identity.MongoDB.csproj | 80 ------------------- .../Properties/AssemblyInfo.cs | 36 --------- src/AspNet.Identity.MongoDB/packages.config | 7 -- .../AspNet.Identity.MongoDB.nuspec | 0 .../IdentityRole.cs | 0 .../IdentityUser.cs | 0 .../IdentityUserClaim.cs | 0 .../IndexChecks.cs | 0 ...icrosoft.AspNetCore.Identity.MongoDB.xproj | 21 +++++ .../Properties/AssemblyInfo.cs | 19 +++++ .../RoleStore.cs | 0 .../UserStore.cs | 0 .../project.json | 13 +++ 14 files changed, 57 insertions(+), 123 deletions(-) delete mode 100644 src/AspNet.Identity.MongoDB/AspNet.Identity.MongoDB.csproj delete mode 100644 src/AspNet.Identity.MongoDB/Properties/AssemblyInfo.cs delete mode 100644 src/AspNet.Identity.MongoDB/packages.config rename src/{AspNet.Identity.MongoDB => Microsoft.AspNetCore.Identity.MongoDB}/AspNet.Identity.MongoDB.nuspec (100%) rename src/{AspNet.Identity.MongoDB => Microsoft.AspNetCore.Identity.MongoDB}/IdentityRole.cs (100%) rename src/{AspNet.Identity.MongoDB => Microsoft.AspNetCore.Identity.MongoDB}/IdentityUser.cs (100%) rename src/{AspNet.Identity.MongoDB => Microsoft.AspNetCore.Identity.MongoDB}/IdentityUserClaim.cs (100%) rename src/{AspNet.Identity.MongoDB => Microsoft.AspNetCore.Identity.MongoDB}/IndexChecks.cs (100%) create mode 100644 src/Microsoft.AspNetCore.Identity.MongoDB/Microsoft.AspNetCore.Identity.MongoDB.xproj create mode 100644 src/Microsoft.AspNetCore.Identity.MongoDB/Properties/AssemblyInfo.cs rename src/{AspNet.Identity.MongoDB => Microsoft.AspNetCore.Identity.MongoDB}/RoleStore.cs (100%) rename src/{AspNet.Identity.MongoDB => Microsoft.AspNetCore.Identity.MongoDB}/UserStore.cs (100%) create mode 100644 src/Microsoft.AspNetCore.Identity.MongoDB/project.json diff --git a/.gitignore b/.gitignore index cdebbbc..20805f3 100644 --- a/.gitignore +++ b/.gitignore @@ -155,3 +155,7 @@ $RECYCLE.BIN/ # Mac desktop service store files .DS_Store /TestResult.xml + +project.lock.json + +.vs/ diff --git a/src/AspNet.Identity.MongoDB/AspNet.Identity.MongoDB.csproj b/src/AspNet.Identity.MongoDB/AspNet.Identity.MongoDB.csproj deleted file mode 100644 index d7761b3..0000000 --- a/src/AspNet.Identity.MongoDB/AspNet.Identity.MongoDB.csproj +++ /dev/null @@ -1,80 +0,0 @@ - - - - - Debug - AnyCPU - {AD64128A-6855-49F7-A846-1FCDAD368C55} - Library - Properties - AspNet.Identity.MongoDB - AspNet.Identity.MongoDB - v4.5 - 512 - ..\ - - - - true - full - false - ..\..\build\lib\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - ..\..\build\lib\ - TRACE - prompt - 4 - - - - False - ..\packages\Microsoft.AspNet.Identity.Core.2.2.1\lib\net45\Microsoft.AspNet.Identity.Core.dll - - - ..\packages\MongoDB.Bson.2.2.0\lib\net45\MongoDB.Bson.dll - True - - - ..\packages\MongoDB.Driver.2.2.0\lib\net45\MongoDB.Driver.dll - True - - - ..\packages\MongoDB.Driver.Core.2.2.0\lib\net45\MongoDB.Driver.Core.dll - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/AspNet.Identity.MongoDB/Properties/AssemblyInfo.cs b/src/AspNet.Identity.MongoDB/Properties/AssemblyInfo.cs deleted file mode 100644 index 17f407b..0000000 --- a/src/AspNet.Identity.MongoDB/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("AspNet.Identity.MongoDB")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("AspNet.Identity.MongoDB")] -[assembly: AssemblyCopyright("Copyright © 2014")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("95d6fb99-0451-44a0-8550-82a9b070208c")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/AspNet.Identity.MongoDB/packages.config b/src/AspNet.Identity.MongoDB/packages.config deleted file mode 100644 index d661142..0000000 --- a/src/AspNet.Identity.MongoDB/packages.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/src/AspNet.Identity.MongoDB/AspNet.Identity.MongoDB.nuspec b/src/Microsoft.AspNetCore.Identity.MongoDB/AspNet.Identity.MongoDB.nuspec similarity index 100% rename from src/AspNet.Identity.MongoDB/AspNet.Identity.MongoDB.nuspec rename to src/Microsoft.AspNetCore.Identity.MongoDB/AspNet.Identity.MongoDB.nuspec diff --git a/src/AspNet.Identity.MongoDB/IdentityRole.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs similarity index 100% rename from src/AspNet.Identity.MongoDB/IdentityRole.cs rename to src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs diff --git a/src/AspNet.Identity.MongoDB/IdentityUser.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs similarity index 100% rename from src/AspNet.Identity.MongoDB/IdentityUser.cs rename to src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs diff --git a/src/AspNet.Identity.MongoDB/IdentityUserClaim.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserClaim.cs similarity index 100% rename from src/AspNet.Identity.MongoDB/IdentityUserClaim.cs rename to src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserClaim.cs diff --git a/src/AspNet.Identity.MongoDB/IndexChecks.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IndexChecks.cs similarity index 100% rename from src/AspNet.Identity.MongoDB/IndexChecks.cs rename to src/Microsoft.AspNetCore.Identity.MongoDB/IndexChecks.cs diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/Microsoft.AspNetCore.Identity.MongoDB.xproj b/src/Microsoft.AspNetCore.Identity.MongoDB/Microsoft.AspNetCore.Identity.MongoDB.xproj new file mode 100644 index 0000000..fdfda80 --- /dev/null +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/Microsoft.AspNetCore.Identity.MongoDB.xproj @@ -0,0 +1,21 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + 6dff5058-e107-459e-87c3-da41b2c1463c + Microsoft.AspNetCore.Identity.MongoDB + .\obj + .\bin\ + v4.6 + + + + 2.0 + + + diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/Properties/AssemblyInfo.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..066aa19 --- /dev/null +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Microsoft.AspNetCore.Identity.MongoDB")] +[assembly: AssemblyTrademark("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("6dff5058-e107-459e-87c3-da41b2c1463c")] diff --git a/src/AspNet.Identity.MongoDB/RoleStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs similarity index 100% rename from src/AspNet.Identity.MongoDB/RoleStore.cs rename to src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs diff --git a/src/AspNet.Identity.MongoDB/UserStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs similarity index 100% rename from src/AspNet.Identity.MongoDB/UserStore.cs rename to src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/project.json b/src/Microsoft.AspNetCore.Identity.MongoDB/project.json new file mode 100644 index 0000000..864b9a5 --- /dev/null +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/project.json @@ -0,0 +1,13 @@ +{ + "version": "1.0.0-*", + + "dependencies": { + "NETStandard.Library": "1.6.0" + }, + + "frameworks": { + "netstandard1.6": { + "imports": "dnxcore50" + } + } +} From c740ede6b02594e40fc33efb27e8e8461c3897ba Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Tue, 6 Sep 2016 23:19:06 -0400 Subject: [PATCH 02/56] Update R# version --- src/AspNet.Identity.MongoDB.sln.DotSettings | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/AspNet.Identity.MongoDB.sln.DotSettings b/src/AspNet.Identity.MongoDB.sln.DotSettings index ddaa17f..90b3393 100644 --- a/src/AspNet.Identity.MongoDB.sln.DotSettings +++ b/src/AspNet.Identity.MongoDB.sln.DotSettings @@ -6,4 +6,6 @@ True <?xml version="1.0" encoding="utf-16"?><Profile name="Shared"><AspOptimizeRegisterDirectives>True</AspOptimizeRegisterDirectives><HtmlReformatCode>True</HtmlReformatCode><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSUseVar><BehavourStyle>CAN_CHANGE_TO_IMPLICIT</BehavourStyle><LocalVariableStyle>ALWAYS_IMPLICIT</LocalVariableStyle><ForeachVariableStyle>ALWAYS_IMPLICIT</ForeachVariableStyle></CSUseVar><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSharpFormatDocComments>True</CSharpFormatDocComments><CSReformatCode>True</CSReformatCode><VBOptimizeImports>True</VBOptimizeImports><VBShortenReferences>True</VBShortenReferences><VBReformatCode>True</VBReformatCode><VBFormatDocComments>True</VBFormatDocComments><JsInsertSemicolon>True</JsInsertSemicolon><JsReformatCode>True</JsReformatCode><JsFormatDocComments>True</JsFormatDocComments><CssReformatCode>True</CssReformatCode><XMLReformatCode>True</XMLReformatCode></Profile> Shared - True \ No newline at end of file + True + True + True \ No newline at end of file From 89016264b1a63450b7b6ced090c5e1a0fac34a20 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Tue, 6 Sep 2016 23:21:55 -0400 Subject: [PATCH 03/56] Add in dependencies for MongDB 2.3 rc1 (dotnet core support) and ASP.NET Core Identity --- src/Microsoft.AspNetCore.Identity.MongoDB/project.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/project.json b/src/Microsoft.AspNetCore.Identity.MongoDB/project.json index 864b9a5..a90cc1a 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/project.json +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/project.json @@ -1,7 +1,9 @@ -{ +{ "version": "1.0.0-*", "dependencies": { + "Microsoft.AspNetCore.Identity": "1.0.0", + "MongoDB.Driver": "2.3.0-rc1", "NETStandard.Library": "1.6.0" }, From 5f99bace796c4fa17a7d20fec3e79e6bb2d8634e Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Wed, 7 Sep 2016 01:07:43 -0400 Subject: [PATCH 04/56] Reworking for ASP.NET Core Identity --- .../IdentityRole.cs | 8 +- .../IdentityUser.cs | 66 +++- .../IdentityUserClaim.cs | 2 +- .../IdentityUserToken.cs | 11 + .../IndexChecks.cs | 2 +- .../RoleStore.cs | 37 ++- .../UserStore.cs | 288 ++++++++++-------- 7 files changed, 264 insertions(+), 150 deletions(-) create mode 100644 src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserToken.cs diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs index fee22ec..2df2016 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs @@ -1,10 +1,9 @@ -namespace AspNet.Identity.MongoDB +namespace Microsoft.AspNetCore.Identity.MongoDB { using global::MongoDB.Bson; using global::MongoDB.Bson.Serialization.Attributes; - using Microsoft.AspNet.Identity; - public class IdentityRole : IRole + public class IdentityRole { public IdentityRole() { @@ -20,5 +19,8 @@ public IdentityRole(string roleName) : this() public string Id { get; private set; } public string Name { get; set; } + + // todo + public string NormalizedName { get; set; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs index fae52c8..232f59e 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs @@ -1,4 +1,4 @@ -namespace AspNet.Identity.MongoDB +namespace Microsoft.AspNetCore.Identity.MongoDB { using System; using System.Collections.Generic; @@ -6,9 +6,8 @@ using System.Security.Claims; using global::MongoDB.Bson; using global::MongoDB.Bson.Serialization.Attributes; - using Microsoft.AspNet.Identity; - public class IdentityUser : IUser + public class IdentityUser { public IdentityUser() { @@ -16,6 +15,8 @@ public IdentityUser() Roles = new List(); Logins = new List(); Claims = new List(); + // todo testing token init + Tokens = new List(); } [BsonRepresentation(BsonType.ObjectId)] @@ -23,6 +24,10 @@ public IdentityUser() public string UserName { get; set; } + // todo what should we do with this in Mongo land + // https://github.com/aspnet/Identity/issues/351 + public virtual string NormalizedUserName { get; set; } + public virtual string SecurityStamp { get; set; } public virtual string Email { get; set; } @@ -35,7 +40,7 @@ public IdentityUser() public virtual bool TwoFactorEnabled { get; set; } - public virtual DateTime? LockoutEndDateUtc { get; set; } + public virtual DateTimeOffset? LockoutEndDateUtc { get; set; } public virtual bool LockoutEnabled { get; set; } @@ -65,11 +70,11 @@ public virtual void AddLogin(UserLoginInfo login) Logins.Add(login); } - public virtual void RemoveLogin(UserLoginInfo login) + public virtual void RemoveLogin(string loginProvider, string providerKey) { var loginsToRemove = Logins - .Where(l => l.LoginProvider == login.LoginProvider) - .Where(l => l.ProviderKey == login.ProviderKey); + .Where(l => l.LoginProvider == loginProvider) + .Where(l => l.ProviderKey == providerKey); Logins = Logins.Except(loginsToRemove).ToList(); } @@ -95,5 +100,52 @@ public virtual void RemoveClaim(Claim claim) Claims = Claims.Except(claimsToRemove).ToList(); } + + public void ReplaceClaim(Claim claim, Claim newClaim) + { + var current = Claims + .FirstOrDefault(c => c.Type == claim.Type && c.Value == claim.Value); + if (current == null) + { + //todo? + return; + } + RemoveClaim(claim); + AddClaim(newClaim); + } + + [BsonIgnoreIfNull] + public List Tokens { get; set; } + + private IdentityUserToken GetToken(string loginProider, string name) + => Tokens.FirstOrDefault(t => t.LoginProvider == loginProider && t.Name == name); + + // todo testing of tokens + public virtual void SetToken(string loginProider, string name, string value) + { + var existingToken = GetToken(loginProider, name); + if (existingToken != null) + { + existingToken.Value = value; + return; + } + + Tokens.Add(new IdentityUserToken + { + LoginProvider = loginProider, + Name = name, + Value = value + }); + } + + public virtual string GetTokenValue(string loginProider, string name) + { + return GetToken(loginProider, name)?.Value; + } + + public virtual void RemoveToken(string loginProvider, string name) + { + Tokens.RemoveAll(t => t.LoginProvider == loginProvider && t.Name == name); + } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserClaim.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserClaim.cs index c2d5780..3b8be9d 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserClaim.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserClaim.cs @@ -1,4 +1,4 @@ -namespace AspNet.Identity.MongoDB +namespace Microsoft.AspNetCore.Identity.MongoDB { using System.Security.Claims; diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserToken.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserToken.cs new file mode 100644 index 0000000..dccf969 --- /dev/null +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserToken.cs @@ -0,0 +1,11 @@ +namespace Microsoft.AspNetCore.Identity.MongoDB +{ + public class IdentityUserToken + { + public string LoginProvider { get; set; } + + public string Name { get; set; } + + public string Value { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IndexChecks.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IndexChecks.cs index 2626959..813501c 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IndexChecks.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IndexChecks.cs @@ -1,4 +1,4 @@ -namespace AspNet.Identity.MongoDB +namespace Microsoft.AspNetCore.Identity.MongoDB { using global::MongoDB.Driver; diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs index c7863cb..0851e5b 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs @@ -1,9 +1,9 @@ -namespace AspNet.Identity.MongoDB +namespace Microsoft.AspNetCore.Identity.MongoDB { using System.Linq; + using System.Threading; using System.Threading.Tasks; using global::MongoDB.Driver; - using Microsoft.AspNet.Identity; /// /// Note: Deleting and updating do not modify the roles stored on a user document. If you desire this dynamic @@ -26,29 +26,42 @@ public virtual void Dispose() // no need to dispose of anything, mongodb handles connection pooling automatically } - public virtual Task CreateAsync(TRole role) + public virtual async Task CreateAsync(TRole role, CancellationToken token) { - return _Roles.InsertOneAsync(role); + await _Roles.InsertOneAsync(role, cancellationToken: token); + return IdentityResult.Success; } - public virtual Task UpdateAsync(TRole role) + public virtual async Task UpdateAsync(TRole role, CancellationToken token) { - return _Roles.ReplaceOneAsync(r => r.Id == role.Id, role); + await _Roles.ReplaceOneAsync(r => r.Id == role.Id, role, cancellationToken: token); + return IdentityResult.Success; } - public virtual Task DeleteAsync(TRole role) + public virtual async Task DeleteAsync(TRole role, CancellationToken token) { - return _Roles.DeleteOneAsync(r => r.Id == role.Id); + await _Roles.DeleteOneAsync(r => r.Id == role.Id, cancellationToken: token); + return IdentityResult.Success; } - public virtual Task FindByIdAsync(string roleId) + public virtual async Task GetRoleIdAsync(TRole role, CancellationToken cancellationToken) => role.Id; + + public virtual async Task GetRoleNameAsync(TRole role, CancellationToken cancellationToken) => role.Name; + + public virtual async Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken) => role.Name = roleName; + + public virtual async Task GetNormalizedRoleNameAsync(TRole role, CancellationToken cancellationToken) => role.NormalizedName; + + public virtual async Task SetNormalizedRoleNameAsync(TRole role, string normalizedName, CancellationToken cancellationToken) => role.NormalizedName = normalizedName; + + public virtual Task FindByIdAsync(string roleId, CancellationToken token) { - return _Roles.Find(r => r.Id == roleId).FirstOrDefaultAsync(); + return _Roles.Find(r => r.Id == roleId).FirstOrDefaultAsync(token); } - public virtual Task FindByNameAsync(string roleName) + public virtual Task FindByNameAsync(string roleName, CancellationToken token) { - return _Roles.Find(r => r.Name == roleName).FirstOrDefaultAsync(); + return _Roles.Find(r => r.Name == roleName).FirstOrDefaultAsync(token); } public virtual IQueryable Roles => _Roles.AsQueryable(); diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs index 2a36b40..6a14ce3 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs @@ -1,25 +1,30 @@ -namespace AspNet.Identity.MongoDB +namespace Microsoft.AspNetCore.Identity.MongoDB { using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; + using System.Threading; using System.Threading.Tasks; using global::MongoDB.Driver; - using Microsoft.AspNet.Identity; - - public class UserStore : IUserStore, - IUserPasswordStore, - IUserRoleStore, - IUserLoginStore, - IUserSecurityStampStore, - IUserEmailStore, - IUserClaimStore, - IUserPhoneNumberStore, - IUserTwoFactorStore, - IUserLockoutStore, - IQueryableUserStore - where TUser : IdentityUser + + /// + /// FYI as for methods with CancellationToken, unless a database op is involved the token is ignored as any in memory + /// ops to manipulate the object graph of a user, or query, are so fast that canellation is a waste of time. + /// + /// + public class UserStore : IUserPasswordStore, + IUserRoleStore, + IUserLoginStore, + IUserSecurityStampStore, + IUserEmailStore, + IUserClaimStore, + IUserPhoneNumberStore, + IUserTwoFactorStore, + IUserLockoutStore, + IQueryableUserStore, + IUserAuthenticationTokenStore + where TUser : IdentityUser { private readonly IMongoCollection _Users; @@ -33,223 +38,254 @@ public virtual void Dispose() // no need to dispose of anything, mongodb handles connection pooling automatically } - public virtual Task CreateAsync(TUser user) + public virtual async Task CreateAsync(TUser user, CancellationToken token) { - return _Users.InsertOneAsync(user); + await _Users.InsertOneAsync(user, cancellationToken: token); + return IdentityResult.Success; } - public virtual Task UpdateAsync(TUser user) + public virtual async Task UpdateAsync(TUser user, CancellationToken token) { // todo should add an optimistic concurrency check - return _Users.ReplaceOneAsync(u => u.Id == user.Id, user); + await _Users.ReplaceOneAsync(u => u.Id == user.Id, user, cancellationToken: token); + return IdentityResult.Success; } - public virtual Task DeleteAsync(TUser user) + public virtual async Task DeleteAsync(TUser user, CancellationToken token) { - return _Users.DeleteOneAsync(u => u.Id == user.Id); + await _Users.DeleteOneAsync(u => u.Id == user.Id, token); + return IdentityResult.Success; } - public virtual Task FindByIdAsync(string userId) - { - return _Users.Find(u => u.Id == userId).FirstOrDefaultAsync(); - } + // todo testing + public virtual async Task GetUserIdAsync(TUser user, CancellationToken cancellationToken) + => user.Id; - public virtual Task FindByNameAsync(string userName) - { + // todo testing + public virtual async Task GetUserNameAsync(TUser user, CancellationToken cancellationToken) + => user.UserName; + + // todo testing + public virtual async Task SetUserNameAsync(TUser user, string userName, CancellationToken cancellationToken) + => user.UserName = userName; + + // todo testing + public virtual async Task GetNormalizedUserNameAsync(TUser user, CancellationToken cancellationToken) + => user.NormalizedUserName; + + // todo testing + public virtual async Task SetNormalizedUserNameAsync(TUser user, string normalizedName, CancellationToken cancellationToken) + => user.NormalizedUserName = normalizedName; + + // todo testing + public virtual Task FindByIdAsync(string userId, CancellationToken token) + => _Users.Find(u => u.Id == userId).FirstOrDefaultAsync(token); + + public virtual Task FindByNameAsync(string userName, CancellationToken token) // todo exception on duplicates? or better to enforce unique index to ensure this - return _Users.Find(u => u.UserName == userName).FirstOrDefaultAsync(); - } + => _Users.Find(u => u.UserName == userName).FirstOrDefaultAsync(token); - public virtual Task SetPasswordHashAsync(TUser user, string passwordHash) - { - user.PasswordHash = passwordHash; - return Task.FromResult(0); - } + public virtual async Task SetPasswordHashAsync(TUser user, string passwordHash, CancellationToken token) + => user.PasswordHash = passwordHash; - public virtual Task GetPasswordHashAsync(TUser user) - { - return Task.FromResult(user.PasswordHash); - } + public virtual async Task GetPasswordHashAsync(TUser user, CancellationToken token) + => user.PasswordHash; - public virtual Task HasPasswordAsync(TUser user) - { - return Task.FromResult(user.HasPassword()); - } + public virtual async Task HasPasswordAsync(TUser user, CancellationToken token) + => user.HasPassword(); - public virtual Task AddToRoleAsync(TUser user, string roleName) - { - user.AddRole(roleName); - return Task.FromResult(0); - } + public virtual async Task AddToRoleAsync(TUser user, string roleName, CancellationToken token) + => user.AddRole(roleName); - public virtual Task RemoveFromRoleAsync(TUser user, string roleName) - { - user.RemoveRole(roleName); - return Task.FromResult(0); - } + public virtual async Task RemoveFromRoleAsync(TUser user, string roleName, CancellationToken token) + => user.RemoveRole(roleName); - public virtual Task> GetRolesAsync(TUser user) - { - return Task.FromResult((IList) user.Roles); - } + public virtual Task> GetRolesAsync(TUser user, CancellationToken token) + => Task.FromResult((IList) user.Roles); - public virtual Task IsInRoleAsync(TUser user, string roleName) - { - return Task.FromResult(user.Roles.Contains(roleName)); - } + public virtual Task IsInRoleAsync(TUser user, string roleName, CancellationToken token) + => Task.FromResult(user.Roles.Contains(roleName)); - public virtual Task AddLoginAsync(TUser user, UserLoginInfo login) - { - user.AddLogin(login); - return Task.FromResult(0); - } + // todo testing + public virtual async Task> GetUsersInRoleAsync(string roleName, CancellationToken token) + => await _Users.Find(u => u.Roles.Contains(roleName)) + .ToListAsync(token); - public virtual Task RemoveLoginAsync(TUser user, UserLoginInfo login) - { - user.RemoveLogin(login); - return Task.FromResult(0); - } + public virtual async Task AddLoginAsync(TUser user, UserLoginInfo login, CancellationToken token) + => user.AddLogin(login); - public virtual Task> GetLoginsAsync(TUser user) - { - return Task.FromResult((IList) user.Logins); - } + public virtual async Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken)) + => user.RemoveLogin(loginProvider, providerKey); - public virtual Task FindAsync(UserLoginInfo login) - { - return _Users - .Find(u => u.Logins.Any(l => l.LoginProvider == login.LoginProvider && l.ProviderKey == login.ProviderKey)) - .FirstOrDefaultAsync(); - } + public virtual async Task> GetLoginsAsync(TUser user, CancellationToken token) + => user.Logins; - public virtual Task SetSecurityStampAsync(TUser user, string stamp) - { - user.SecurityStamp = stamp; - return Task.FromResult(0); - } + public virtual Task FindByLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken)) + => _Users + .Find(u => u.Logins.Any(l => l.LoginProvider == loginProvider && l.ProviderKey == providerKey)) + .FirstOrDefaultAsync(cancellationToken); - public virtual Task GetSecurityStampAsync(TUser user) - { - return Task.FromResult(user.SecurityStamp); - } + public virtual async Task SetSecurityStampAsync(TUser user, string stamp, CancellationToken token) + => user.SecurityStamp = stamp; + + public virtual async Task GetSecurityStampAsync(TUser user, CancellationToken token) + => user.SecurityStamp; - public virtual Task GetEmailConfirmedAsync(TUser user) + public virtual Task GetEmailConfirmedAsync(TUser user, CancellationToken token) { return Task.FromResult(user.EmailConfirmed); } - public virtual Task SetEmailConfirmedAsync(TUser user, bool confirmed) + public virtual Task SetEmailConfirmedAsync(TUser user, bool confirmed, CancellationToken token) { user.EmailConfirmed = confirmed; return Task.FromResult(0); } - public virtual Task SetEmailAsync(TUser user, string email) + public virtual Task SetEmailAsync(TUser user, string email, CancellationToken token) { user.Email = email; return Task.FromResult(0); } - public virtual Task GetEmailAsync(TUser user) + public virtual Task GetEmailAsync(TUser user, CancellationToken token) { return Task.FromResult(user.Email); } - public virtual Task FindByEmailAsync(string email) + // todo testing + public virtual Task GetNormalizedEmailAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + // todo return Task.FromResult(user.NormalizedEmail); + return null; + } + + // todo testing + public virtual Task SetNormalizedEmailAsync(TUser user, string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken)) + { + // todo user.NormalizedEmail = normalizedEmail; + return Task.FromResult(0); + } + + public virtual Task FindByEmailAsync(string email, CancellationToken token) { // todo what if a user can have multiple accounts with the same email? return _Users.Find(u => u.Email == email).FirstOrDefaultAsync(); } - public virtual Task> GetClaimsAsync(TUser user) + public virtual async Task> GetClaimsAsync(TUser user, CancellationToken token) + => user.Claims.Select(c => c.ToSecurityClaim()).ToList(); + + public virtual Task AddClaimsAsync(TUser user, IEnumerable claims, CancellationToken token) { - return Task.FromResult((IList) user.Claims.Select(c => c.ToSecurityClaim()).ToList()); + foreach (var claim in claims) + { + user.AddClaim(claim); + } + return Task.FromResult(0); } - public virtual Task AddClaimAsync(TUser user, Claim claim) + public virtual Task RemoveClaimsAsync(TUser user, IEnumerable claims, CancellationToken token) { - user.AddClaim(claim); + foreach (var claim in claims) + { + user.RemoveClaim(claim); + } return Task.FromResult(0); } - public virtual Task RemoveClaimAsync(TUser user, Claim claim) + // todo testing + public virtual async Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken)) { - user.RemoveClaim(claim); - return Task.FromResult(0); + user.ReplaceClaim(claim, newClaim); } - public virtual Task SetPhoneNumberAsync(TUser user, string phoneNumber) + public virtual Task SetPhoneNumberAsync(TUser user, string phoneNumber, CancellationToken token) { user.PhoneNumber = phoneNumber; return Task.FromResult(0); } - public virtual Task GetPhoneNumberAsync(TUser user) + public virtual Task GetPhoneNumberAsync(TUser user, CancellationToken token) { return Task.FromResult(user.PhoneNumber); } - public virtual Task GetPhoneNumberConfirmedAsync(TUser user) + public virtual Task GetPhoneNumberConfirmedAsync(TUser user, CancellationToken token) { return Task.FromResult(user.PhoneNumberConfirmed); } - public virtual Task SetPhoneNumberConfirmedAsync(TUser user, bool confirmed) + public virtual Task SetPhoneNumberConfirmedAsync(TUser user, bool confirmed, CancellationToken token) { user.PhoneNumberConfirmed = confirmed; return Task.FromResult(0); } - public virtual Task SetTwoFactorEnabledAsync(TUser user, bool enabled) + public virtual Task SetTwoFactorEnabledAsync(TUser user, bool enabled, CancellationToken token) { user.TwoFactorEnabled = enabled; return Task.FromResult(0); } - public virtual Task GetTwoFactorEnabledAsync(TUser user) + public virtual Task GetTwoFactorEnabledAsync(TUser user, CancellationToken token) { return Task.FromResult(user.TwoFactorEnabled); } - public virtual Task GetLockoutEndDateAsync(TUser user) + public virtual async Task> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken)) + { + return await _Users + // todo integration test + .Find(u => u.Claims.Any(c => c.Type == claim.Type && c.Value == claim.Value)) + .ToListAsync(cancellationToken); + } + + public virtual Task GetLockoutEndDateAsync(TUser user, CancellationToken token) { - return Task.FromResult(user.LockoutEndDateUtc ?? new DateTimeOffset()); + // todo migration? + return Task.FromResult(user.LockoutEndDateUtc); } - public virtual Task SetLockoutEndDateAsync(TUser user, DateTimeOffset lockoutEnd) + public virtual Task SetLockoutEndDateAsync(TUser user, DateTimeOffset? lockoutEnd, CancellationToken token) { - user.LockoutEndDateUtc = new DateTime(lockoutEnd.Ticks, DateTimeKind.Utc); + user.LockoutEndDateUtc = lockoutEnd; return Task.FromResult(0); } - public virtual Task IncrementAccessFailedCountAsync(TUser user) + public virtual Task IncrementAccessFailedCountAsync(TUser user, CancellationToken token) { user.AccessFailedCount++; return Task.FromResult(user.AccessFailedCount); } - public virtual Task ResetAccessFailedCountAsync(TUser user) + public virtual Task ResetAccessFailedCountAsync(TUser user, CancellationToken token) { user.AccessFailedCount = 0; return Task.FromResult(0); } - public virtual Task GetAccessFailedCountAsync(TUser user) - { - return Task.FromResult(user.AccessFailedCount); - } + public virtual async Task GetAccessFailedCountAsync(TUser user, CancellationToken token) + => user.AccessFailedCount; - public virtual Task GetLockoutEnabledAsync(TUser user) - { - return Task.FromResult(user.LockoutEnabled); - } + public virtual async Task GetLockoutEnabledAsync(TUser user, CancellationToken token) + => user.LockoutEnabled; - public virtual Task SetLockoutEnabledAsync(TUser user, bool enabled) - { - user.LockoutEnabled = enabled; - return Task.FromResult(0); - } + public virtual async Task SetLockoutEnabledAsync(TUser user, bool enabled, CancellationToken token) + => user.LockoutEnabled = enabled; public virtual IQueryable Users => _Users.AsQueryable(); + + // todo testing + public virtual async Task SetTokenAsync(TUser user, string loginProvider, string name, string value, CancellationToken cancellationToken) + => user.SetToken(loginProvider, name, value); + + // todo testing + public virtual async Task RemoveTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken) + => user.RemoveToken(loginProvider, name); + + // todo testing + public virtual async Task GetTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken) + => user.GetTokenValue(loginProvider, name); } } \ No newline at end of file From d125cccf5b034084ea3630e07aea2c636454e54b Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Wed, 7 Sep 2016 01:16:27 -0400 Subject: [PATCH 05/56] Project updates after killing old Identity project --- src/AspNet.Identity.MongoDB.sln | 14 +++++++------- src/IntegrationTests/IntegrationTests.csproj | 4 ---- src/Tests/Tests.csproj | 6 ------ 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/src/AspNet.Identity.MongoDB.sln b/src/AspNet.Identity.MongoDB.sln index aa6d41b..39c81f8 100644 --- a/src/AspNet.Identity.MongoDB.sln +++ b/src/AspNet.Identity.MongoDB.sln @@ -1,24 +1,20 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.23107.0 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNet.Identity.MongoDB", "AspNet.Identity.MongoDB\AspNet.Identity.MongoDB.csproj", "{AD64128A-6855-49F7-A846-1FCDAD368C55}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{62483144-11D8-4ECE-990D-17B4716AFF69}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationTests", "IntegrationTests\IntegrationTests.csproj", "{2817B6BF-006D-415D-8CAB-34E14DCCBC3C}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Identity.MongoDB", "Microsoft.AspNetCore.Identity.MongoDB\Microsoft.AspNetCore.Identity.MongoDB.xproj", "{6DFF5058-E107-459E-87C3-DA41B2C1463C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {AD64128A-6855-49F7-A846-1FCDAD368C55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AD64128A-6855-49F7-A846-1FCDAD368C55}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AD64128A-6855-49F7-A846-1FCDAD368C55}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AD64128A-6855-49F7-A846-1FCDAD368C55}.Release|Any CPU.Build.0 = Release|Any CPU {62483144-11D8-4ECE-990D-17B4716AFF69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {62483144-11D8-4ECE-990D-17B4716AFF69}.Debug|Any CPU.Build.0 = Debug|Any CPU {62483144-11D8-4ECE-990D-17B4716AFF69}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -27,6 +23,10 @@ Global {2817B6BF-006D-415D-8CAB-34E14DCCBC3C}.Debug|Any CPU.Build.0 = Debug|Any CPU {2817B6BF-006D-415D-8CAB-34E14DCCBC3C}.Release|Any CPU.ActiveCfg = Release|Any CPU {2817B6BF-006D-415D-8CAB-34E14DCCBC3C}.Release|Any CPU.Build.0 = Release|Any CPU + {6DFF5058-E107-459E-87C3-DA41B2C1463C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6DFF5058-E107-459E-87C3-DA41B2C1463C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6DFF5058-E107-459E-87C3-DA41B2C1463C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6DFF5058-E107-459E-87C3-DA41B2C1463C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/IntegrationTests/IntegrationTests.csproj b/src/IntegrationTests/IntegrationTests.csproj index efb2cff..7366aa5 100644 --- a/src/IntegrationTests/IntegrationTests.csproj +++ b/src/IntegrationTests/IntegrationTests.csproj @@ -88,10 +88,6 @@ - - {AD64128A-6855-49F7-A846-1FCDAD368C55} - AspNet.Identity.MongoDB - {62483144-11D8-4ECE-990D-17B4716AFF69} Tests diff --git a/src/Tests/Tests.csproj b/src/Tests/Tests.csproj index 008de45..8f84afd 100644 --- a/src/Tests/Tests.csproj +++ b/src/Tests/Tests.csproj @@ -76,12 +76,6 @@ - - - {AD64128A-6855-49F7-A846-1FCDAD368C55} - AspNet.Identity.MongoDB - - - \ No newline at end of file diff --git a/src/Tests/packages.config b/src/Tests/packages.config deleted file mode 100644 index 92ef483..0000000 --- a/src/Tests/packages.config +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file From 7870cbc7ad9cd7f6cdec01b779170bb8c6931d49 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Wed, 7 Sep 2016 01:20:32 -0400 Subject: [PATCH 08/56] Add refs to tests --- src/CoreTests/project.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/CoreTests/project.json b/src/CoreTests/project.json index 602952a..3120d53 100644 --- a/src/CoreTests/project.json +++ b/src/CoreTests/project.json @@ -4,6 +4,8 @@ "testRunner": "nunit", "dependencies": { + "Microsoft.AspNetCore.Identity": "1.0.0", + "MongoDB.Driver": "2.3.0-rc1", "NETStandard.Library": "1.6.0" }, From 3caa9ba518b64531f240512c96eb932833d27e99 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Wed, 7 Sep 2016 01:26:17 -0400 Subject: [PATCH 09/56] Getting tests working --- src/CoreTests/IdentityRoleTests.cs | 2 +- src/CoreTests/IdentityUserClaimTests.cs | 2 +- src/CoreTests/IdentityUserTests.cs | 3 +-- src/CoreTests/UserHelpers.cs | 4 ++-- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/CoreTests/IdentityRoleTests.cs b/src/CoreTests/IdentityRoleTests.cs index 9c21a52..b387892 100644 --- a/src/CoreTests/IdentityRoleTests.cs +++ b/src/CoreTests/IdentityRoleTests.cs @@ -1,6 +1,6 @@ namespace Tests { - using AspNet.Identity.MongoDB; + using Microsoft.AspNetCore.Identity.MongoDB; using MongoDB.Bson; using NUnit.Framework; diff --git a/src/CoreTests/IdentityUserClaimTests.cs b/src/CoreTests/IdentityUserClaimTests.cs index f0123c1..9dd1820 100644 --- a/src/CoreTests/IdentityUserClaimTests.cs +++ b/src/CoreTests/IdentityUserClaimTests.cs @@ -1,7 +1,7 @@ namespace Tests { using System.Security.Claims; - using AspNet.Identity.MongoDB; + using Microsoft.AspNetCore.Identity.MongoDB; using NUnit.Framework; [TestFixture] diff --git a/src/CoreTests/IdentityUserTests.cs b/src/CoreTests/IdentityUserTests.cs index 97ba772..1b76b41 100644 --- a/src/CoreTests/IdentityUserTests.cs +++ b/src/CoreTests/IdentityUserTests.cs @@ -1,7 +1,6 @@ namespace Tests { - using System; - using AspNet.Identity.MongoDB; + using Microsoft.AspNetCore.Identity.MongoDB; using MongoDB.Bson; using NUnit.Framework; diff --git a/src/CoreTests/UserHelpers.cs b/src/CoreTests/UserHelpers.cs index 319ec14..dbadb22 100644 --- a/src/CoreTests/UserHelpers.cs +++ b/src/CoreTests/UserHelpers.cs @@ -1,13 +1,13 @@ namespace Tests { - using ReflectionMagic; + //using ReflectionMagic; public static class UserHelpers { public static void SetId(this object instance, object value) { // note: nice to keep reflection code isolated in one place - instance.AsDynamic().Id = value; + //instance.AsDynamic().Id = value; } } } \ No newline at end of file From b63d8ce2925c3c6a611cad297a3eb3fc9f7b9d0c Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Wed, 7 Sep 2016 01:33:01 -0400 Subject: [PATCH 10/56] Get test project to compile --- src/AspNet.Identity.MongoDB.sln | 12 ++++++------ src/CoreTests/project.json | 7 ++++--- src/IntegrationTests/IntegrationTests.csproj | 6 ------ 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/AspNet.Identity.MongoDB.sln b/src/AspNet.Identity.MongoDB.sln index 39c81f8..f7990b9 100644 --- a/src/AspNet.Identity.MongoDB.sln +++ b/src/AspNet.Identity.MongoDB.sln @@ -3,22 +3,18 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{62483144-11D8-4ECE-990D-17B4716AFF69}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IntegrationTests", "IntegrationTests\IntegrationTests.csproj", "{2817B6BF-006D-415D-8CAB-34E14DCCBC3C}" EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Identity.MongoDB", "Microsoft.AspNetCore.Identity.MongoDB\Microsoft.AspNetCore.Identity.MongoDB.xproj", "{6DFF5058-E107-459E-87C3-DA41B2C1463C}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "CoreTests", "CoreTests\CoreTests.xproj", "{EAC53866-6DBF-40B7-900A-22267FD0634A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {62483144-11D8-4ECE-990D-17B4716AFF69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {62483144-11D8-4ECE-990D-17B4716AFF69}.Debug|Any CPU.Build.0 = Debug|Any CPU - {62483144-11D8-4ECE-990D-17B4716AFF69}.Release|Any CPU.ActiveCfg = Release|Any CPU - {62483144-11D8-4ECE-990D-17B4716AFF69}.Release|Any CPU.Build.0 = Release|Any CPU {2817B6BF-006D-415D-8CAB-34E14DCCBC3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2817B6BF-006D-415D-8CAB-34E14DCCBC3C}.Debug|Any CPU.Build.0 = Debug|Any CPU {2817B6BF-006D-415D-8CAB-34E14DCCBC3C}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -27,6 +23,10 @@ Global {6DFF5058-E107-459E-87C3-DA41B2C1463C}.Debug|Any CPU.Build.0 = Debug|Any CPU {6DFF5058-E107-459E-87C3-DA41B2C1463C}.Release|Any CPU.ActiveCfg = Release|Any CPU {6DFF5058-E107-459E-87C3-DA41B2C1463C}.Release|Any CPU.Build.0 = Release|Any CPU + {EAC53866-6DBF-40B7-900A-22267FD0634A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EAC53866-6DBF-40B7-900A-22267FD0634A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EAC53866-6DBF-40B7-900A-22267FD0634A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EAC53866-6DBF-40B7-900A-22267FD0634A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/CoreTests/project.json b/src/CoreTests/project.json index 3120d53..b062202 100644 --- a/src/CoreTests/project.json +++ b/src/CoreTests/project.json @@ -1,12 +1,13 @@ -{ +{ "version": "1.0.0-*", - "testRunner": "nunit", "dependencies": { "Microsoft.AspNetCore.Identity": "1.0.0", + "Microsoft.AspNetCore.Identity.MongoDB": "1.0.0-*", "MongoDB.Driver": "2.3.0-rc1", - "NETStandard.Library": "1.6.0" + "NETStandard.Library": "1.6.0", + "NUnit": "3.4.1" }, "frameworks": { diff --git a/src/IntegrationTests/IntegrationTests.csproj b/src/IntegrationTests/IntegrationTests.csproj index 7366aa5..af07b57 100644 --- a/src/IntegrationTests/IntegrationTests.csproj +++ b/src/IntegrationTests/IntegrationTests.csproj @@ -87,12 +87,6 @@ - - - {62483144-11D8-4ECE-990D-17B4716AFF69} - Tests - - - \ No newline at end of file diff --git a/src/IntegrationTests/Properties/AssemblyInfo.cs b/src/IntegrationTests/Properties/AssemblyInfo.cs deleted file mode 100644 index 5607e35..0000000 --- a/src/IntegrationTests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("IntegrationTests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("IntegrationTests")] -[assembly: AssemblyCopyright("Copyright © 2014")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("c09b5202-c583-46be-b9ee-b514c8fc102b")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/IntegrationTests/packages.config b/src/IntegrationTests/packages.config deleted file mode 100644 index 92ef483..0000000 --- a/src/IntegrationTests/packages.config +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file From c9119570ce57a1d74aea3253f2d4dae13d0ffd55 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Wed, 7 Sep 2016 13:36:10 -0400 Subject: [PATCH 15/56] Get extended role tests working - first set of integration tests - figure out DI for testing --- .../CoreIntegrationTests.xproj | 7 +-- .../EnsureWeCanExtendIdentityRoleTests.cs | 21 ++++---- .../UserIntegrationTestsBase.cs | 54 ++++++++++++++++--- src/CoreIntegrationTests/project.json | 3 ++ 4 files changed, 65 insertions(+), 20 deletions(-) diff --git a/src/CoreIntegrationTests/CoreIntegrationTests.xproj b/src/CoreIntegrationTests/CoreIntegrationTests.xproj index 3d93673..7737bec 100644 --- a/src/CoreIntegrationTests/CoreIntegrationTests.xproj +++ b/src/CoreIntegrationTests/CoreIntegrationTests.xproj @@ -4,7 +4,6 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - 836f4635-9b6b-4090-8cdb-9cd9f7bea829 @@ -13,9 +12,11 @@ .\bin\ v4.6 - 2.0 + + + - + \ No newline at end of file diff --git a/src/CoreIntegrationTests/EnsureWeCanExtendIdentityRoleTests.cs b/src/CoreIntegrationTests/EnsureWeCanExtendIdentityRoleTests.cs index d6baaac..24104e4 100644 --- a/src/CoreIntegrationTests/EnsureWeCanExtendIdentityRoleTests.cs +++ b/src/CoreIntegrationTests/EnsureWeCanExtendIdentityRoleTests.cs @@ -1,8 +1,10 @@ namespace IntegrationTests { using System.Linq; - using AspNet.Identity.MongoDB; - using Microsoft.AspNet.Identity; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Identity; + using Microsoft.AspNetCore.Identity.MongoDB; + using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; [TestFixture] @@ -19,9 +21,8 @@ public class ExtendedIdentityRole : IdentityRole [SetUp] public void BeforeEachTestAfterBase() { - var roles = DatabaseNewApi.GetCollection("roles"); - var roleStore = new RoleStore(roles); - _Manager = new RoleManager(roleStore); + _Manager = CreateServiceProvider() + .GetService>(); _Role = new ExtendedIdentityRole { Name = "admin" @@ -29,24 +30,24 @@ public void BeforeEachTestAfterBase() } [Test] - public void Create_ExtendedRoleType_SavesExtraFields() + public async Task Create_ExtendedRoleType_SavesExtraFields() { _Role.ExtendedField = "extendedField"; - _Manager.Create(_Role); + await _Manager.CreateAsync(_Role); var savedRole = Roles.FindAllAs().Single(); Expect(savedRole.ExtendedField, Is.EqualTo("extendedField")); } [Test] - public void Create_ExtendedRoleType_ReadsExtraFields() + public async Task Create_ExtendedRoleType_ReadsExtraFields() { _Role.ExtendedField = "extendedField"; - _Manager.Create(_Role); + await _Manager.CreateAsync(_Role); - var savedRole = _Manager.FindById(_Role.Id); + var savedRole = await _Manager.FindByIdAsync(_Role.Id); Expect(savedRole.ExtendedField, Is.EqualTo("extendedField")); } } diff --git a/src/CoreIntegrationTests/UserIntegrationTestsBase.cs b/src/CoreIntegrationTests/UserIntegrationTestsBase.cs index ee8f471..202c5ac 100644 --- a/src/CoreIntegrationTests/UserIntegrationTestsBase.cs +++ b/src/CoreIntegrationTests/UserIntegrationTestsBase.cs @@ -1,7 +1,10 @@ namespace IntegrationTests { - using AspNet.Identity.MongoDB; - using Microsoft.AspNet.Identity; + using System; + using Microsoft.AspNetCore.Identity; + using Microsoft.AspNetCore.Identity.MongoDB; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Logging; using MongoDB.Driver; using NUnit.Framework; @@ -15,6 +18,7 @@ public class UserIntegrationTestsBase : AssertionHelper protected IMongoDatabase DatabaseNewApi; private IMongoCollection _UsersNewApi; private IMongoCollection _RolesNewApi; + protected IServiceProvider ServiceProvider; [SetUp] public void BeforeEachTest() @@ -32,18 +36,54 @@ public void BeforeEachTest() Database.DropCollection("users"); Database.DropCollection("roles"); + + ServiceProvider = CreateServiceProvider(); } protected UserManager GetUserManager() + => ServiceProvider.GetService>(); + + protected RoleManager GetRoleManager() + => ServiceProvider.GetService>(); + + protected IServiceProvider CreateServiceProvider() + where TUser : IdentityUser + where TRole : IdentityRole { - var store = new UserStore(_UsersNewApi); - return new UserManager(store); + var services = new ServiceCollection(); + services.AddIdentity() + .AddDefaultTokenProviders(); + + var roles = DatabaseNewApi.GetCollection("roles"); + var roleStore = new RoleStore(roles); + services.AddSingleton>(roleStore); + + var users = DatabaseNewApi.GetCollection("users"); + var userStore = new UserStore(users); + services.AddSingleton>(userStore); + + services.AddLogging(); + services.AddSingleton>>(new TestLogger>()); + services.AddSingleton>>(new TestLogger>()); + + return services.BuildServiceProvider(); } - protected RoleManager GetRoleManager() + public class TestLogger : ILogger { - var store = new RoleStore(_RolesNewApi); - return new RoleManager(store); + public IDisposable BeginScope(TState state) + { + return null; + } + + public bool IsEnabled(LogLevel logLevel) + { + return true; + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + } } } } \ No newline at end of file diff --git a/src/CoreIntegrationTests/project.json b/src/CoreIntegrationTests/project.json index fe4f65d..db5d9fc 100644 --- a/src/CoreIntegrationTests/project.json +++ b/src/CoreIntegrationTests/project.json @@ -7,6 +7,9 @@ "dotnet-test-nunit": "3.4.0-beta-2", "Microsoft.AspNetCore.Identity": "1.0.0", "Microsoft.AspNetCore.Identity.MongoDB": "1.0.0-*", + "Microsoft.Extensions.DependencyInjection": "1.0.0", + "Microsoft.Extensions.Logging": "1.0.0", + "mongocsharpdriver": "2.3.0-rc1", "MongoDB.Driver": "2.3.0-rc1", "NETStandard.Library": "1.6.0", "NUnit": "3.4.1" From 0b866c11eca570ff2f430af4ebdd73e3b061ad82 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Wed, 7 Sep 2016 14:52:29 -0400 Subject: [PATCH 16/56] Port more integration tests - Email and name searches are now by normalized fields --- src/AspNet.Identity.MongoDB.sln | 6 ++ .../EnsureWeCanExtendIdentityUserTests.cs | 22 +++---- src/CoreIntegrationTests/IdentityUserTests.cs | 4 +- src/CoreIntegrationTests/IndexChecksTests.cs | 2 +- src/CoreIntegrationTests/RoleStoreTests.cs | 37 ++++++------ .../UserClaimStoreTests.cs | 52 ++++++++--------- .../UserEmailStoreTests.cs | 58 +++++++++---------- src/CoreIntegrationTests/project.json | 1 + src/CoreTests/CoreTests.xproj | 7 ++- .../IdentityRole.cs | 2 +- .../IdentityUser.cs | 3 +- .../RoleStore.cs | 3 +- .../UserStore.cs | 18 +++--- .../project.json | 2 +- 14 files changed, 111 insertions(+), 106 deletions(-) diff --git a/src/AspNet.Identity.MongoDB.sln b/src/AspNet.Identity.MongoDB.sln index bcac383..6712285 100644 --- a/src/AspNet.Identity.MongoDB.sln +++ b/src/AspNet.Identity.MongoDB.sln @@ -7,6 +7,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "CoreTests", "CoreTests\Core EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Identity.MongoDB", "Microsoft.AspNetCore.Identity.MongoDB\Microsoft.AspNetCore.Identity.MongoDB.xproj", "{6DFF5058-E107-459E-87C3-DA41B2C1463C}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "CoreIntegrationTests", "CoreIntegrationTests\CoreIntegrationTests.xproj", "{836F4635-9B6B-4090-8CDB-9CD9F7BEA829}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {6DFF5058-E107-459E-87C3-DA41B2C1463C}.Debug|Any CPU.Build.0 = Debug|Any CPU {6DFF5058-E107-459E-87C3-DA41B2C1463C}.Release|Any CPU.ActiveCfg = Release|Any CPU {6DFF5058-E107-459E-87C3-DA41B2C1463C}.Release|Any CPU.Build.0 = Release|Any CPU + {836F4635-9B6B-4090-8CDB-9CD9F7BEA829}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {836F4635-9B6B-4090-8CDB-9CD9F7BEA829}.Debug|Any CPU.Build.0 = Debug|Any CPU + {836F4635-9B6B-4090-8CDB-9CD9F7BEA829}.Release|Any CPU.ActiveCfg = Release|Any CPU + {836F4635-9B6B-4090-8CDB-9CD9F7BEA829}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/CoreIntegrationTests/EnsureWeCanExtendIdentityUserTests.cs b/src/CoreIntegrationTests/EnsureWeCanExtendIdentityUserTests.cs index 0675414..bc89ace 100644 --- a/src/CoreIntegrationTests/EnsureWeCanExtendIdentityUserTests.cs +++ b/src/CoreIntegrationTests/EnsureWeCanExtendIdentityUserTests.cs @@ -1,8 +1,10 @@ namespace IntegrationTests { using System.Linq; - using AspNet.Identity.MongoDB; - using Microsoft.AspNet.Identity; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Identity; + using Microsoft.AspNetCore.Identity.MongoDB; + using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; [TestFixture] @@ -19,9 +21,8 @@ public class ExtendedIdentityUser : IdentityUser [SetUp] public void BeforeEachTestAfterBase() { - var users = DatabaseNewApi.GetCollection("users"); - var userStore = new UserStore(users); - _Manager = new UserManager(userStore); + _Manager = CreateServiceProvider() + .GetService>(); _User = new ExtendedIdentityUser { UserName = "bob" @@ -29,24 +30,25 @@ public void BeforeEachTestAfterBase() } [Test] - public void Create_ExtendedUserType_SavesExtraFields() + public async Task Create_ExtendedUserType_SavesExtraFields() { _User.ExtendedField = "extendedField"; - _Manager.Create(_User); + // todo note: async methods dropped in Identity 3 + await _Manager.CreateAsync(_User); var savedUser = Users.FindAllAs().Single(); Expect(savedUser.ExtendedField, Is.EqualTo("extendedField")); } [Test] - public void Create_ExtendedUserType_ReadsExtraFields() + public async Task Create_ExtendedUserType_ReadsExtraFields() { _User.ExtendedField = "extendedField"; - _Manager.Create(_User); + await _Manager.CreateAsync(_User); - var savedUser = _Manager.FindById(_User.Id); + var savedUser = await _Manager.FindByIdAsync(_User.Id); Expect(savedUser.ExtendedField, Is.EqualTo("extendedField")); } } diff --git a/src/CoreIntegrationTests/IdentityUserTests.cs b/src/CoreIntegrationTests/IdentityUserTests.cs index 840ca5e..bbbe935 100644 --- a/src/CoreIntegrationTests/IdentityUserTests.cs +++ b/src/CoreIntegrationTests/IdentityUserTests.cs @@ -1,6 +1,6 @@ namespace IntegrationTests { - using AspNet.Identity.MongoDB; + using Microsoft.AspNetCore.Identity.MongoDB; using MongoDB.Bson; using NUnit.Framework; using Tests; @@ -12,7 +12,7 @@ public class IdentityUserTests : UserIntegrationTestsBase public void Insert_NoId_SetsId() { var user = new IdentityUser(); - user.SetId(null); + user.Id = null; Users.Insert(user); diff --git a/src/CoreIntegrationTests/IndexChecksTests.cs b/src/CoreIntegrationTests/IndexChecksTests.cs index 0b95e95..8552a1e 100644 --- a/src/CoreIntegrationTests/IndexChecksTests.cs +++ b/src/CoreIntegrationTests/IndexChecksTests.cs @@ -1,7 +1,7 @@ namespace IntegrationTests { using System.Linq; - using AspNet.Identity.MongoDB; + using Microsoft.AspNetCore.Identity.MongoDB; using NUnit.Framework; [TestFixture] diff --git a/src/CoreIntegrationTests/RoleStoreTests.cs b/src/CoreIntegrationTests/RoleStoreTests.cs index 743ce9d..b22b168 100644 --- a/src/CoreIntegrationTests/RoleStoreTests.cs +++ b/src/CoreIntegrationTests/RoleStoreTests.cs @@ -1,80 +1,79 @@ namespace IntegrationTests { using System.Linq; - using AspNet.Identity.MongoDB; - using Microsoft.AspNet.Identity; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Identity.MongoDB; using MongoDB.Bson; using NUnit.Framework; - using Tests; [TestFixture] public class RoleStoreTests : UserIntegrationTestsBase { [Test] - public void Create_NewRole_Saves() + public async Task Create_NewRole_Saves() { var roleName = "admin"; var role = new IdentityRole(roleName); var manager = GetRoleManager(); - manager.Create(role); + await manager.CreateAsync(role); var savedRole = Roles.FindAll().Single(); Expect(savedRole.Name, Is.EqualTo(roleName)); } [Test] - public void FindByName_SavedRole_ReturnsRole() + public async Task FindByName_SavedRole_ReturnsRole() { var roleName = "name"; var role = new IdentityRole {Name = roleName}; var manager = GetRoleManager(); - manager.Create(role); + await manager.CreateAsync(role); - var foundRole = manager.FindByName(roleName); + var foundRole = await manager.FindByNameAsync(roleName); Expect(foundRole, Is.Not.Null); Expect(foundRole.Name, Is.EqualTo(roleName)); } [Test] - public void FindById_SavedRole_ReturnsRole() + public async Task FindById_SavedRole_ReturnsRole() { var roleId = ObjectId.GenerateNewId().ToString(); var role = new IdentityRole {Name = "name"}; - role.SetId(roleId); + role.Id = roleId; var manager = GetRoleManager(); - manager.Create(role); + await manager.CreateAsync(role); - var foundRole = manager.FindById(roleId); + var foundRole = await manager.FindByIdAsync(roleId); Expect(foundRole, Is.Not.Null); Expect(foundRole.Id, Is.EqualTo(roleId)); } [Test] - public void Delete_ExistingRole_Removes() + public async Task Delete_ExistingRole_Removes() { var role = new IdentityRole {Name = "name"}; var manager = GetRoleManager(); - manager.Create(role); + await manager.CreateAsync(role); Expect(Roles.FindAll(), Is.Not.Empty); - manager.Delete(role); + await manager.DeleteAsync(role); Expect(Roles.FindAll(), Is.Empty); } [Test] - public void Update_ExistingRole_Updates() + public async Task Update_ExistingRole_Updates() { var role = new IdentityRole {Name = "name"}; var manager = GetRoleManager(); - manager.Create(role); - var savedRole = manager.FindById(role.Id); + await manager.CreateAsync(role); + var savedRole = await manager.FindByIdAsync(role.Id); savedRole.Name = "newname"; - manager.Update(savedRole); + await manager.UpdateAsync(savedRole); var changedRole = Roles.FindAll().Single(); Expect(changedRole, Is.Not.Null); diff --git a/src/CoreIntegrationTests/UserClaimStoreTests.cs b/src/CoreIntegrationTests/UserClaimStoreTests.cs index 27a259a..649ae96 100644 --- a/src/CoreIntegrationTests/UserClaimStoreTests.cs +++ b/src/CoreIntegrationTests/UserClaimStoreTests.cs @@ -2,76 +2,76 @@ { using System.Linq; using System.Security.Claims; - using AspNet.Identity.MongoDB; - using Microsoft.AspNet.Identity; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Identity.MongoDB; using NUnit.Framework; [TestFixture] public class UserClaimStoreTests : UserIntegrationTestsBase { [Test] - public void Create_NewUser_HasNoClaims() + public async Task Create_NewUser_HasNoClaims() { var user = new IdentityUser {UserName = "bob"}; var manager = GetUserManager(); - manager.Create(user); + await manager.CreateAsync(user); - var claims = manager.GetClaims(user.Id); + var claims = await manager.GetClaimsAsync(user); Expect(claims, Is.Empty); } [Test] - public void AddClaim_ReturnsClaim() + public async Task AddClaim_ReturnsClaim() { var user = new IdentityUser {UserName = "bob"}; var manager = GetUserManager(); - manager.Create(user); + await manager.CreateAsync(user); - manager.AddClaim(user.Id, new Claim("type", "value")); + await manager.AddClaimAsync(user, new Claim("type", "value")); - var claim = manager.GetClaims(user.Id).Single(); + var claim = (await manager.GetClaimsAsync(user)).Single(); Expect(claim.Type, Is.EqualTo("type")); Expect(claim.Value, Is.EqualTo("value")); } [Test] - public void RemoveClaim_RemovesExistingClaim() + public async Task RemoveClaim_RemovesExistingClaim() { var user = new IdentityUser {UserName = "bob"}; var manager = GetUserManager(); - manager.Create(user); - manager.AddClaim(user.Id, new Claim("type", "value")); + await manager.CreateAsync(user); + await manager.AddClaimAsync(user, new Claim("type", "value")); - manager.RemoveClaim(user.Id, new Claim("type", "value")); + await manager.RemoveClaimAsync(user, new Claim("type", "value")); - Expect(manager.GetClaims(user.Id), Is.Empty); + Expect(await manager.GetClaimsAsync(user), Is.Empty); } [Test] - public void RemoveClaim_DifferentType_DoesNotRemoveClaim() + public async Task RemoveClaim_DifferentType_DoesNotRemoveClaim() { - var user = new IdentityUser { UserName = "bob" }; + var user = new IdentityUser {UserName = "bob"}; var manager = GetUserManager(); - manager.Create(user); - manager.AddClaim(user.Id, new Claim("type", "value")); + await manager.CreateAsync(user); + await manager.AddClaimAsync(user, new Claim("type", "value")); - manager.RemoveClaim(user.Id, new Claim("otherType", "value")); + await manager.RemoveClaimAsync(user, new Claim("otherType", "value")); - Expect(manager.GetClaims(user.Id), Is.Not.Empty); + Expect(await manager.GetClaimsAsync(user), Is.Not.Empty); } [Test] - public void RemoveClaim_DifferentValue_DoesNotRemoveClaim() + public async Task RemoveClaim_DifferentValue_DoesNotRemoveClaim() { - var user = new IdentityUser { UserName = "bob" }; + var user = new IdentityUser {UserName = "bob"}; var manager = GetUserManager(); - manager.Create(user); - manager.AddClaim(user.Id, new Claim("type", "value")); + await manager.CreateAsync(user); + await manager.AddClaimAsync(user, new Claim("type", "value")); - manager.RemoveClaim(user.Id, new Claim("type", "otherValue")); + await manager.RemoveClaimAsync(user, new Claim("type", "otherValue")); - Expect(manager.GetClaims(user.Id), Is.Not.Empty); + Expect(await manager.GetClaimsAsync(user), Is.Not.Empty); } } } \ No newline at end of file diff --git a/src/CoreIntegrationTests/UserEmailStoreTests.cs b/src/CoreIntegrationTests/UserEmailStoreTests.cs index 0210819..5dc49e8 100644 --- a/src/CoreIntegrationTests/UserEmailStoreTests.cs +++ b/src/CoreIntegrationTests/UserEmailStoreTests.cs @@ -1,89 +1,87 @@ namespace IntegrationTests { - using AspNet.Identity.MongoDB; - using Microsoft.AspNet.Identity; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Identity.MongoDB; using NUnit.Framework; [TestFixture] public class UserEmailStoreTests : UserIntegrationTestsBase { [Test] - public void Create_NewUser_HasNoEmail() + public async Task Create_NewUser_HasNoEmail() { var user = new IdentityUser {UserName = "bob"}; var manager = GetUserManager(); - manager.Create(user); + await manager.CreateAsync(user); - var email = manager.GetEmail(user.Id); + var email = await manager.GetEmailAsync(user); Expect(email, Is.Null); } [Test] - public void SetEmail_SetsEmail() + public async Task SetEmail_SetsEmail() { var user = new IdentityUser {UserName = "bob"}; var manager = GetUserManager(); - manager.Create(user); + await manager.CreateAsync(user); - manager.SetEmail(user.Id, "email"); + await manager.SetEmailAsync(user, "email"); - Expect(manager.GetEmail(user.Id), Is.EqualTo("email")); + Expect(await manager.GetEmailAsync(user), Is.EqualTo("email")); } [Test] - public void FindUserByEmail_ReturnsUser() + public async Task FindUserByEmail_ReturnsUser() { var user = new IdentityUser {UserName = "bob"}; var manager = GetUserManager(); - manager.Create(user); - Expect(manager.FindByEmail("email"), Is.Null); + await manager.CreateAsync(user); + Expect(await manager.FindByEmailAsync("email"), Is.Null); - manager.SetEmail(user.Id, "email"); + await manager.SetEmailAsync(user, "email"); - Expect(manager.FindByEmail("email"), Is.Not.Null); + Expect(await manager.FindByEmailAsync("email"), Is.Not.Null); } [Test] - public void Create_NewUser_IsNotEmailConfirmed() + public async Task Create_NewUser_IsNotEmailConfirmed() { var manager = GetUserManager(); var user = new IdentityUser {UserName = "bob"}; - manager.Create(user); + await manager.CreateAsync(user); - var isConfirmed = manager.IsEmailConfirmed(user.Id); + var isConfirmed = await manager.IsEmailConfirmedAsync(user); Expect(isConfirmed, Is.False); } [Test] - public void SetEmailConfirmed_IsConfirmed() + public async Task SetEmailConfirmed_IsConfirmed() { var manager = GetUserManager(); var user = new IdentityUser {UserName = "bob"}; - manager.Create(user); - manager.UserTokenProvider = new EmailTokenProvider(); - var token = manager.GenerateEmailConfirmationToken(user.Id); + await manager.CreateAsync(user); + var token = await manager.GenerateEmailConfirmationTokenAsync(user); - manager.ConfirmEmail(user.Id, token); + await manager.ConfirmEmailAsync(user, token); - var isConfirmed = manager.IsEmailConfirmed(user.Id); + var isConfirmed = await manager.IsEmailConfirmedAsync(user); Expect(isConfirmed); } [Test] - public void ChangeEmail_AfterConfirmedOriginalEmail_NotEmailConfirmed() + public async Task ChangeEmail_AfterConfirmedOriginalEmail_NotEmailConfirmed() { var manager = GetUserManager(); var user = new IdentityUser {UserName = "bob"}; - manager.Create(user); - manager.UserTokenProvider = new EmailTokenProvider(); - var token = manager.GenerateEmailConfirmationToken(user.Id); - manager.ConfirmEmail(user.Id, token); + await manager.CreateAsync(user); + var token = await manager.GenerateEmailConfirmationTokenAsync(user); + await manager.ConfirmEmailAsync(user, token); - manager.SetEmail(user.Id, "new@email.com"); + await manager.SetEmailAsync(user, "new@email.com"); - var isConfirmed = manager.IsEmailConfirmed(user.Id); + var isConfirmed = await manager.IsEmailConfirmedAsync(user); Expect(isConfirmed, Is.False); } } diff --git a/src/CoreIntegrationTests/project.json b/src/CoreIntegrationTests/project.json index db5d9fc..b225fbc 100644 --- a/src/CoreIntegrationTests/project.json +++ b/src/CoreIntegrationTests/project.json @@ -4,6 +4,7 @@ "testRunner": "nunit", "dependencies": { + "CoreTests": "1.0.0-*", "dotnet-test-nunit": "3.4.0-beta-2", "Microsoft.AspNetCore.Identity": "1.0.0", "Microsoft.AspNetCore.Identity.MongoDB": "1.0.0-*", diff --git a/src/CoreTests/CoreTests.xproj b/src/CoreTests/CoreTests.xproj index 25285e7..08832b8 100644 --- a/src/CoreTests/CoreTests.xproj +++ b/src/CoreTests/CoreTests.xproj @@ -4,7 +4,6 @@ 14.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - eac53866-6dbf-40b7-900a-22267fd0634a @@ -13,9 +12,11 @@ .\bin\ v4.6 - 2.0 + + + - + \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs index 2df2016..e8e4c5b 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs @@ -16,7 +16,7 @@ public IdentityRole(string roleName) : this() } [BsonRepresentation(BsonType.ObjectId)] - public string Id { get; private set; } + public string Id { get; set; } public string Name { get; set; } diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs index 232f59e..4708042 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs @@ -20,7 +20,7 @@ public IdentityUser() } [BsonRepresentation(BsonType.ObjectId)] - public string Id { get; private set; } + public string Id { get; set; } public string UserName { get; set; } @@ -31,6 +31,7 @@ public IdentityUser() public virtual string SecurityStamp { get; set; } public virtual string Email { get; set; } + public virtual string NormalizedEmail { get; set; } public virtual bool EmailConfirmed { get; set; } diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs index 0851e5b..2ed7994 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs @@ -61,7 +61,8 @@ public virtual Task FindByIdAsync(string roleId, CancellationToken token) public virtual Task FindByNameAsync(string roleName, CancellationToken token) { - return _Roles.Find(r => r.Name == roleName).FirstOrDefaultAsync(token); + // todo thoughts on searching now by normalized name without changing api... + return _Roles.Find(r => r.NormalizedName == roleName).FirstOrDefaultAsync(token); } public virtual IQueryable Roles => _Roles.AsQueryable(); diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs index 6a14ce3..530ffb7 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs @@ -82,6 +82,7 @@ public virtual Task FindByIdAsync(string userId, CancellationToken token) => _Users.Find(u => u.Id == userId).FirstOrDefaultAsync(token); public virtual Task FindByNameAsync(string userName, CancellationToken token) + // todo test fails with normalized? // todo exception on duplicates? or better to enforce unique index to ensure this => _Users.Find(u => u.UserName == userName).FirstOrDefaultAsync(token); @@ -154,23 +155,18 @@ public virtual Task GetEmailAsync(TUser user, CancellationToken token) } // todo testing - public virtual Task GetNormalizedEmailAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) - { - // todo return Task.FromResult(user.NormalizedEmail); - return null; - } + public virtual async Task GetNormalizedEmailAsync(TUser user, CancellationToken cancellationToken) + => user.NormalizedEmail; // todo testing - public virtual Task SetNormalizedEmailAsync(TUser user, string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken)) - { - // todo user.NormalizedEmail = normalizedEmail; - return Task.FromResult(0); - } + public virtual async Task SetNormalizedEmailAsync(TUser user, string normalizedEmail, CancellationToken cancellationToken) + => user.NormalizedEmail = normalizedEmail; public virtual Task FindByEmailAsync(string email, CancellationToken token) { + // todo I don't like that this now searches on normalized email :(... why not FindByNormalizedEmailAsync then? // todo what if a user can have multiple accounts with the same email? - return _Users.Find(u => u.Email == email).FirstOrDefaultAsync(); + return _Users.Find(u => u.NormalizedEmail == email).FirstOrDefaultAsync(token); } public virtual async Task> GetClaimsAsync(TUser user, CancellationToken token) diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/project.json b/src/Microsoft.AspNetCore.Identity.MongoDB/project.json index a90cc1a..a511b83 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/project.json +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/project.json @@ -8,7 +8,7 @@ }, "frameworks": { - "netstandard1.6": { + "netstandard1.5": { "imports": "dnxcore50" } } From cc4797d26aaceefe398f0c6d4abb8d20825d2605 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Wed, 7 Sep 2016 16:25:52 -0400 Subject: [PATCH 17/56] Another batch - with custom lockout settings --- .../UserIntegrationTestsBase.cs | 6 +- .../UserLockoutStoreTests.cs | 83 +++++++++++-------- .../UserLoginStoreTests.cs | 65 ++++++++------- 3 files changed, 84 insertions(+), 70 deletions(-) diff --git a/src/CoreIntegrationTests/UserIntegrationTestsBase.cs b/src/CoreIntegrationTests/UserIntegrationTestsBase.cs index 202c5ac..90fe76b 100644 --- a/src/CoreIntegrationTests/UserIntegrationTestsBase.cs +++ b/src/CoreIntegrationTests/UserIntegrationTestsBase.cs @@ -1,6 +1,7 @@ namespace IntegrationTests { using System; + using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.MongoDB; using Microsoft.Extensions.DependencyInjection; @@ -46,12 +47,13 @@ protected UserManager GetUserManager() protected RoleManager GetRoleManager() => ServiceProvider.GetService>(); - protected IServiceProvider CreateServiceProvider() + protected IServiceProvider CreateServiceProvider(Action optionsProvider = null) where TUser : IdentityUser where TRole : IdentityRole { var services = new ServiceCollection(); - services.AddIdentity() + optionsProvider = optionsProvider ?? (options => { }); + services.AddIdentity(optionsProvider) .AddDefaultTokenProviders(); var roles = DatabaseNewApi.GetCollection("roles"); diff --git a/src/CoreIntegrationTests/UserLockoutStoreTests.cs b/src/CoreIntegrationTests/UserLockoutStoreTests.cs index 67ee8a5..3e45005 100644 --- a/src/CoreIntegrationTests/UserLockoutStoreTests.cs +++ b/src/CoreIntegrationTests/UserLockoutStoreTests.cs @@ -1,24 +1,33 @@ namespace IntegrationTests { using System; - using AspNet.Identity.MongoDB; - using Microsoft.AspNet.Identity; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Identity; + using Microsoft.AspNetCore.Identity.MongoDB; + using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; [TestFixture] public class UserLockoutStoreTests : UserIntegrationTestsBase { [Test] - public void AccessFailed_IncrementsAccessFailedCount() + public async Task AccessFailed_IncrementsAccessFailedCount() { - var manager = GetUserManager(); + var manager = GetUserManagerWithThreeMaxAccessAttempts(); var user = new IdentityUser {UserName = "bob"}; - manager.Create(user); - manager.MaxFailedAccessAttemptsBeforeLockout = 3; + await manager.CreateAsync(user); + + await manager.AccessFailedAsync(user); - manager.AccessFailed(user.Id); + Expect(await manager.GetAccessFailedCountAsync(user), Is.EqualTo(1)); + } - Expect(manager.GetAccessFailedCount(user.Id), Is.EqualTo(1)); + private UserManager GetUserManagerWithThreeMaxAccessAttempts() + { + // todo is this really needed to set Max Failed Attempts? + return CreateServiceProvider(options => options.Lockout.MaxFailedAccessAttempts = 3) + .GetService>(); } [Test] @@ -27,65 +36,67 @@ public void IncrementAccessFailedCount_ReturnsNewCount() var store = new UserStore(null); var user = new IdentityUser {UserName = "bob"}; - var count = store.IncrementAccessFailedCountAsync(user); + var count = store.IncrementAccessFailedCountAsync(user, default(CancellationToken)); Expect(count.Result, Is.EqualTo(1)); } [Test] - public void ResetAccessFailed_AfterAnAccessFailed_SetsToZero() + public async Task ResetAccessFailed_AfterAnAccessFailed_SetsToZero() { - var manager = GetUserManager(); + var manager = GetUserManagerWithThreeMaxAccessAttempts(); var user = new IdentityUser {UserName = "bob"}; - manager.Create(user); - manager.MaxFailedAccessAttemptsBeforeLockout = 3; - manager.AccessFailed(user.Id); + await manager.CreateAsync(user); + await manager.AccessFailedAsync(user); - manager.ResetAccessFailedCount(user.Id); + await manager.ResetAccessFailedCountAsync(user); - Expect(manager.GetAccessFailedCount(user.Id), Is.EqualTo(0)); + Expect(await manager.GetAccessFailedCountAsync(user), Is.EqualTo(0)); } [Test] - public void AccessFailed_NotOverMaxFailures_NoLockoutEndDate() + public async Task AccessFailed_NotOverMaxFailures_NoLockoutEndDate() { - var manager = GetUserManager(); + var manager = GetUserManagerWithThreeMaxAccessAttempts(); var user = new IdentityUser {UserName = "bob"}; - manager.Create(user); - manager.MaxFailedAccessAttemptsBeforeLockout = 3; + await manager.CreateAsync(user); - manager.AccessFailed(user.Id); + await manager.AccessFailedAsync(user); - Expect(manager.GetLockoutEndDate(user.Id), Is.EqualTo(DateTimeOffset.MinValue)); + Expect(await manager.GetLockoutEndDateAsync(user), Is.Null); } [Test] - public void AccessFailed_ExceedsMaxFailedAccessAttempts_LocksAccount() + public async Task AccessFailed_ExceedsMaxFailedAccessAttempts_LocksAccount() { - var manager = GetUserManager(); + var manager = CreateServiceProvider(options => + { + options.Lockout.MaxFailedAccessAttempts = 0; + options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); + }) + .GetService>(); + var user = new IdentityUser {UserName = "bob"}; - manager.Create(user); - manager.MaxFailedAccessAttemptsBeforeLockout = 0; - manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromHours(1); + await manager.CreateAsync(user); - manager.AccessFailed(user.Id); + await manager.AccessFailedAsync(user); - var lockoutEndDate = manager.GetLockoutEndDate(user.Id); - Expect(lockoutEndDate.Subtract(DateTime.UtcNow).TotalHours, Is.GreaterThan(0.9).And.LessThan(1.1)); + var lockoutEndDate = await manager.GetLockoutEndDateAsync(user); + Expect(lockoutEndDate?.Subtract(DateTime.UtcNow).TotalHours, Is.GreaterThan(0.9).And.LessThan(1.1)); } [Test] - public void SetLockoutEnabled() + public async Task SetLockoutEnabled() { var manager = GetUserManager(); var user = new IdentityUser {UserName = "bob"}; - manager.Create(user); + await manager.CreateAsync(user); - manager.SetLockoutEnabled(user.Id, true); - Expect(manager.GetLockoutEnabled(user.Id)); + await manager.SetLockoutEnabledAsync(user, true); + Expect(await manager.GetLockoutEnabledAsync(user)); - manager.SetLockoutEnabled(user.Id, false); - Expect(manager.GetLockoutEnabled(user.Id), Is.False); + await manager.SetLockoutEnabledAsync(user, false); + Expect(await manager.GetLockoutEnabledAsync(user), Is.False); } } } \ No newline at end of file diff --git a/src/CoreIntegrationTests/UserLoginStoreTests.cs b/src/CoreIntegrationTests/UserLoginStoreTests.cs index fd8f57d..aa01589 100644 --- a/src/CoreIntegrationTests/UserLoginStoreTests.cs +++ b/src/CoreIntegrationTests/UserLoginStoreTests.cs @@ -1,54 +1,55 @@ namespace IntegrationTests { using System.Linq; - using AspNet.Identity.MongoDB; - using Microsoft.AspNet.Identity; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Identity; + using Microsoft.AspNetCore.Identity.MongoDB; using NUnit.Framework; [TestFixture] public class UserLoginStoreTests : UserIntegrationTestsBase { [Test] - public void AddLogin_NewLogin_Adds() + public async Task AddLogin_NewLogin_Adds() { var manager = GetUserManager(); - var login = new UserLoginInfo("provider", "key"); + // todo what's this new displayName for userLoginInfo + var login = new UserLoginInfo("provider", "key", "name"); var user = new IdentityUser {UserName = "bob"}; - manager.Create(user); + await manager.CreateAsync(user); - manager.AddLogin(user.Id, login); + await manager.AddLoginAsync(user, login); var savedLogin = Users.FindAll().Single().Logins.Single(); Expect(savedLogin.LoginProvider, Is.EqualTo("provider")); Expect(savedLogin.ProviderKey, Is.EqualTo("key")); } - [Test] - public void RemoveLogin_NewLogin_Removes() + public async Task RemoveLogin_NewLogin_Removes() { var manager = GetUserManager(); - var login = new UserLoginInfo("provider", "key"); + var login = new UserLoginInfo("provider", "key", "name"); var user = new IdentityUser {UserName = "bob"}; - manager.Create(user); - manager.AddLogin(user.Id, login); + await manager.CreateAsync(user); + await manager.AddLoginAsync(user, login); - manager.RemoveLogin(user.Id, login); + await manager.RemoveLoginAsync(user, login.LoginProvider, login.ProviderKey); var savedUser = Users.FindAll().Single(); Expect(savedUser.Logins, Is.Empty); } [Test] - public void GetLogins_OneLogin_ReturnsLogin() + public async Task GetLogins_OneLogin_ReturnsLogin() { var manager = GetUserManager(); - var login = new UserLoginInfo("provider", "key"); + var login = new UserLoginInfo("provider", "key", "name"); var user = new IdentityUser {UserName = "bob"}; - manager.Create(user); - manager.AddLogin(user.Id, login); + await manager.CreateAsync(user); + await manager.AddLoginAsync(user, login); - var logins = manager.GetLogins(user.Id); + var logins = await manager.GetLoginsAsync(user); var savedLogin = logins.Single(); Expect(savedLogin.LoginProvider, Is.EqualTo("provider")); @@ -56,43 +57,43 @@ public void GetLogins_OneLogin_ReturnsLogin() } [Test] - public void Find_UserWithLogin_FindsUser() + public async Task Find_UserWithLogin_FindsUser() { var manager = GetUserManager(); - var login = new UserLoginInfo("provider", "key"); + var login = new UserLoginInfo("provider", "key", "name"); var user = new IdentityUser {UserName = "bob"}; - manager.Create(user); - manager.AddLogin(user.Id, login); + await manager.CreateAsync(user); + await manager.AddLoginAsync(user, login); - var findUser = manager.Find(login); + var findUser = manager.FindByLoginAsync(login.LoginProvider, login.ProviderKey); Expect(findUser, Is.Not.Null); } [Test] - public void Find_UserWithDifferentKey_DoesNotFindUser() + public async Task Find_UserWithDifferentKey_DoesNotFindUser() { var manager = GetUserManager(); - var login = new UserLoginInfo("provider", "key"); + var login = new UserLoginInfo("provider", "key", "name"); var user = new IdentityUser {UserName = "bob"}; - manager.Create(user); - manager.AddLogin(user.Id, login); + await manager.CreateAsync(user); + await manager.AddLoginAsync(user, login); - var findUser = manager.Find(new UserLoginInfo("provider", "otherkey")); + var findUser = await manager.FindByLoginAsync("provider", "otherkey"); Expect(findUser, Is.Null); } [Test] - public void Find_UserWithDifferentProvider_DoesNotFindUser() + public async Task Find_UserWithDifferentProvider_DoesNotFindUser() { var manager = GetUserManager(); - var login = new UserLoginInfo("provider", "key"); + var login = new UserLoginInfo("provider", "key", "name"); var user = new IdentityUser {UserName = "bob"}; - manager.Create(user); - manager.AddLogin(user.Id, login); + await manager.CreateAsync(user); + await manager.AddLoginAsync(user, login); - var findUser = manager.Find(new UserLoginInfo("otherprovider", "key")); + var findUser = await manager.FindByLoginAsync("otherprovider", "key"); Expect(findUser, Is.Null); } From 7e42dbbd71063a70d55a8d77a1a9af92ffd9a3c1 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Wed, 7 Sep 2016 17:05:45 -0400 Subject: [PATCH 18/56] Cleanup --- .../RoleStore.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs index 2ed7994..8348c9a 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs @@ -44,15 +44,20 @@ public virtual async Task DeleteAsync(TRole role, CancellationTo return IdentityResult.Success; } - public virtual async Task GetRoleIdAsync(TRole role, CancellationToken cancellationToken) => role.Id; + public virtual async Task GetRoleIdAsync(TRole role, CancellationToken cancellationToken) + => role.Id; - public virtual async Task GetRoleNameAsync(TRole role, CancellationToken cancellationToken) => role.Name; + public virtual async Task GetRoleNameAsync(TRole role, CancellationToken cancellationToken) + => role.Name; - public virtual async Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken) => role.Name = roleName; + public virtual async Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken) + => role.Name = roleName; - public virtual async Task GetNormalizedRoleNameAsync(TRole role, CancellationToken cancellationToken) => role.NormalizedName; + public virtual async Task GetNormalizedRoleNameAsync(TRole role, CancellationToken cancellationToken) + => role.NormalizedName; - public virtual async Task SetNormalizedRoleNameAsync(TRole role, string normalizedName, CancellationToken cancellationToken) => role.NormalizedName = normalizedName; + public virtual async Task SetNormalizedRoleNameAsync(TRole role, string normalizedName, CancellationToken cancellationToken) + => role.NormalizedName = normalizedName; public virtual Task FindByIdAsync(string roleId, CancellationToken token) { From c8f2a4ef3aa32eef979449fbfb4edd7702dd16d7 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Wed, 7 Sep 2016 17:06:39 -0400 Subject: [PATCH 19/56] Get more tests working - fix user by name to use Normalized Name --- .../UserPasswordStoreTests.cs | 45 ++++++++------ .../UserPhoneNumberStoreTests.cs | 34 +++++------ .../UserRoleStoreTests.cs | 32 +++++----- .../UserSecurityStampStoreTests.cs | 14 ++--- src/CoreIntegrationTests/UserStoreTests.cs | 45 +++++++------- .../UserTwoFactorStoreTests.cs | 22 +++---- .../UserStore.cs | 58 ++++++++----------- 7 files changed, 126 insertions(+), 124 deletions(-) diff --git a/src/CoreIntegrationTests/UserPasswordStoreTests.cs b/src/CoreIntegrationTests/UserPasswordStoreTests.cs index e15af52..94fce58 100644 --- a/src/CoreIntegrationTests/UserPasswordStoreTests.cs +++ b/src/CoreIntegrationTests/UserPasswordStoreTests.cs @@ -1,47 +1,58 @@ namespace IntegrationTests { using System.Linq; - using AspNet.Identity.MongoDB; - using Microsoft.AspNet.Identity; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Identity; + using Microsoft.AspNetCore.Identity.MongoDB; + using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; [TestFixture] public class UserPasswordStoreTests : UserIntegrationTestsBase { [Test] - public void HasPassword_NoPassword_ReturnsFalse() + public async Task HasPassword_NoPassword_ReturnsFalse() { var user = new IdentityUser {UserName = "bob"}; var manager = GetUserManager(); - manager.Create(user); + await manager.CreateAsync(user); - var hasPassword = manager.HasPassword(user.Id); + var hasPassword = await manager.HasPasswordAsync(user); Expect(hasPassword, Is.False); } [Test] - public void AddPassword_NewPassword_CanFindUserByPassword() + public async Task AddPassword_NewPassword_CanFindUserByPassword() { var user = new IdentityUser {UserName = "bob"}; - var manager = GetUserManager(); - manager.Create(user); - - manager.AddPassword(user.Id, "testtest"); - - var findUserByPassword = manager.Find("bob", "testtest"); - Expect(findUserByPassword, Is.Not.Null); + var manager = CreateServiceProvider(options => + { + options.Password.RequireDigit = false; + options.Password.RequireNonAlphanumeric = false; + options.Password.RequireUppercase = false; + }) + .GetService>(); + await manager.CreateAsync(user); + + var result = await manager.AddPasswordAsync(user, "testtest"); + Expect(result.Succeeded, Is.True); + + var userByName = await manager.FindByNameAsync("bob"); + Expect(userByName, Is.Not.Null); + var passwordIsValid = await manager.CheckPasswordAsync(userByName, "testtest"); + Expect(passwordIsValid, Is.True); } [Test] - public void RemovePassword_UserWithPassword_SetsPasswordNull() + public async Task RemovePassword_UserWithPassword_SetsPasswordNull() { var user = new IdentityUser {UserName = "bob"}; var manager = GetUserManager(); - manager.Create(user); - manager.AddPassword(user.Id, "testtest"); + await manager.CreateAsync(user); + await manager.AddPasswordAsync(user, "testtest"); - manager.RemovePassword(user.Id); + await manager.RemovePasswordAsync(user); var savedUser = Users.FindAll().Single(); Expect(savedUser.PasswordHash, Is.Null); diff --git a/src/CoreIntegrationTests/UserPhoneNumberStoreTests.cs b/src/CoreIntegrationTests/UserPhoneNumberStoreTests.cs index 5eb9209..d2cd83a 100644 --- a/src/CoreIntegrationTests/UserPhoneNumberStoreTests.cs +++ b/src/CoreIntegrationTests/UserPhoneNumberStoreTests.cs @@ -1,7 +1,7 @@ namespace IntegrationTests { - using AspNet.Identity.MongoDB; - using Microsoft.AspNet.Identity; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Identity.MongoDB; using NUnit.Framework; [TestFixture] @@ -10,42 +10,42 @@ public class UserPhoneNumberStoreTests : UserIntegrationTestsBase private const string PhoneNumber = "1234567890"; [Test] - public void SetPhoneNumber_StoresPhoneNumber() + public async Task SetPhoneNumber_StoresPhoneNumber() { var user = new IdentityUser {UserName = "bob"}; var manager = GetUserManager(); - manager.Create(user); + await manager.CreateAsync(user); - manager.SetPhoneNumber(user.Id, PhoneNumber); + await manager.SetPhoneNumberAsync(user, PhoneNumber); - Expect(manager.GetPhoneNumber(user.Id), Is.EqualTo(PhoneNumber)); + Expect(await manager.GetPhoneNumberAsync(user), Is.EqualTo(PhoneNumber)); } [Test] - public void ConfirmPhoneNumber_StoresPhoneNumberConfirmed() + public async Task ConfirmPhoneNumber_StoresPhoneNumberConfirmed() { var user = new IdentityUser {UserName = "bob"}; var manager = GetUserManager(); - manager.Create(user); - var token = manager.GenerateChangePhoneNumberToken(user.Id, PhoneNumber); + await manager.CreateAsync(user); + var token = await manager.GenerateChangePhoneNumberTokenAsync(user, PhoneNumber); - manager.ChangePhoneNumber(user.Id, PhoneNumber, token); + await manager.ChangePhoneNumberAsync(user, PhoneNumber, token); - Expect(manager.IsPhoneNumberConfirmed(user.Id)); + Expect(await manager.IsPhoneNumberConfirmedAsync(user)); } [Test] - public void ChangePhoneNumber_OriginalPhoneNumberWasConfirmed_NotPhoneNumberConfirmed() + public async Task ChangePhoneNumber_OriginalPhoneNumberWasConfirmed_NotPhoneNumberConfirmed() { var user = new IdentityUser {UserName = "bob"}; var manager = GetUserManager(); - manager.Create(user); - var token = manager.GenerateChangePhoneNumberToken(user.Id, PhoneNumber); - manager.ChangePhoneNumber(user.Id, PhoneNumber, token); + await manager.CreateAsync(user); + var token = await manager.GenerateChangePhoneNumberTokenAsync(user, PhoneNumber); + await manager.ChangePhoneNumberAsync(user, PhoneNumber, token); - manager.SetPhoneNumber(user.Id, PhoneNumber); + await manager.SetPhoneNumberAsync(user, PhoneNumber); - Expect(manager.IsPhoneNumberConfirmed(user.Id), Is.False); + Expect(await manager.IsPhoneNumberConfirmedAsync(user), Is.False); } } } \ No newline at end of file diff --git a/src/CoreIntegrationTests/UserRoleStoreTests.cs b/src/CoreIntegrationTests/UserRoleStoreTests.cs index 3d39978..3185fd0 100644 --- a/src/CoreIntegrationTests/UserRoleStoreTests.cs +++ b/src/CoreIntegrationTests/UserRoleStoreTests.cs @@ -1,52 +1,54 @@ namespace IntegrationTests { using System.Linq; - using AspNet.Identity.MongoDB; - using Microsoft.AspNet.Identity; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Identity.MongoDB; using NUnit.Framework; [TestFixture] public class UserRoleStoreTests : UserIntegrationTestsBase { [Test] - public void GetRoles_UserHasNoRoles_ReturnsNoRoles() + public async Task GetRoles_UserHasNoRoles_ReturnsNoRoles() { var manager = GetUserManager(); var user = new IdentityUser {UserName = "bob"}; - manager.Create(user); + await manager.CreateAsync(user); - var roles = manager.GetRoles(user.Id); + var roles = await manager.GetRolesAsync(user); Expect(roles, Is.Empty); } [Test] - public void AddRole_Adds() + public async Task AddRole_Adds() { var manager = GetUserManager(); var user = new IdentityUser {UserName = "bob"}; - manager.Create(user); + await manager.CreateAsync(user); - manager.AddToRole(user.Id, "role"); + // todo it would be nice if the API for UserManager asked for a normalized name and not just a name + await manager.AddToRoleAsync(user, "role"); var savedUser = Users.FindAll().Single(); - Expect(savedUser.Roles, Is.EquivalentTo(new[] {"role"})); - Expect(manager.IsInRole(user.Id, "role"), Is.True); + // note: addToRole now passes a normalized role name + Expect(savedUser.Roles, Is.EquivalentTo(new[] {"ROLE"})); + Expect(await manager.IsInRoleAsync(user, "role"), Is.True); } [Test] - public void RemoveRole_Removes() + public async Task RemoveRole_Removes() { var manager = GetUserManager(); var user = new IdentityUser {UserName = "bob"}; - manager.Create(user); - manager.AddToRole(user.Id, "role"); + await manager.CreateAsync(user); + await manager.AddToRoleAsync(user, "role"); - manager.RemoveFromRole(user.Id, "role"); + await manager.RemoveFromRoleAsync(user, "role"); var savedUser = Users.FindAll().Single(); Expect(savedUser.Roles, Is.Empty); - Expect(manager.IsInRole(user.Id, "role"), Is.False); + Expect(await manager.IsInRoleAsync(user, "role"), Is.False); } } } \ No newline at end of file diff --git a/src/CoreIntegrationTests/UserSecurityStampStoreTests.cs b/src/CoreIntegrationTests/UserSecurityStampStoreTests.cs index 12a6d05..b02e1de 100644 --- a/src/CoreIntegrationTests/UserSecurityStampStoreTests.cs +++ b/src/CoreIntegrationTests/UserSecurityStampStoreTests.cs @@ -1,33 +1,33 @@ namespace IntegrationTests { using System.Linq; - using AspNet.Identity.MongoDB; - using Microsoft.AspNet.Identity; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Identity.MongoDB; using NUnit.Framework; [TestFixture] public class UserSecurityStampStoreTests : UserIntegrationTestsBase { [Test] - public void Create_NewUser_HasSecurityStamp() + public async Task Create_NewUser_HasSecurityStamp() { var manager = GetUserManager(); var user = new IdentityUser {UserName = "bob"}; - manager.Create(user); + await manager.CreateAsync(user); var savedUser = Users.FindAll().Single(); Expect(savedUser.SecurityStamp, Is.Not.Null); } [Test] - public void GetSecurityStamp_NewUser_ReturnsStamp() + public async Task GetSecurityStamp_NewUser_ReturnsStamp() { var manager = GetUserManager(); var user = new IdentityUser {UserName = "bob"}; - manager.Create(user); + await manager.CreateAsync(user); - var stamp = manager.GetSecurityStamp(user.Id); + var stamp = await manager.GetSecurityStampAsync(user); Expect(stamp, Is.Not.Null); } diff --git a/src/CoreIntegrationTests/UserStoreTests.cs b/src/CoreIntegrationTests/UserStoreTests.cs index c10ea83..3ff1b14 100644 --- a/src/CoreIntegrationTests/UserStoreTests.cs +++ b/src/CoreIntegrationTests/UserStoreTests.cs @@ -1,100 +1,99 @@ namespace IntegrationTests { using System.Linq; - using AspNet.Identity.MongoDB; - using Microsoft.AspNet.Identity; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Identity.MongoDB; using MongoDB.Bson; using NUnit.Framework; - using Tests; [TestFixture] public class UserStoreTests : UserIntegrationTestsBase { [Test] - public void Create_NewUser_Saves() + public async Task Create_NewUser_Saves() { var userName = "name"; var user = new IdentityUser {UserName = userName}; var manager = GetUserManager(); - manager.Create(user); + await manager.CreateAsync(user); var savedUser = Users.FindAll().Single(); Expect(savedUser.UserName, Is.EqualTo(user.UserName)); } [Test] - public void FindByName_SavedUser_ReturnsUser() + public async Task FindByName_SavedUser_ReturnsUser() { var userName = "name"; var user = new IdentityUser {UserName = userName}; var manager = GetUserManager(); - manager.Create(user); + await manager.CreateAsync(user); - var foundUser = manager.FindByName(userName); + var foundUser = await manager.FindByNameAsync(userName); Expect(foundUser, Is.Not.Null); Expect(foundUser.UserName, Is.EqualTo(userName)); } [Test] - public void FindByName_NoUser_ReturnsNull() + public async Task FindByName_NoUser_ReturnsNull() { var manager = GetUserManager(); - var foundUser = manager.FindByName("nouserbyname"); + var foundUser = await manager.FindByNameAsync("nouserbyname"); Expect(foundUser, Is.Null); } [Test] - public void FindById_SavedUser_ReturnsUser() + public async Task FindById_SavedUser_ReturnsUser() { var userId = ObjectId.GenerateNewId().ToString(); var user = new IdentityUser {UserName = "name"}; - user.SetId(userId); + user.Id = userId; var manager = GetUserManager(); - manager.Create(user); + await manager.CreateAsync(user); - var foundUser = manager.FindById(userId); + var foundUser = await manager.FindByIdAsync(userId); Expect(foundUser, Is.Not.Null); Expect(foundUser.Id, Is.EqualTo(userId)); } [Test] - public void FindById_NoUser_ReturnsNull() + public async Task FindById_NoUser_ReturnsNull() { var manager = GetUserManager(); - var foundUser = manager.FindById(ObjectId.GenerateNewId().ToString()); + var foundUser = await manager.FindByIdAsync(ObjectId.GenerateNewId().ToString()); Expect(foundUser, Is.Null); } [Test] - public void Delete_ExistingUser_Removes() + public async Task Delete_ExistingUser_Removes() { var user = new IdentityUser {UserName = "name"}; var manager = GetUserManager(); - manager.Create(user); + await manager.CreateAsync(user); Expect(Users.FindAll(), Is.Not.Empty); - manager.Delete(user); + await manager.DeleteAsync(user); Expect(Users.FindAll(), Is.Empty); } [Test] - public void Update_ExistingUser_Updates() + public async Task Update_ExistingUser_Updates() { var user = new IdentityUser {UserName = "name"}; var manager = GetUserManager(); - manager.Create(user); - var savedUser = manager.FindById(user.Id); + await manager.CreateAsync(user); + var savedUser = await manager.FindByIdAsync(user.Id); savedUser.UserName = "newname"; - manager.Update(savedUser); + await manager.UpdateAsync(savedUser); var changedUser = Users.FindAll().Single(); Expect(changedUser, Is.Not.Null); diff --git a/src/CoreIntegrationTests/UserTwoFactorStoreTests.cs b/src/CoreIntegrationTests/UserTwoFactorStoreTests.cs index 1739680..eb26080 100644 --- a/src/CoreIntegrationTests/UserTwoFactorStoreTests.cs +++ b/src/CoreIntegrationTests/UserTwoFactorStoreTests.cs @@ -1,35 +1,35 @@ namespace IntegrationTests { - using AspNet.Identity.MongoDB; - using Microsoft.AspNet.Identity; + using System.Threading.Tasks; + using Microsoft.AspNetCore.Identity.MongoDB; using NUnit.Framework; [TestFixture] public class UserTwoFactorStoreTests : UserIntegrationTestsBase { [Test] - public void SetTwoFactorEnabled() + public async Task SetTwoFactorEnabled() { var user = new IdentityUser {UserName = "bob"}; var manager = GetUserManager(); - manager.Create(user); + await manager.CreateAsync(user); - manager.SetTwoFactorEnabled(user.Id, true); + await manager.SetTwoFactorEnabledAsync(user, true); - Expect(manager.GetTwoFactorEnabled(user.Id)); + Expect(await manager.GetTwoFactorEnabledAsync(user)); } [Test] - public void ClearTwoFactorEnabled_PreviouslyEnabled_NotEnabled() + public async Task ClearTwoFactorEnabled_PreviouslyEnabled_NotEnabled() { var user = new IdentityUser {UserName = "bob"}; var manager = GetUserManager(); - manager.Create(user); - manager.SetTwoFactorEnabled(user.Id, true); + await manager.CreateAsync(user); + await manager.SetTwoFactorEnabledAsync(user, true); - manager.SetTwoFactorEnabled(user.Id, false); + await manager.SetTwoFactorEnabledAsync(user, false); - Expect(manager.GetTwoFactorEnabled(user.Id), Is.False); + Expect(await manager.GetTwoFactorEnabledAsync(user), Is.False); } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs index 530ffb7..1c7a46a 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs @@ -74,17 +74,17 @@ public virtual async Task GetNormalizedUserNameAsync(TUser user, Cancell => user.NormalizedUserName; // todo testing - public virtual async Task SetNormalizedUserNameAsync(TUser user, string normalizedName, CancellationToken cancellationToken) - => user.NormalizedUserName = normalizedName; + public virtual async Task SetNormalizedUserNameAsync(TUser user, string normalizedUserName, CancellationToken cancellationToken) + => user.NormalizedUserName = normalizedUserName; // todo testing public virtual Task FindByIdAsync(string userId, CancellationToken token) => _Users.Find(u => u.Id == userId).FirstOrDefaultAsync(token); - public virtual Task FindByNameAsync(string userName, CancellationToken token) + public virtual Task FindByNameAsync(string normalizedUserName, CancellationToken token) // todo test fails with normalized? // todo exception on duplicates? or better to enforce unique index to ensure this - => _Users.Find(u => u.UserName == userName).FirstOrDefaultAsync(token); + => _Users.Find(u => u.NormalizedUserName == normalizedUserName).FirstOrDefaultAsync(token); public virtual async Task SetPasswordHashAsync(TUser user, string passwordHash, CancellationToken token) => user.PasswordHash = passwordHash; @@ -95,21 +95,21 @@ public virtual async Task GetPasswordHashAsync(TUser user, CancellationT public virtual async Task HasPasswordAsync(TUser user, CancellationToken token) => user.HasPassword(); - public virtual async Task AddToRoleAsync(TUser user, string roleName, CancellationToken token) - => user.AddRole(roleName); + public virtual async Task AddToRoleAsync(TUser user, string normalizedRoleName, CancellationToken token) + => user.AddRole(normalizedRoleName); - public virtual async Task RemoveFromRoleAsync(TUser user, string roleName, CancellationToken token) - => user.RemoveRole(roleName); + public virtual async Task RemoveFromRoleAsync(TUser user, string normalizedRoleName, CancellationToken token) + => user.RemoveRole(normalizedRoleName); - public virtual Task> GetRolesAsync(TUser user, CancellationToken token) - => Task.FromResult((IList) user.Roles); + public virtual async Task> GetRolesAsync(TUser user, CancellationToken token) + => user.Roles; - public virtual Task IsInRoleAsync(TUser user, string roleName, CancellationToken token) - => Task.FromResult(user.Roles.Contains(roleName)); + public virtual async Task IsInRoleAsync(TUser user, string normalizedRoleName, CancellationToken token) + => user.Roles.Contains(normalizedRoleName); // todo testing - public virtual async Task> GetUsersInRoleAsync(string roleName, CancellationToken token) - => await _Users.Find(u => u.Roles.Contains(roleName)) + public virtual async Task> GetUsersInRoleAsync(string normalizedRoleName, CancellationToken token) + => await _Users.Find(u => u.Roles.Contains(normalizedRoleName)) .ToListAsync(token); public virtual async Task AddLoginAsync(TUser user, UserLoginInfo login, CancellationToken token) @@ -132,27 +132,17 @@ public virtual async Task SetSecurityStampAsync(TUser user, string stamp, Cancel public virtual async Task GetSecurityStampAsync(TUser user, CancellationToken token) => user.SecurityStamp; - public virtual Task GetEmailConfirmedAsync(TUser user, CancellationToken token) - { - return Task.FromResult(user.EmailConfirmed); - } + public virtual async Task GetEmailConfirmedAsync(TUser user, CancellationToken token) + => user.EmailConfirmed; - public virtual Task SetEmailConfirmedAsync(TUser user, bool confirmed, CancellationToken token) - { - user.EmailConfirmed = confirmed; - return Task.FromResult(0); - } + public virtual async Task SetEmailConfirmedAsync(TUser user, bool confirmed, CancellationToken token) + => user.EmailConfirmed = confirmed; - public virtual Task SetEmailAsync(TUser user, string email, CancellationToken token) - { - user.Email = email; - return Task.FromResult(0); - } + public virtual async Task SetEmailAsync(TUser user, string email, CancellationToken token) + => user.Email = email; - public virtual Task GetEmailAsync(TUser user, CancellationToken token) - { - return Task.FromResult(user.Email); - } + public virtual async Task GetEmailAsync(TUser user, CancellationToken token) + => user.Email; // todo testing public virtual async Task GetNormalizedEmailAsync(TUser user, CancellationToken cancellationToken) @@ -162,11 +152,11 @@ public virtual async Task GetNormalizedEmailAsync(TUser user, Cancellati public virtual async Task SetNormalizedEmailAsync(TUser user, string normalizedEmail, CancellationToken cancellationToken) => user.NormalizedEmail = normalizedEmail; - public virtual Task FindByEmailAsync(string email, CancellationToken token) + public virtual Task FindByEmailAsync(string normalizedEmail, CancellationToken token) { // todo I don't like that this now searches on normalized email :(... why not FindByNormalizedEmailAsync then? // todo what if a user can have multiple accounts with the same email? - return _Users.Find(u => u.NormalizedEmail == email).FirstOrDefaultAsync(token); + return _Users.Find(u => u.NormalizedEmail == normalizedEmail).FirstOrDefaultAsync(token); } public virtual async Task> GetClaimsAsync(TUser user, CancellationToken token) From f81c51f49cf1704a9108b68b1ea76b663139a4b9 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Wed, 7 Sep 2016 20:31:27 -0400 Subject: [PATCH 20/56] Tidying up, notes, disable some warnings for a style of code I'm using, and change to RemoveAll instead of replacing list references which could silently surprise consumers. --- .../IdentityRole.cs | 3 +- .../IdentityUser.cs | 32 ++++++++----------- .../RoleStore.cs | 25 ++++++++------- .../UserStore.cs | 11 +++++-- 4 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs index e8e4c5b..ab85e89 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs @@ -20,7 +20,8 @@ public IdentityRole(string roleName) : this() public string Name { get; set; } - // todo + // todo migration from legacy AspNet.Identity.MongoDB type? At least in docs + // lookup how normalization is provided OOB and use that for default case public string NormalizedName { get; set; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs index 4708042..208b72f 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs @@ -20,17 +20,20 @@ public IdentityUser() } [BsonRepresentation(BsonType.ObjectId)] - public string Id { get; set; } + public virtual string Id { get; set; } - public string UserName { get; set; } + public virtual string UserName { get; set; } // todo what should we do with this in Mongo land // https://github.com/aspnet/Identity/issues/351 + // todo migration public virtual string NormalizedUserName { get; set; } public virtual string SecurityStamp { get; set; } public virtual string Email { get; set; } + + // todo migration public virtual string NormalizedEmail { get; set; } public virtual bool EmailConfirmed { get; set; } @@ -41,6 +44,7 @@ public IdentityUser() public virtual bool TwoFactorEnabled { get; set; } + // todo migration public virtual DateTimeOffset? LockoutEndDateUtc { get; set; } public virtual bool LockoutEnabled { get; set; } @@ -48,7 +52,7 @@ public IdentityUser() public virtual int AccessFailedCount { get; set; } [BsonIgnoreIfNull] - public List Roles { get; set; } + public virtual List Roles { get; set; } public virtual void AddRole(string role) { @@ -64,7 +68,7 @@ public virtual void RemoveRole(string role) public virtual string PasswordHash { get; set; } [BsonIgnoreIfNull] - public List Logins { get; set; } + public virtual List Logins { get; set; } public virtual void AddLogin(UserLoginInfo login) { @@ -73,11 +77,7 @@ public virtual void AddLogin(UserLoginInfo login) public virtual void RemoveLogin(string loginProvider, string providerKey) { - var loginsToRemove = Logins - .Where(l => l.LoginProvider == loginProvider) - .Where(l => l.ProviderKey == providerKey); - - Logins = Logins.Except(loginsToRemove).ToList(); + Logins.RemoveAll(l => l.LoginProvider == loginProvider && l.ProviderKey == providerKey); } public virtual bool HasPassword() @@ -86,7 +86,7 @@ public virtual bool HasPassword() } [BsonIgnoreIfNull] - public List Claims { get; set; } + public virtual List Claims { get; set; } public virtual void AddClaim(Claim claim) { @@ -95,14 +95,10 @@ public virtual void AddClaim(Claim claim) public virtual void RemoveClaim(Claim claim) { - var claimsToRemove = Claims - .Where(c => c.Type == claim.Type) - .Where(c => c.Value == claim.Value); - - Claims = Claims.Except(claimsToRemove).ToList(); + Claims.RemoveAll(c => c.Type == claim.Type && c.Value == claim.Value); } - public void ReplaceClaim(Claim claim, Claim newClaim) + public virtual void ReplaceClaim(Claim claim, Claim newClaim) { var current = Claims .FirstOrDefault(c => c.Type == claim.Type && c.Value == claim.Value); @@ -116,12 +112,12 @@ public void ReplaceClaim(Claim claim, Claim newClaim) } [BsonIgnoreIfNull] - public List Tokens { get; set; } + public virtual List Tokens { get; set; } private IdentityUserToken GetToken(string loginProider, string name) => Tokens.FirstOrDefault(t => t.LoginProvider == loginProider && t.Name == name); - // todo testing of tokens + // todo testing of tokens, what are these for? public virtual void SetToken(string loginProider, string name, string value) { var existingToken = GetToken(loginProider, name); diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs index 8348c9a..c4c97a7 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs @@ -1,4 +1,8 @@ -namespace Microsoft.AspNetCore.Identity.MongoDB + +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously +// I'm using async methods to leverage implicit Task wrapping of results from expression bodied functions. + +namespace Microsoft.AspNetCore.Identity.MongoDB { using System.Linq; using System.Threading; @@ -9,8 +13,9 @@ /// Note: Deleting and updating do not modify the roles stored on a user document. If you desire this dynamic /// capability, override the appropriate operations on RoleStore as desired for your application. For example you could /// perform a document modification on the users collection before a delete or a rename. + /// When passing a cancellation token, it will only be used if the operation requires a database interaction. /// - /// + /// Needs to extend the provided IdentityRole type. public class RoleStore : IRoleStore, IQueryableRoleStore where TRole : IdentityRole { @@ -35,12 +40,14 @@ public virtual async Task CreateAsync(TRole role, CancellationTo public virtual async Task UpdateAsync(TRole role, CancellationToken token) { await _Roles.ReplaceOneAsync(r => r.Id == role.Id, role, cancellationToken: token); + // todo result based on replace result return IdentityResult.Success; } public virtual async Task DeleteAsync(TRole role, CancellationToken token) { - await _Roles.DeleteOneAsync(r => r.Id == role.Id, cancellationToken: token); + await _Roles.DeleteOneAsync(r => r.Id == role.Id, token); + // todo result based on delete result return IdentityResult.Success; } @@ -60,16 +67,12 @@ public virtual async Task SetNormalizedRoleNameAsync(TRole role, string normaliz => role.NormalizedName = normalizedName; public virtual Task FindByIdAsync(string roleId, CancellationToken token) - { - return _Roles.Find(r => r.Id == roleId).FirstOrDefaultAsync(token); - } + => _Roles.Find(r => r.Id == roleId).FirstOrDefaultAsync(token); public virtual Task FindByNameAsync(string roleName, CancellationToken token) - { - // todo thoughts on searching now by normalized name without changing api... - return _Roles.Find(r => r.NormalizedName == roleName).FirstOrDefaultAsync(token); - } + => _Roles.Find(r => r.NormalizedName == roleName).FirstOrDefaultAsync(token); - public virtual IQueryable Roles => _Roles.AsQueryable(); + public virtual IQueryable Roles + => _Roles.AsQueryable(); } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs index 1c7a46a..5f12b14 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs @@ -1,4 +1,8 @@ -namespace Microsoft.AspNetCore.Identity.MongoDB + +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously +// I'm using async methods to leverage implicit Task wrapping of results from expression bodied functions. + +namespace Microsoft.AspNetCore.Identity.MongoDB { using System; using System.Collections.Generic; @@ -9,8 +13,7 @@ using global::MongoDB.Driver; /// - /// FYI as for methods with CancellationToken, unless a database op is involved the token is ignored as any in memory - /// ops to manipulate the object graph of a user, or query, are so fast that canellation is a waste of time. + /// When passing a cancellation token, it will only be used if the operation requires a database interaction. /// /// public class UserStore : IUserPasswordStore, @@ -48,12 +51,14 @@ public virtual async Task UpdateAsync(TUser user, CancellationTo { // todo should add an optimistic concurrency check await _Users.ReplaceOneAsync(u => u.Id == user.Id, user, cancellationToken: token); + // todo success based on replace result return IdentityResult.Success; } public virtual async Task DeleteAsync(TUser user, CancellationToken token) { await _Users.DeleteOneAsync(u => u.Id == user.Id, token); + // todo success based on delete result return IdentityResult.Success; } From 2660b13d6e718f862102d8e9ae11b19a8895eabb Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Wed, 7 Sep 2016 21:38:18 -0400 Subject: [PATCH 21/56] Notes about display name on logins --- src/CoreIntegrationTests/UserLoginStoreTests.cs | 3 +++ src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/CoreIntegrationTests/UserLoginStoreTests.cs b/src/CoreIntegrationTests/UserLoginStoreTests.cs index aa01589..7e3b723 100644 --- a/src/CoreIntegrationTests/UserLoginStoreTests.cs +++ b/src/CoreIntegrationTests/UserLoginStoreTests.cs @@ -23,6 +23,8 @@ public async Task AddLogin_NewLogin_Adds() var savedLogin = Users.FindAll().Single().Logins.Single(); Expect(savedLogin.LoginProvider, Is.EqualTo("provider")); Expect(savedLogin.ProviderKey, Is.EqualTo("key")); + // todo test displayname, what about migrating this? + Expect(savedLogin.ProviderDisplayName, Is.EqualTo("name")); } [Test] @@ -54,6 +56,7 @@ public async Task GetLogins_OneLogin_ReturnsLogin() var savedLogin = logins.Single(); Expect(savedLogin.LoginProvider, Is.EqualTo("provider")); Expect(savedLogin.ProviderKey, Is.EqualTo("key")); + // todo name } [Test] diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs index 208b72f..f68ca9c 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs @@ -67,6 +67,9 @@ public virtual void RemoveRole(string role) [BsonIgnoreIfNull] public virtual string PasswordHash { get; set; } + + // todo move to a type I manage - and check for changes to UserLoginInfo for migration purposes + // todo I know that displayName was added [BsonIgnoreIfNull] public virtual List Logins { get; set; } From aa039ece479fb42e0b7632feddac71f6e35edf4f Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Wed, 7 Sep 2016 20:31:54 -0400 Subject: [PATCH 22/56] project.json options for packing library --- .../AspNet.Identity.MongoDB.nuspec | 17 ---------- .../project.json | 32 ++++++++++++++++++- 2 files changed, 31 insertions(+), 18 deletions(-) delete mode 100644 src/Microsoft.AspNetCore.Identity.MongoDB/AspNet.Identity.MongoDB.nuspec diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/AspNet.Identity.MongoDB.nuspec b/src/Microsoft.AspNetCore.Identity.MongoDB/AspNet.Identity.MongoDB.nuspec deleted file mode 100644 index 398e5e1..0000000 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/AspNet.Identity.MongoDB.nuspec +++ /dev/null @@ -1,17 +0,0 @@ - - - - AspNet.Identity.MongoDB - 2.1.0 - Wes Higbee - Wes Higbee - https://github.com/g0t4/aspnet-identity-mongo/blob/master/LICENSE - https://github.com/g0t4/aspnet-identity-mongo - https://github.com/g0t4/aspnet-identity-mongo - false - A mongodb provider for the new ASP.NET Identity framework. My aim is to ensure this project is well tested and configurable. - MongoDB 2.2 c# driver and add back queryable (LINQ) user and role stores. - MIT - mongo mongodb aspnet identity - - \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/project.json b/src/Microsoft.AspNetCore.Identity.MongoDB/project.json index a511b83..5ebf0ee 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/project.json +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/project.json @@ -1,6 +1,36 @@ { + "name": "Microsoft.AspNetCore.Identity.MongoDB", "version": "1.0.0-*", + "description": + "A MongoDB provider for ASP.NET Core Identity. NET Core Identity fwk. My aim is to ensure this project is well tested and configurable.", + + "authors": ["Wes Higbee"], + + "buildOptions": { + "warningsAsErrors": true + }, + + "packOptions": { + "summary": "A MongoDB provider for ASP.NET Core Identity", + "owners": ["Wes Higbee"], + "releaseNotes": "Port of AspNet.Identity.MongoDB to ASP.NET Core", + + "tags": [ + "aspnetcore", + "mongo", + "mongodb", + "identity", + "membership" + ], + "repository": { + "type": "git", + "url": "https://github.com/g0t4/aspnet-identity-mongo" + }, + "licenseUrl": "https://github.com/g0t4/aspnet-identity-mongo/blob/master/LICENSE", + "projectUrl": "https://github.com/g0t4/aspnet-identity-mongo" + }, + "dependencies": { "Microsoft.AspNetCore.Identity": "1.0.0", "MongoDB.Driver": "2.3.0-rc1", @@ -9,7 +39,7 @@ "frameworks": { "netstandard1.5": { - "imports": "dnxcore50" + } } } From a0038000e0293c78367b964fc69f30032e1275a4 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Sat, 24 Sep 2016 12:15:13 -0400 Subject: [PATCH 23/56] Notes --- .../IdentityUser.cs | 11 ++++++++++ .../IdentityUserClaim.cs | 10 ++++++++++ .../IdentityUserToken.cs | 12 +++++++++++ .../IndexChecks.cs | 2 ++ .../README.md | 9 +++++++++ .../RoleStore.cs | 18 +++++++++++++---- .../UserStore.cs | 20 ++++++++++++++++++- 7 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 src/Microsoft.AspNetCore.Identity.MongoDB/README.md diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs index f68ca9c..840f266 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs @@ -29,6 +29,10 @@ public IdentityUser() // todo migration public virtual string NormalizedUserName { get; set; } + /// + /// A random value that must change whenever a users credentials change + /// (password changed, login removed) + /// public virtual string SecurityStamp { get; set; } public virtual string Email { get; set; } @@ -45,6 +49,7 @@ public IdentityUser() public virtual bool TwoFactorEnabled { get; set; } // todo migration + // ef has LockoutEnd ... what was this before? public virtual DateTimeOffset? LockoutEndDateUtc { get; set; } public virtual bool LockoutEnabled { get; set; } @@ -78,6 +83,7 @@ public virtual void AddLogin(UserLoginInfo login) Logins.Add(login); } + // todo testing? public virtual void RemoveLogin(string loginProvider, string providerKey) { Logins.RemoveAll(l => l.LoginProvider == loginProvider && l.ProviderKey == providerKey); @@ -101,6 +107,7 @@ public virtual void RemoveClaim(Claim claim) Claims.RemoveAll(c => c.Type == claim.Type && c.Value == claim.Value); } + // todo testing? public virtual void ReplaceClaim(Claim claim, Claim newClaim) { var current = Claims @@ -114,9 +121,11 @@ public virtual void ReplaceClaim(Claim claim, Claim newClaim) AddClaim(newClaim); } + // todo testing? [BsonIgnoreIfNull] public virtual List Tokens { get; set; } + // todo testing? private IdentityUserToken GetToken(string loginProider, string name) => Tokens.FirstOrDefault(t => t.LoginProvider == loginProider && t.Name == name); @@ -138,11 +147,13 @@ public virtual void SetToken(string loginProider, string name, string value) }); } + // todo testing? public virtual string GetTokenValue(string loginProider, string name) { return GetToken(loginProider, name)?.Value; } + // todo testing? public virtual void RemoveToken(string loginProvider, string name) { Tokens.RemoveAll(t => t.LoginProvider == loginProvider && t.Name == name); diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserClaim.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserClaim.cs index 3b8be9d..7f8292e 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserClaim.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserClaim.cs @@ -2,6 +2,9 @@ { using System.Security.Claims; + /// + /// A claim that a user possesses. + /// public class IdentityUserClaim { public IdentityUserClaim() @@ -14,7 +17,14 @@ public IdentityUserClaim(Claim claim) Value = claim.Value; } + /// + /// Claim type + /// public string Type { get; set; } + + /// + /// Claim value + /// public string Value { get; set; } public Claim ToSecurityClaim() diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserToken.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserToken.cs index dccf969..85f72ab 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserToken.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserToken.cs @@ -1,11 +1,23 @@ namespace Microsoft.AspNetCore.Identity.MongoDB { + /// + /// Authentication token associated with a user + /// public class IdentityUserToken { + /// + /// The provider that the token came from. + /// public string LoginProvider { get; set; } + /// + /// The name of the token. + /// public string Name { get; set; } + /// + /// The value of the token. + /// public string Value { get; set; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IndexChecks.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IndexChecks.cs index 813501c..f37ed27 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IndexChecks.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IndexChecks.cs @@ -27,5 +27,7 @@ public static void EnsureUniqueIndexOnEmail(IMongoCollection users var unique = new CreateIndexOptions {Unique = true}; users.Indexes.CreateOneAsync(email, unique); } + + // todo indexes for normalized role name, user name and emails? } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/README.md b/src/Microsoft.AspNetCore.Identity.MongoDB/README.md new file mode 100644 index 0000000..7d5389a --- /dev/null +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/README.md @@ -0,0 +1,9 @@ +## Migrating from ASP.NET Identity 2.0 + +- roles names need to be normalized (user.roles) + - Default uppercase - tell people if they customize this they have to deal with custom migration + +- add IdentityRole.NormalizedName +- add IdentityUser.NormalizedUserName, IdentityUser.NormalizedEmail +- LockoutEndDateUtc - type changed in code, but I think it is still the same in db +- \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs index c4c97a7..ece0abe 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs @@ -16,7 +16,8 @@ namespace Microsoft.AspNetCore.Identity.MongoDB /// When passing a cancellation token, it will only be used if the operation requires a database interaction. /// /// Needs to extend the provided IdentityRole type. - public class RoleStore : IRoleStore, IQueryableRoleStore + public class RoleStore : IQueryableRoleStore + // todo IRoleClaimStore where TRole : IdentityRole { private readonly IMongoCollection _Roles; @@ -51,26 +52,35 @@ public virtual async Task DeleteAsync(TRole role, CancellationTo return IdentityResult.Success; } + // todo testing? public virtual async Task GetRoleIdAsync(TRole role, CancellationToken cancellationToken) => role.Id; + // todo testing? public virtual async Task GetRoleNameAsync(TRole role, CancellationToken cancellationToken) => role.Name; + // todo testing? public virtual async Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken) => role.Name = roleName; + // todo testing? public virtual async Task GetNormalizedRoleNameAsync(TRole role, CancellationToken cancellationToken) => role.NormalizedName; + // todo testing? public virtual async Task SetNormalizedRoleNameAsync(TRole role, string normalizedName, CancellationToken cancellationToken) => role.NormalizedName = normalizedName; + // todo testing? public virtual Task FindByIdAsync(string roleId, CancellationToken token) - => _Roles.Find(r => r.Id == roleId).FirstOrDefaultAsync(token); + => _Roles.Find(r => r.Id == roleId) + .FirstOrDefaultAsync(token); - public virtual Task FindByNameAsync(string roleName, CancellationToken token) - => _Roles.Find(r => r.NormalizedName == roleName).FirstOrDefaultAsync(token); + // todo testing normalized? + public virtual Task FindByNameAsync(string normalizedName, CancellationToken token) + => _Roles.Find(r => r.NormalizedName == normalizedName) + .FirstOrDefaultAsync(token); public virtual IQueryable Roles => _Roles.AsQueryable(); diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs index 5f12b14..7b69dab 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs @@ -16,7 +16,8 @@ namespace Microsoft.AspNetCore.Identity.MongoDB /// When passing a cancellation token, it will only be used if the operation requires a database interaction. /// /// - public class UserStore : IUserPasswordStore, + public class UserStore : + IUserPasswordStore, IUserRoleStore, IUserLoginStore, IUserSecurityStampStore, @@ -106,6 +107,10 @@ public virtual async Task AddToRoleAsync(TUser user, string normalizedRoleName, public virtual async Task RemoveFromRoleAsync(TUser user, string normalizedRoleName, CancellationToken token) => user.RemoveRole(normalizedRoleName); + // todo might have issue, I'm just storing Normalized only now, so I'm returning normalized here instead of not normalized. + // EF provider returns not noramlized here + // however, the rest of the API uses normalized (add/remove/isinrole) so maybe this approach is better anyways + // note: could always map normalized to not if people complain public virtual async Task> GetRolesAsync(TUser user, CancellationToken token) => user.Roles; @@ -117,23 +122,29 @@ public virtual async Task> GetUsersInRoleAsync(string normalizedRol => await _Users.Find(u => u.Roles.Contains(normalizedRoleName)) .ToListAsync(token); + // todo testing? public virtual async Task AddLoginAsync(TUser user, UserLoginInfo login, CancellationToken token) => user.AddLogin(login); + // todo testing? public virtual async Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken)) => user.RemoveLogin(loginProvider, providerKey); + // todo testing? public virtual async Task> GetLoginsAsync(TUser user, CancellationToken token) => user.Logins; + // todo testing? public virtual Task FindByLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken)) => _Users .Find(u => u.Logins.Any(l => l.LoginProvider == loginProvider && l.ProviderKey == providerKey)) .FirstOrDefaultAsync(cancellationToken); + // todo testing? public virtual async Task SetSecurityStampAsync(TUser user, string stamp, CancellationToken token) => user.SecurityStamp = stamp; + // todo testing? public virtual async Task GetSecurityStampAsync(TUser user, CancellationToken token) => user.SecurityStamp; @@ -167,6 +178,7 @@ public virtual Task FindByEmailAsync(string normalizedEmail, Cancellation public virtual async Task> GetClaimsAsync(TUser user, CancellationToken token) => user.Claims.Select(c => c.ToSecurityClaim()).ToList(); + // todo testing ok? public virtual Task AddClaimsAsync(TUser user, IEnumerable claims, CancellationToken token) { foreach (var claim in claims) @@ -176,6 +188,7 @@ public virtual Task AddClaimsAsync(TUser user, IEnumerable claims, Cancel return Task.FromResult(0); } + // todo testing ok? public virtual Task RemoveClaimsAsync(TUser user, IEnumerable claims, CancellationToken token) { foreach (var claim in claims) @@ -224,6 +237,7 @@ public virtual Task GetTwoFactorEnabledAsync(TUser user, CancellationToken return Task.FromResult(user.TwoFactorEnabled); } + // todo testing public virtual async Task> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken)) { return await _Users @@ -232,12 +246,14 @@ public virtual Task GetTwoFactorEnabledAsync(TUser user, CancellationToken .ToListAsync(cancellationToken); } + // todo testing? public virtual Task GetLockoutEndDateAsync(TUser user, CancellationToken token) { // todo migration? return Task.FromResult(user.LockoutEndDateUtc); } + // todo testing? public virtual Task SetLockoutEndDateAsync(TUser user, DateTimeOffset? lockoutEnd, CancellationToken token) { user.LockoutEndDateUtc = lockoutEnd; @@ -259,9 +275,11 @@ public virtual Task ResetAccessFailedCountAsync(TUser user, CancellationToken to public virtual async Task GetAccessFailedCountAsync(TUser user, CancellationToken token) => user.AccessFailedCount; + // todo testing? public virtual async Task GetLockoutEnabledAsync(TUser user, CancellationToken token) => user.LockoutEnabled; + // todo testing? public virtual async Task SetLockoutEnabledAsync(TUser user, bool enabled, CancellationToken token) => user.LockoutEnabled = enabled; From 144e316709c639767a2e327167f4d417449ffecf Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Sat, 24 Sep 2016 12:49:23 -0400 Subject: [PATCH 24/56] Testing role store for simple accessors and getters --- src/CoreIntegrationTests/RoleStoreTests.cs | 17 +++++++++++++++++ .../RoleStore.cs | 12 ++++-------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/CoreIntegrationTests/RoleStoreTests.cs b/src/CoreIntegrationTests/RoleStoreTests.cs index b22b168..690dc09 100644 --- a/src/CoreIntegrationTests/RoleStoreTests.cs +++ b/src/CoreIntegrationTests/RoleStoreTests.cs @@ -79,5 +79,22 @@ public async Task Update_ExistingRole_Updates() Expect(changedRole, Is.Not.Null); Expect(changedRole.Name, Is.EqualTo("newname")); } + + [Test] + public async Task SimpleAccessorsAndGetters() + { + var role = new IdentityRole + { + Name = "name" + }; + var manager = GetRoleManager(); + await manager.CreateAsync(role); + + Expect(await manager.GetRoleIdAsync(role), Is.EqualTo(role.Id)); + Expect(await manager.GetRoleNameAsync(role), Is.EqualTo("name")); + + await manager.SetRoleNameAsync(role, "newName"); + Expect(await manager.GetRoleNameAsync(role), Is.EqualTo("newName")); + } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs index ece0abe..4d5eb3a 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs @@ -40,27 +40,24 @@ public virtual async Task CreateAsync(TRole role, CancellationTo public virtual async Task UpdateAsync(TRole role, CancellationToken token) { - await _Roles.ReplaceOneAsync(r => r.Id == role.Id, role, cancellationToken: token); - // todo result based on replace result + var result = await _Roles.ReplaceOneAsync(r => r.Id == role.Id, role, cancellationToken: token); + // todo low priority result based on replace result return IdentityResult.Success; } public virtual async Task DeleteAsync(TRole role, CancellationToken token) { - await _Roles.DeleteOneAsync(r => r.Id == role.Id, token); - // todo result based on delete result + var result = await _Roles.DeleteOneAsync(r => r.Id == role.Id, token); + // todo low priority result based on delete result return IdentityResult.Success; } - // todo testing? public virtual async Task GetRoleIdAsync(TRole role, CancellationToken cancellationToken) => role.Id; - // todo testing? public virtual async Task GetRoleNameAsync(TRole role, CancellationToken cancellationToken) => role.Name; - // todo testing? public virtual async Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken) => role.Name = roleName; @@ -72,7 +69,6 @@ public virtual async Task GetNormalizedRoleNameAsync(TRole role, Cancell public virtual async Task SetNormalizedRoleNameAsync(TRole role, string normalizedName, CancellationToken cancellationToken) => role.NormalizedName = normalizedName; - // todo testing? public virtual Task FindByIdAsync(string roleId, CancellationToken token) => _Roles.Find(r => r.Id == roleId) .FirstOrDefaultAsync(token); From b53148e971e5237363e3817fd79171afcefbe242 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Sat, 24 Sep 2016 12:54:46 -0400 Subject: [PATCH 25/56] Testing for role name normalization --- src/CoreIntegrationTests/RoleStoreTests.cs | 2 ++ src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs | 2 -- src/Microsoft.AspNetCore.Identity.MongoDB/README.md | 5 +++-- src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs | 4 +--- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/CoreIntegrationTests/RoleStoreTests.cs b/src/CoreIntegrationTests/RoleStoreTests.cs index 690dc09..a09fd86 100644 --- a/src/CoreIntegrationTests/RoleStoreTests.cs +++ b/src/CoreIntegrationTests/RoleStoreTests.cs @@ -20,6 +20,7 @@ public async Task Create_NewRole_Saves() var savedRole = Roles.FindAll().Single(); Expect(savedRole.Name, Is.EqualTo(roleName)); + Expect(savedRole.NormalizedName, Is.EqualTo("ADMIN")); } [Test] @@ -30,6 +31,7 @@ public async Task FindByName_SavedRole_ReturnsRole() var manager = GetRoleManager(); await manager.CreateAsync(role); + // note: also tests normalization as FindByName now uses normalization var foundRole = await manager.FindByNameAsync(roleName); Expect(foundRole, Is.Not.Null); diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs index ab85e89..2979839 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs @@ -20,8 +20,6 @@ public IdentityRole(string roleName) : this() public string Name { get; set; } - // todo migration from legacy AspNet.Identity.MongoDB type? At least in docs - // lookup how normalization is provided OOB and use that for default case public string NormalizedName { get; set; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/README.md b/src/Microsoft.AspNetCore.Identity.MongoDB/README.md index 7d5389a..6e4cb60 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/README.md +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/README.md @@ -3,7 +3,8 @@ - roles names need to be normalized (user.roles) - Default uppercase - tell people if they customize this they have to deal with custom migration -- add IdentityRole.NormalizedName -- add IdentityUser.NormalizedUserName, IdentityUser.NormalizedEmail +- normalization by uppercase: + - add IdentityRole.NormalizedName + - add IdentityUser.NormalizedUserName, IdentityUser.NormalizedEmail - LockoutEndDateUtc - type changed in code, but I think it is still the same in db - \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs index 4d5eb3a..48ea086 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/RoleStore.cs @@ -61,11 +61,10 @@ public virtual async Task GetRoleNameAsync(TRole role, CancellationToken public virtual async Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken) => role.Name = roleName; - // todo testing? + // note: can't test as of yet through integration testing because the Identity framework doesn't use this method internally anywhere public virtual async Task GetNormalizedRoleNameAsync(TRole role, CancellationToken cancellationToken) => role.NormalizedName; - // todo testing? public virtual async Task SetNormalizedRoleNameAsync(TRole role, string normalizedName, CancellationToken cancellationToken) => role.NormalizedName = normalizedName; @@ -73,7 +72,6 @@ public virtual Task FindByIdAsync(string roleId, CancellationToken token) => _Roles.Find(r => r.Id == roleId) .FirstOrDefaultAsync(token); - // todo testing normalized? public virtual Task FindByNameAsync(string normalizedName, CancellationToken token) => _Roles.Find(r => r.NormalizedName == normalizedName) .FirstOrDefaultAsync(token); From f20dee2f2e7f7a2a45e2c4b07042de3e4a23692e Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Sat, 24 Sep 2016 13:01:48 -0400 Subject: [PATCH 26/56] Simple getter/setter testing for user --- src/CoreIntegrationTests/UserStoreTests.cs | 17 +++++++++++++++++ .../UserStore.cs | 10 ++-------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/CoreIntegrationTests/UserStoreTests.cs b/src/CoreIntegrationTests/UserStoreTests.cs index 3ff1b14..d2b03eb 100644 --- a/src/CoreIntegrationTests/UserStoreTests.cs +++ b/src/CoreIntegrationTests/UserStoreTests.cs @@ -99,5 +99,22 @@ public async Task Update_ExistingUser_Updates() Expect(changedUser, Is.Not.Null); Expect(changedUser.UserName, Is.EqualTo("newname")); } + + [Test] + public async Task SimpleAccessorsAndGetters() + { + var user = new IdentityUser + { + UserName = "username" + }; + var manager = GetUserManager(); + await manager.CreateAsync(user); + + Expect(await manager.GetUserIdAsync(user), Is.EqualTo(user.Id)); + Expect(await manager.GetUserNameAsync(user), Is.EqualTo("username")); + + await manager.SetUserNameAsync(user, "newUserName"); + Expect(await manager.GetUserNameAsync(user), Is.EqualTo("newUserName")); + } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs index 7b69dab..308334e 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs @@ -63,33 +63,27 @@ public virtual async Task DeleteAsync(TUser user, CancellationTo return IdentityResult.Success; } - // todo testing public virtual async Task GetUserIdAsync(TUser user, CancellationToken cancellationToken) => user.Id; - // todo testing public virtual async Task GetUserNameAsync(TUser user, CancellationToken cancellationToken) => user.UserName; - // todo testing public virtual async Task SetUserNameAsync(TUser user, string userName, CancellationToken cancellationToken) => user.UserName = userName; - // todo testing + // note: again this isn't used by Identity framework so no way to integration test it public virtual async Task GetNormalizedUserNameAsync(TUser user, CancellationToken cancellationToken) => user.NormalizedUserName; - // todo testing public virtual async Task SetNormalizedUserNameAsync(TUser user, string normalizedUserName, CancellationToken cancellationToken) => user.NormalizedUserName = normalizedUserName; - // todo testing public virtual Task FindByIdAsync(string userId, CancellationToken token) => _Users.Find(u => u.Id == userId).FirstOrDefaultAsync(token); public virtual Task FindByNameAsync(string normalizedUserName, CancellationToken token) - // todo test fails with normalized? - // todo exception on duplicates? or better to enforce unique index to ensure this + // todo low priority exception on duplicates? or better to enforce unique index to ensure this => _Users.Find(u => u.NormalizedUserName == normalizedUserName).FirstOrDefaultAsync(token); public virtual async Task SetPasswordHashAsync(TUser user, string passwordHash, CancellationToken token) From 8d077791db36c3463eb5ab2dcdbffa10a866951a Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Sat, 24 Sep 2016 13:29:28 -0400 Subject: [PATCH 27/56] Add GetUsersInRole test --- .../UserRoleStoreTests.cs | 19 +++++++++++++++++++ .../UserStore.cs | 1 - 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/CoreIntegrationTests/UserRoleStoreTests.cs b/src/CoreIntegrationTests/UserRoleStoreTests.cs index 3185fd0..13fe730 100644 --- a/src/CoreIntegrationTests/UserRoleStoreTests.cs +++ b/src/CoreIntegrationTests/UserRoleStoreTests.cs @@ -50,5 +50,24 @@ public async Task RemoveRole_Removes() Expect(savedUser.Roles, Is.Empty); Expect(await manager.IsInRoleAsync(user, "role"), Is.False); } + + [Test] + public async Task GetUsersInRole_FiltersOnRole() + { + var roleA = "roleA"; + var roleB = "roleB"; + var userInA = new IdentityUser {UserName = "nameA"}; + var userInB = new IdentityUser {UserName = "nameB"}; + var manager = GetUserManager(); + await manager.CreateAsync(userInA); + await manager.CreateAsync(userInB); + await manager.AddToRoleAsync(userInA, roleA); + await manager.AddToRoleAsync(userInB, roleB); + + var matchedUsers = await manager.GetUsersInRoleAsync("roleA"); + + Expect(matchedUsers.Count, Is.EqualTo(1)); + Expect(matchedUsers.First().UserName, Is.EqualTo("nameA")); + } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs index 308334e..5b1ad68 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs @@ -111,7 +111,6 @@ public virtual async Task> GetRolesAsync(TUser user, CancellationT public virtual async Task IsInRoleAsync(TUser user, string normalizedRoleName, CancellationToken token) => user.Roles.Contains(normalizedRoleName); - // todo testing public virtual async Task> GetUsersInRoleAsync(string normalizedRoleName, CancellationToken token) => await _Users.Find(u => u.Roles.Contains(normalizedRoleName)) .ToListAsync(token); From 03933525678945986a6091d61ac3404a3363fd83 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Sat, 24 Sep 2016 13:34:11 -0400 Subject: [PATCH 28/56] Validated these tests still work of Login functions --- src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs index 5b1ad68..b5ed719 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs @@ -115,15 +115,12 @@ public virtual async Task> GetUsersInRoleAsync(string normalizedRol => await _Users.Find(u => u.Roles.Contains(normalizedRoleName)) .ToListAsync(token); - // todo testing? public virtual async Task AddLoginAsync(TUser user, UserLoginInfo login, CancellationToken token) => user.AddLogin(login); - // todo testing? public virtual async Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken)) => user.RemoveLogin(loginProvider, providerKey); - // todo testing? public virtual async Task> GetLoginsAsync(TUser user, CancellationToken token) => user.Logins; From c48e2b5b2dbe32c5c5bb96d50e2d9d1cf84a1f84 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Sat, 24 Sep 2016 13:41:46 -0400 Subject: [PATCH 29/56] Woops on not awaiting :) --- src/CoreIntegrationTests/UserLoginStoreTests.cs | 2 +- src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/CoreIntegrationTests/UserLoginStoreTests.cs b/src/CoreIntegrationTests/UserLoginStoreTests.cs index 7e3b723..9908dd5 100644 --- a/src/CoreIntegrationTests/UserLoginStoreTests.cs +++ b/src/CoreIntegrationTests/UserLoginStoreTests.cs @@ -68,7 +68,7 @@ public async Task Find_UserWithLogin_FindsUser() await manager.CreateAsync(user); await manager.AddLoginAsync(user, login); - var findUser = manager.FindByLoginAsync(login.LoginProvider, login.ProviderKey); + var findUser = await manager.FindByLoginAsync(login.LoginProvider, login.ProviderKey); Expect(findUser, Is.Not.Null); } diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs index b5ed719..e14843f 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs @@ -124,7 +124,6 @@ public virtual async Task AddLoginAsync(TUser user, UserLoginInfo login, Cancell public virtual async Task> GetLoginsAsync(TUser user, CancellationToken token) => user.Logins; - // todo testing? public virtual Task FindByLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken)) => _Users .Find(u => u.Logins.Any(l => l.LoginProvider == loginProvider && l.ProviderKey == providerKey)) From 27a8a6758fd38e1f2cbee003e028a0c90f2f6ed7 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Sat, 24 Sep 2016 14:04:02 -0400 Subject: [PATCH 30/56] Tests ok --- src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs index e14843f..c4a1e9d 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs @@ -167,7 +167,6 @@ public virtual Task FindByEmailAsync(string normalizedEmail, Cancellation public virtual async Task> GetClaimsAsync(TUser user, CancellationToken token) => user.Claims.Select(c => c.ToSecurityClaim()).ToList(); - // todo testing ok? public virtual Task AddClaimsAsync(TUser user, IEnumerable claims, CancellationToken token) { foreach (var claim in claims) @@ -177,7 +176,6 @@ public virtual Task AddClaimsAsync(TUser user, IEnumerable claims, Cancel return Task.FromResult(0); } - // todo testing ok? public virtual Task RemoveClaimsAsync(TUser user, IEnumerable claims, CancellationToken token) { foreach (var claim in claims) From a96b24baafa3b507288f7eb02f2c49f616d9e1e4 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Sat, 24 Sep 2016 14:17:04 -0400 Subject: [PATCH 31/56] todos to validate tests still work after migration to async... takes so long with current test runner for NET Core --- src/CoreIntegrationTests/UserLockoutStoreTests.cs | 1 + src/CoreIntegrationTests/UserLoginStoreTests.cs | 1 + src/CoreIntegrationTests/UserPasswordStoreTests.cs | 1 + src/CoreIntegrationTests/UserPhoneNumberStoreTests.cs | 1 + src/CoreIntegrationTests/UserRoleStoreTests.cs | 1 + src/CoreIntegrationTests/UserSecurityStampStoreTests.cs | 1 + src/CoreIntegrationTests/UserStoreTests.cs | 1 + src/CoreIntegrationTests/UserTwoFactorStoreTests.cs | 1 + src/CoreTests/IdentityRoleTests.cs | 1 + src/CoreTests/IdentityUserClaimTests.cs | 1 + src/CoreTests/IdentityUserTests.cs | 1 + 11 files changed, 11 insertions(+) diff --git a/src/CoreIntegrationTests/UserLockoutStoreTests.cs b/src/CoreIntegrationTests/UserLockoutStoreTests.cs index 3e45005..398ba88 100644 --- a/src/CoreIntegrationTests/UserLockoutStoreTests.cs +++ b/src/CoreIntegrationTests/UserLockoutStoreTests.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; + // todo low - validate all tests work [TestFixture] public class UserLockoutStoreTests : UserIntegrationTestsBase { diff --git a/src/CoreIntegrationTests/UserLoginStoreTests.cs b/src/CoreIntegrationTests/UserLoginStoreTests.cs index 9908dd5..1981fc7 100644 --- a/src/CoreIntegrationTests/UserLoginStoreTests.cs +++ b/src/CoreIntegrationTests/UserLoginStoreTests.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Identity.MongoDB; using NUnit.Framework; + // todo low - validate all tests work [TestFixture] public class UserLoginStoreTests : UserIntegrationTestsBase { diff --git a/src/CoreIntegrationTests/UserPasswordStoreTests.cs b/src/CoreIntegrationTests/UserPasswordStoreTests.cs index 94fce58..c58d271 100644 --- a/src/CoreIntegrationTests/UserPasswordStoreTests.cs +++ b/src/CoreIntegrationTests/UserPasswordStoreTests.cs @@ -7,6 +7,7 @@ using Microsoft.Extensions.DependencyInjection; using NUnit.Framework; + // todo low - validate all tests work [TestFixture] public class UserPasswordStoreTests : UserIntegrationTestsBase { diff --git a/src/CoreIntegrationTests/UserPhoneNumberStoreTests.cs b/src/CoreIntegrationTests/UserPhoneNumberStoreTests.cs index d2cd83a..9e8fed9 100644 --- a/src/CoreIntegrationTests/UserPhoneNumberStoreTests.cs +++ b/src/CoreIntegrationTests/UserPhoneNumberStoreTests.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Identity.MongoDB; using NUnit.Framework; + // todo low - validate all tests work [TestFixture] public class UserPhoneNumberStoreTests : UserIntegrationTestsBase { diff --git a/src/CoreIntegrationTests/UserRoleStoreTests.cs b/src/CoreIntegrationTests/UserRoleStoreTests.cs index 13fe730..407b237 100644 --- a/src/CoreIntegrationTests/UserRoleStoreTests.cs +++ b/src/CoreIntegrationTests/UserRoleStoreTests.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Identity.MongoDB; using NUnit.Framework; + // todo low - validate all tests work [TestFixture] public class UserRoleStoreTests : UserIntegrationTestsBase { diff --git a/src/CoreIntegrationTests/UserSecurityStampStoreTests.cs b/src/CoreIntegrationTests/UserSecurityStampStoreTests.cs index b02e1de..e74c5c8 100644 --- a/src/CoreIntegrationTests/UserSecurityStampStoreTests.cs +++ b/src/CoreIntegrationTests/UserSecurityStampStoreTests.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Identity.MongoDB; using NUnit.Framework; + // todo low - validate all tests work [TestFixture] public class UserSecurityStampStoreTests : UserIntegrationTestsBase { diff --git a/src/CoreIntegrationTests/UserStoreTests.cs b/src/CoreIntegrationTests/UserStoreTests.cs index d2b03eb..348821c 100644 --- a/src/CoreIntegrationTests/UserStoreTests.cs +++ b/src/CoreIntegrationTests/UserStoreTests.cs @@ -6,6 +6,7 @@ using MongoDB.Bson; using NUnit.Framework; + // todo low - validate all tests work [TestFixture] public class UserStoreTests : UserIntegrationTestsBase { diff --git a/src/CoreIntegrationTests/UserTwoFactorStoreTests.cs b/src/CoreIntegrationTests/UserTwoFactorStoreTests.cs index eb26080..fd12b4a 100644 --- a/src/CoreIntegrationTests/UserTwoFactorStoreTests.cs +++ b/src/CoreIntegrationTests/UserTwoFactorStoreTests.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Identity.MongoDB; using NUnit.Framework; + // todo low - validate all tests work [TestFixture] public class UserTwoFactorStoreTests : UserIntegrationTestsBase { diff --git a/src/CoreTests/IdentityRoleTests.cs b/src/CoreTests/IdentityRoleTests.cs index 6e09b8d..1e9513f 100644 --- a/src/CoreTests/IdentityRoleTests.cs +++ b/src/CoreTests/IdentityRoleTests.cs @@ -4,6 +4,7 @@ using MongoDB.Bson; using NUnit.Framework; + // todo low - validate all tests work [TestFixture] public class IdentityRoleTests : AssertionHelper { diff --git a/src/CoreTests/IdentityUserClaimTests.cs b/src/CoreTests/IdentityUserClaimTests.cs index 9dd1820..96a2f68 100644 --- a/src/CoreTests/IdentityUserClaimTests.cs +++ b/src/CoreTests/IdentityUserClaimTests.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Identity.MongoDB; using NUnit.Framework; + // todo low - validate all tests work [TestFixture] public class IdentityUserClaimTests : AssertionHelper { diff --git a/src/CoreTests/IdentityUserTests.cs b/src/CoreTests/IdentityUserTests.cs index 4c53887..77b0e66 100644 --- a/src/CoreTests/IdentityUserTests.cs +++ b/src/CoreTests/IdentityUserTests.cs @@ -4,6 +4,7 @@ using MongoDB.Bson; using NUnit.Framework; + // todo low - validate all tests work [TestFixture] public class IdentityUserTests : AssertionHelper { From d4fe88bd0cc71dabd49247a354e9f6e428e683a4 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Sat, 24 Sep 2016 14:21:16 -0400 Subject: [PATCH 32/56] tests ok --- src/CoreIntegrationTests/UserSecurityStampStoreTests.cs | 1 - src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs | 9 +++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/CoreIntegrationTests/UserSecurityStampStoreTests.cs b/src/CoreIntegrationTests/UserSecurityStampStoreTests.cs index e74c5c8..b02e1de 100644 --- a/src/CoreIntegrationTests/UserSecurityStampStoreTests.cs +++ b/src/CoreIntegrationTests/UserSecurityStampStoreTests.cs @@ -5,7 +5,6 @@ using Microsoft.AspNetCore.Identity.MongoDB; using NUnit.Framework; - // todo low - validate all tests work [TestFixture] public class UserSecurityStampStoreTests : UserIntegrationTestsBase { diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs index c4a1e9d..30f9569 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs @@ -129,11 +129,9 @@ public virtual async Task> GetLoginsAsync(TUser user, Cance .Find(u => u.Logins.Any(l => l.LoginProvider == loginProvider && l.ProviderKey == providerKey)) .FirstOrDefaultAsync(cancellationToken); - // todo testing? public virtual async Task SetSecurityStampAsync(TUser user, string stamp, CancellationToken token) => user.SecurityStamp = stamp; - // todo testing? public virtual async Task GetSecurityStampAsync(TUser user, CancellationToken token) => user.SecurityStamp; @@ -149,18 +147,17 @@ public virtual async Task SetEmailAsync(TUser user, string email, CancellationTo public virtual async Task GetEmailAsync(TUser user, CancellationToken token) => user.Email; - // todo testing + // note: no way to intergation test as this isn't used by Identity framework public virtual async Task GetNormalizedEmailAsync(TUser user, CancellationToken cancellationToken) => user.NormalizedEmail; - // todo testing public virtual async Task SetNormalizedEmailAsync(TUser user, string normalizedEmail, CancellationToken cancellationToken) => user.NormalizedEmail = normalizedEmail; public virtual Task FindByEmailAsync(string normalizedEmail, CancellationToken token) { - // todo I don't like that this now searches on normalized email :(... why not FindByNormalizedEmailAsync then? - // todo what if a user can have multiple accounts with the same email? + // note: I don't like that this now searches on normalized email :(... why not FindByNormalizedEmailAsync then? + // todo low - what if a user can have multiple accounts with the same email? return _Users.Find(u => u.NormalizedEmail == normalizedEmail).FirstOrDefaultAsync(token); } From 0411dc57fa2b3b84287b9dfe619965154d2b6899 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Sat, 24 Sep 2016 15:02:52 -0400 Subject: [PATCH 33/56] Add tests for replacing claims --- .../UserClaimStoreTests.cs | 17 ++++++ src/CoreTests/IdentityUserClaimTests.cs | 52 ++++++++++++++++++- src/CoreTests/TestExtensions.cs | 18 +++++++ .../IdentityUser.cs | 18 +++---- .../UserStore.cs | 1 - 5 files changed, 94 insertions(+), 12 deletions(-) create mode 100644 src/CoreTests/TestExtensions.cs diff --git a/src/CoreIntegrationTests/UserClaimStoreTests.cs b/src/CoreIntegrationTests/UserClaimStoreTests.cs index 649ae96..509ed4c 100644 --- a/src/CoreIntegrationTests/UserClaimStoreTests.cs +++ b/src/CoreIntegrationTests/UserClaimStoreTests.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Identity.MongoDB; using NUnit.Framework; + using Tests; [TestFixture] public class UserClaimStoreTests : UserIntegrationTestsBase @@ -73,5 +74,21 @@ public async Task RemoveClaim_DifferentValue_DoesNotRemoveClaim() Expect(await manager.GetClaimsAsync(user), Is.Not.Empty); } + + [Test] + public async Task ReplaceClaim_Replaces() + { + // note: unit tests cover behavior of ReplaceClaim method on IdentityUser + var user = new IdentityUser {UserName = "bob"}; + var manager = GetUserManager(); + await manager.CreateAsync(user); + var existingClaim = new Claim("type", "value"); + await manager.AddClaimAsync(user, existingClaim); + var newClaim = new Claim("newType", "newValue"); + + await manager.ReplaceClaimAsync(user, existingClaim, newClaim); + + user.ExpectOnlyHasThisClaim(newClaim); + } } } \ No newline at end of file diff --git a/src/CoreTests/IdentityUserClaimTests.cs b/src/CoreTests/IdentityUserClaimTests.cs index 96a2f68..e6dc137 100644 --- a/src/CoreTests/IdentityUserClaimTests.cs +++ b/src/CoreTests/IdentityUserClaimTests.cs @@ -4,7 +4,6 @@ using Microsoft.AspNetCore.Identity.MongoDB; using NUnit.Framework; - // todo low - validate all tests work [TestFixture] public class IdentityUserClaimTests : AssertionHelper { @@ -29,5 +28,56 @@ public void ToSecurityClaim_SetsTypeAndValue() Expect(claim.Type, Is.EqualTo("t")); Expect(claim.Value, Is.EqualTo("v")); } + + [Test] + public void ReplaceClaim_NoExistingClaim_Ignores() + { + // note: per EF implemention - only existing claims are updated by looping through them so that impl ignores too + var user = new IdentityUser(); + var newClaim = new Claim("newType", "newValue"); + + user.ReplaceClaim(newClaim, newClaim); + + Expect(user.Claims, Is.Empty); + } + + [Test] + public void ReplaceClaim_ExistingClaim_Replaces() + { + var user = new IdentityUser(); + var firstClaim = new Claim("type", "value"); + user.AddClaim(firstClaim); + var newClaim = new Claim("newType", "newValue"); + + user.ReplaceClaim(firstClaim, newClaim); + + user.ExpectOnlyHasThisClaim(newClaim); + } + + [Test] + public void ReplaceClaim_ValueMatchesButTypeDoesNot_DoesNotReplace() + { + var user = new IdentityUser(); + var firstClaim = new Claim("type", "sameValue"); + user.AddClaim(firstClaim); + var newClaim = new Claim("newType", "sameValue"); + + user.ReplaceClaim(new Claim("wrongType", "sameValue"), newClaim); + + user.ExpectOnlyHasThisClaim(firstClaim); + } + + [Test] + public void ReplaceClaim_TypeMatchesButValueDoesNot_DoesNotReplace() + { + var user = new IdentityUser(); + var firstClaim = new Claim("sameType", "value"); + user.AddClaim(firstClaim); + var newClaim = new Claim("sameType", "newValue"); + + user.ReplaceClaim(new Claim("sameType", "wrongValue"), newClaim); + + user.ExpectOnlyHasThisClaim(firstClaim); + } } } \ No newline at end of file diff --git a/src/CoreTests/TestExtensions.cs b/src/CoreTests/TestExtensions.cs new file mode 100644 index 0000000..1e0795f --- /dev/null +++ b/src/CoreTests/TestExtensions.cs @@ -0,0 +1,18 @@ +namespace Tests +{ + using System.Linq; + using System.Security.Claims; + using Microsoft.AspNetCore.Identity.MongoDB; + using NUnit.Framework; + + public static class TestExtensions + { + public static void ExpectOnlyHasThisClaim(this IdentityUser user, Claim expectedClaim) + { + AssertionHelper.Expect(user.Claims.Count, Is.EqualTo(1)); + var actualClaim = user.Claims.Single(); + AssertionHelper.Expect(actualClaim.Type, Is.EqualTo(expectedClaim.Type)); + AssertionHelper.Expect(actualClaim.Value, Is.EqualTo(expectedClaim.Value)); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs index 840f266..ca1397f 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs @@ -30,8 +30,8 @@ public IdentityUser() public virtual string NormalizedUserName { get; set; } /// - /// A random value that must change whenever a users credentials change - /// (password changed, login removed) + /// A random value that must change whenever a users credentials change + /// (password changed, login removed) /// public virtual string SecurityStamp { get; set; } @@ -72,7 +72,6 @@ public virtual void RemoveRole(string role) [BsonIgnoreIfNull] public virtual string PasswordHash { get; set; } - // todo move to a type I manage - and check for changes to UserLoginInfo for migration purposes // todo I know that displayName was added [BsonIgnoreIfNull] @@ -107,17 +106,16 @@ public virtual void RemoveClaim(Claim claim) Claims.RemoveAll(c => c.Type == claim.Type && c.Value == claim.Value); } - // todo testing? - public virtual void ReplaceClaim(Claim claim, Claim newClaim) + public virtual void ReplaceClaim(Claim existingClaim, Claim newClaim) { - var current = Claims - .FirstOrDefault(c => c.Type == claim.Type && c.Value == claim.Value); - if (current == null) + var claimExists = Claims + .Any(c => c.Type == existingClaim.Type && c.Value == existingClaim.Value); + if (!claimExists) { - //todo? + // note: nothing to update, ignore, no need to throw return; } - RemoveClaim(claim); + RemoveClaim(existingClaim); AddClaim(newClaim); } diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs index 30f9569..dd9d25c 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs @@ -182,7 +182,6 @@ public virtual Task RemoveClaimsAsync(TUser user, IEnumerable claims, Can return Task.FromResult(0); } - // todo testing public virtual async Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken)) { user.ReplaceClaim(claim, newClaim); From 488345f8ab4f825ec0b01e28da1b41ed5207a39b Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Sat, 24 Sep 2016 17:39:32 -0400 Subject: [PATCH 34/56] Add tests for GetUsersForClaimAsync --- .../UserClaimStoreTests.cs | 26 +++++++++++++++++++ .../UserStore.cs | 2 -- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/CoreIntegrationTests/UserClaimStoreTests.cs b/src/CoreIntegrationTests/UserClaimStoreTests.cs index 509ed4c..fe8adff 100644 --- a/src/CoreIntegrationTests/UserClaimStoreTests.cs +++ b/src/CoreIntegrationTests/UserClaimStoreTests.cs @@ -90,5 +90,31 @@ public async Task ReplaceClaim_Replaces() user.ExpectOnlyHasThisClaim(newClaim); } + + [Test] + public async Task GetUsersForClaim() + { + var userWithClaim = new IdentityUser + { + UserName = "with" + }; + var userWithout = new IdentityUser(); + var manager = GetUserManager(); + await manager.CreateAsync(userWithClaim); + await manager.CreateAsync(userWithout); + var claim = new Claim("sameType", "sameValue"); + await manager.AddClaimAsync(userWithClaim, claim); + + var matchedUsers = await manager.GetUsersForClaimAsync(claim); + + Expect(matchedUsers.Count, Is.EqualTo(1)); + Expect(matchedUsers.Single().UserName, Is.EqualTo("with")); + + var matchesForWrongType = await manager.GetUsersForClaimAsync(new Claim("wrongType", "sameValue")); + Expect(matchesForWrongType, Is.Empty, "Users with claim with wrongType should not be returned but were."); + + var matchesForWrongValue = await manager.GetUsersForClaimAsync(new Claim("sameType", "wrongValue")); + Expect(matchesForWrongValue, Is.Empty, "Users with claim with wrongValue should not be returned but were."); + } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs index dd9d25c..348f361 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs @@ -220,11 +220,9 @@ public virtual Task GetTwoFactorEnabledAsync(TUser user, CancellationToken return Task.FromResult(user.TwoFactorEnabled); } - // todo testing public virtual async Task> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken)) { return await _Users - // todo integration test .Find(u => u.Claims.Any(c => c.Type == claim.Type && c.Value == claim.Value)) .ToListAsync(cancellationToken); } From fb134ecfa2e7387e9cf515d205eff89cc34f4614 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Sat, 24 Sep 2016 22:23:03 -0400 Subject: [PATCH 35/56] These are tested --- src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs index 348f361..380306a 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs @@ -227,14 +227,12 @@ public virtual Task GetTwoFactorEnabledAsync(TUser user, CancellationToken .ToListAsync(cancellationToken); } - // todo testing? public virtual Task GetLockoutEndDateAsync(TUser user, CancellationToken token) { // todo migration? return Task.FromResult(user.LockoutEndDateUtc); } - // todo testing? public virtual Task SetLockoutEndDateAsync(TUser user, DateTimeOffset? lockoutEnd, CancellationToken token) { user.LockoutEndDateUtc = lockoutEnd; @@ -256,11 +254,9 @@ public virtual Task ResetAccessFailedCountAsync(TUser user, CancellationToken to public virtual async Task GetAccessFailedCountAsync(TUser user, CancellationToken token) => user.AccessFailedCount; - // todo testing? public virtual async Task GetLockoutEnabledAsync(TUser user, CancellationToken token) => user.LockoutEnabled; - // todo testing? public virtual async Task SetLockoutEnabledAsync(TUser user, bool enabled, CancellationToken token) => user.LockoutEnabled = enabled; From 351692877758bc1506ea51e0f6d7120f574da0bc Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Sat, 24 Sep 2016 22:33:56 -0400 Subject: [PATCH 36/56] Add testing of UserAuthenticationTokenStore --- .../UserAuthenticationTokenStoreTests.cs | 28 ++++++++ .../IdentityUserAuthenticationTokenTests.cs | 66 +++++++++++++++++++ src/CoreTests/IdentityUserTests.cs | 55 ++++------------ .../IdentityUser.cs | 9 +-- .../UserStore.cs | 3 - 5 files changed, 108 insertions(+), 53 deletions(-) create mode 100644 src/CoreIntegrationTests/UserAuthenticationTokenStoreTests.cs create mode 100644 src/CoreTests/IdentityUserAuthenticationTokenTests.cs diff --git a/src/CoreIntegrationTests/UserAuthenticationTokenStoreTests.cs b/src/CoreIntegrationTests/UserAuthenticationTokenStoreTests.cs new file mode 100644 index 0000000..487af66 --- /dev/null +++ b/src/CoreIntegrationTests/UserAuthenticationTokenStoreTests.cs @@ -0,0 +1,28 @@ +namespace CoreIntegrationTests +{ + using System.Threading.Tasks; + using IntegrationTests; + using Microsoft.AspNetCore.Identity.MongoDB; + using NUnit.Framework; + + public class UserAuthenticationTokenStoreTests : UserIntegrationTestsBase + { + [Test] + public async Task SetGetAndRemoveTokens() + { + // note: this is just an integration test, testing of IdentityUser behavior is in domain/unit tests + var user = new IdentityUser(); + var manager = GetUserManager(); + await manager.CreateAsync(user); + + await manager.SetAuthenticationTokenAsync(user, "loginProvider", "tokenName", "tokenValue"); + + var tokenValue = await manager.GetAuthenticationTokenAsync(user, "loginProvider", "tokenName"); + Expect(tokenValue, Is.EqualTo("tokenValue")); + + await manager.RemoveAuthenticationTokenAsync(user, "loginProvider", "tokenName"); + var afterRemovedValue = await manager.GetAuthenticationTokenAsync(user, "loginProvider", "tokenName"); + Expect(afterRemovedValue, Is.Null); + } + } +} \ No newline at end of file diff --git a/src/CoreTests/IdentityUserAuthenticationTokenTests.cs b/src/CoreTests/IdentityUserAuthenticationTokenTests.cs new file mode 100644 index 0000000..d774990 --- /dev/null +++ b/src/CoreTests/IdentityUserAuthenticationTokenTests.cs @@ -0,0 +1,66 @@ +namespace CoreTests +{ + using Microsoft.AspNetCore.Identity.MongoDB; + using NUnit.Framework; + + public class IdentityUserAuthenticationTokenTests : AssertionHelper + { + [Test] + public void GetToken_NoTokens_ReturnsNull() + { + var user = new IdentityUser(); + + var value = user.GetTokenValue("loginProvider", "tokenName"); + + Expect(value, Is.Null); + } + + [Test] + public void GetToken_WithToken_ReturnsValueIfProviderAndNameMatch() + { + var user = new IdentityUser(); + user.SetToken("loginProvider", "tokenName", "tokenValue"); + + Expect(user.GetTokenValue("loginProvider", "tokenName"), + Is.EqualTo("tokenValue"), "GetToken should match on both provider and name, but isn't"); + + Expect(user.GetTokenValue("wrongProvider", "tokenName"), + Is.Null, "GetToken should match on loginProvider, but isn't"); + + Expect(user.GetTokenValue("loginProvider", "wrongName"), + Is.Null, "GetToken should match on tokenName, but isn't"); + } + + [Test] + public void RemoveToken_OnlyRemovesIfNameAndProviderMatch() + { + var user = new IdentityUser(); + user.SetToken("loginProvider", "tokenName", "tokenValue"); + + user.RemoveToken("wrongProvider", "tokenName"); + Expect(user.GetTokenValue("loginProvider", "tokenName"), + Is.EqualTo("tokenValue"), "RemoveToken should match on loginProvider, but isn't"); + + user.RemoveToken("loginProvider", "wrongName"); + Expect(user.GetTokenValue("loginProvider", "tokenName"), + Is.EqualTo("tokenValue"), "RemoveToken should match on tokenName, but isn't"); + + user.RemoveToken("loginProvider", "tokenName"); + Expect(user.GetTokenValue("loginProvider", "tokenName"), + Is.Null, "RemoveToken should match on both loginProvider and tokenName, but isn't"); + } + + [Test] + public void SetToken_ReplacesValue() + { + var user = new IdentityUser(); + user.SetToken("loginProvider", "tokenName", "tokenValue"); + + user.SetToken("loginProvider", "tokenName", "updatedValue"); + + Expect(user.Tokens.Count, Is.EqualTo(1)); + Expect(user.GetTokenValue("loginProvider", "tokenName"), + Is.EqualTo("updatedValue")); + } + } +} \ No newline at end of file diff --git a/src/CoreTests/IdentityUserTests.cs b/src/CoreTests/IdentityUserTests.cs index 77b0e66..a8538a0 100644 --- a/src/CoreTests/IdentityUserTests.cs +++ b/src/CoreTests/IdentityUserTests.cs @@ -41,65 +41,34 @@ public void Create_NoPassword_DoesNotSerializePasswordField() } [Test] - public void Create_NewIdentityUser_RolesNotNull() - { - var user = new IdentityUser(); - - Expect(user.Roles, Is.Not.Null); - } - - [Test] - public void Create_NullRoles_DoesNotSerializeRoles() + public void Create_NullLists_DoesNotSerializeNullLists() { // serialized nulls can cause havoc in deserialization, overwriting the constructor's initial empty list var user = new IdentityUser(); user.Roles = null; - - var document = user.ToBsonDocument(); - - Expect(document.Contains("Roles"), Is.False); - } - - // todo consider if we want to not serialize the empty Roles array, also empty Logins array - - [Test] - public void Create_NewIdentityUser_LoginsNotNull() - { - var user = new IdentityUser(); - - Expect(user.Logins, Is.Not.Null); - } - - [Test] - public void Create_NullLogins_DoesNotSerializeLogins() - { - // serialized nulls can cause havoc in deserialization, overwriting the constructor's initial empty list - var user = new IdentityUser(); + user.Tokens = null; user.Logins = null; + user.Claims = null; var document = user.ToBsonDocument(); + Expect(document.Contains("Roles"), Is.False); + Expect(document.Contains("Tokens"), Is.False); Expect(document.Contains("Logins"), Is.False); + Expect(document.Contains("Claims"), Is.False); } - [Test] - public void Create_NewIdentityUser_ClaimsNotNull() - { - var user = new IdentityUser(); - - Expect(user.Claims, Is.Not.Null); - } + // todo consider if we want to not serialize the empty Roles array, also empty Logins array [Test] - public void Create_NullClaims_DoesNotSerializeClaims() + public void Create_NewIdentityUser_ListsNotNull() { - // serialized nulls can cause havoc in deserialization, overwriting the constructor's initial empty list var user = new IdentityUser(); - user.Claims = null; - - var document = user.ToBsonDocument(); - Expect(document.Contains("Claims"), Is.False); + Expect(user.Logins, Is.Empty); + Expect(user.Tokens, Is.Empty); + Expect(user.Roles, Is.Empty); + Expect(user.Claims, Is.Empty); } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs index ca1397f..6be2f0b 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs @@ -15,7 +15,6 @@ public IdentityUser() Roles = new List(); Logins = new List(); Claims = new List(); - // todo testing token init Tokens = new List(); } @@ -82,7 +81,6 @@ public virtual void AddLogin(UserLoginInfo login) Logins.Add(login); } - // todo testing? public virtual void RemoveLogin(string loginProvider, string providerKey) { Logins.RemoveAll(l => l.LoginProvider == loginProvider && l.ProviderKey == providerKey); @@ -123,11 +121,10 @@ public virtual void ReplaceClaim(Claim existingClaim, Claim newClaim) [BsonIgnoreIfNull] public virtual List Tokens { get; set; } - // todo testing? private IdentityUserToken GetToken(string loginProider, string name) - => Tokens.FirstOrDefault(t => t.LoginProvider == loginProider && t.Name == name); + => Tokens + .FirstOrDefault(t => t.LoginProvider == loginProider && t.Name == name); - // todo testing of tokens, what are these for? public virtual void SetToken(string loginProider, string name, string value) { var existingToken = GetToken(loginProider, name); @@ -145,13 +142,11 @@ public virtual void SetToken(string loginProider, string name, string value) }); } - // todo testing? public virtual string GetTokenValue(string loginProider, string name) { return GetToken(loginProider, name)?.Value; } - // todo testing? public virtual void RemoveToken(string loginProvider, string name) { Tokens.RemoveAll(t => t.LoginProvider == loginProvider && t.Name == name); diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs index 380306a..453410e 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs @@ -262,15 +262,12 @@ public virtual async Task SetLockoutEnabledAsync(TUser user, bool enabled, Cance public virtual IQueryable Users => _Users.AsQueryable(); - // todo testing public virtual async Task SetTokenAsync(TUser user, string loginProvider, string name, string value, CancellationToken cancellationToken) => user.SetToken(loginProvider, name, value); - // todo testing public virtual async Task RemoveTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken) => user.RemoveToken(loginProvider, name); - // todo testing public virtual async Task GetTokenAsync(TUser user, string loginProvider, string name, CancellationToken cancellationToken) => user.GetTokenValue(loginProvider, name); } From 70caa464f6273d0777d9dbaf8e1ded7edb2d49c2 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Sat, 24 Sep 2016 23:05:57 -0400 Subject: [PATCH 37/56] review todos --- .../EnsureWeCanExtendIdentityUserTests.cs | 1 - src/CoreIntegrationTests/UserLockoutStoreTests.cs | 1 - src/CoreIntegrationTests/UserLoginStoreTests.cs | 4 +--- src/CoreIntegrationTests/UserRoleStoreTests.cs | 1 - src/CoreTests/IdentityUserTests.cs | 2 -- src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs | 8 -------- src/Microsoft.AspNetCore.Identity.MongoDB/README.md | 3 ++- src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs | 1 - 8 files changed, 3 insertions(+), 18 deletions(-) diff --git a/src/CoreIntegrationTests/EnsureWeCanExtendIdentityUserTests.cs b/src/CoreIntegrationTests/EnsureWeCanExtendIdentityUserTests.cs index bc89ace..d451074 100644 --- a/src/CoreIntegrationTests/EnsureWeCanExtendIdentityUserTests.cs +++ b/src/CoreIntegrationTests/EnsureWeCanExtendIdentityUserTests.cs @@ -34,7 +34,6 @@ public async Task Create_ExtendedUserType_SavesExtraFields() { _User.ExtendedField = "extendedField"; - // todo note: async methods dropped in Identity 3 await _Manager.CreateAsync(_User); var savedUser = Users.FindAllAs().Single(); diff --git a/src/CoreIntegrationTests/UserLockoutStoreTests.cs b/src/CoreIntegrationTests/UserLockoutStoreTests.cs index 398ba88..8e99159 100644 --- a/src/CoreIntegrationTests/UserLockoutStoreTests.cs +++ b/src/CoreIntegrationTests/UserLockoutStoreTests.cs @@ -26,7 +26,6 @@ public async Task AccessFailed_IncrementsAccessFailedCount() private UserManager GetUserManagerWithThreeMaxAccessAttempts() { - // todo is this really needed to set Max Failed Attempts? return CreateServiceProvider(options => options.Lockout.MaxFailedAccessAttempts = 3) .GetService>(); } diff --git a/src/CoreIntegrationTests/UserLoginStoreTests.cs b/src/CoreIntegrationTests/UserLoginStoreTests.cs index 1981fc7..5d987d5 100644 --- a/src/CoreIntegrationTests/UserLoginStoreTests.cs +++ b/src/CoreIntegrationTests/UserLoginStoreTests.cs @@ -14,7 +14,6 @@ public class UserLoginStoreTests : UserIntegrationTestsBase public async Task AddLogin_NewLogin_Adds() { var manager = GetUserManager(); - // todo what's this new displayName for userLoginInfo var login = new UserLoginInfo("provider", "key", "name"); var user = new IdentityUser {UserName = "bob"}; await manager.CreateAsync(user); @@ -24,7 +23,6 @@ public async Task AddLogin_NewLogin_Adds() var savedLogin = Users.FindAll().Single().Logins.Single(); Expect(savedLogin.LoginProvider, Is.EqualTo("provider")); Expect(savedLogin.ProviderKey, Is.EqualTo("key")); - // todo test displayname, what about migrating this? Expect(savedLogin.ProviderDisplayName, Is.EqualTo("name")); } @@ -57,7 +55,7 @@ public async Task GetLogins_OneLogin_ReturnsLogin() var savedLogin = logins.Single(); Expect(savedLogin.LoginProvider, Is.EqualTo("provider")); Expect(savedLogin.ProviderKey, Is.EqualTo("key")); - // todo name + Expect(savedLogin.ProviderDisplayName, Is.EqualTo("name")); } [Test] diff --git a/src/CoreIntegrationTests/UserRoleStoreTests.cs b/src/CoreIntegrationTests/UserRoleStoreTests.cs index 407b237..bdf4927 100644 --- a/src/CoreIntegrationTests/UserRoleStoreTests.cs +++ b/src/CoreIntegrationTests/UserRoleStoreTests.cs @@ -28,7 +28,6 @@ public async Task AddRole_Adds() var user = new IdentityUser {UserName = "bob"}; await manager.CreateAsync(user); - // todo it would be nice if the API for UserManager asked for a normalized name and not just a name await manager.AddToRoleAsync(user, "role"); var savedUser = Users.FindAll().Single(); diff --git a/src/CoreTests/IdentityUserTests.cs b/src/CoreTests/IdentityUserTests.cs index a8538a0..3f1a59b 100644 --- a/src/CoreTests/IdentityUserTests.cs +++ b/src/CoreTests/IdentityUserTests.cs @@ -58,8 +58,6 @@ public void Create_NullLists_DoesNotSerializeNullLists() Expect(document.Contains("Claims"), Is.False); } - // todo consider if we want to not serialize the empty Roles array, also empty Logins array - [Test] public void Create_NewIdentityUser_ListsNotNull() { diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs index 6be2f0b..f760491 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs @@ -23,9 +23,6 @@ public IdentityUser() public virtual string UserName { get; set; } - // todo what should we do with this in Mongo land - // https://github.com/aspnet/Identity/issues/351 - // todo migration public virtual string NormalizedUserName { get; set; } /// @@ -36,7 +33,6 @@ public IdentityUser() public virtual string Email { get; set; } - // todo migration public virtual string NormalizedEmail { get; set; } public virtual bool EmailConfirmed { get; set; } @@ -47,8 +43,6 @@ public IdentityUser() public virtual bool TwoFactorEnabled { get; set; } - // todo migration - // ef has LockoutEnd ... what was this before? public virtual DateTimeOffset? LockoutEndDateUtc { get; set; } public virtual bool LockoutEnabled { get; set; } @@ -72,7 +66,6 @@ public virtual void RemoveRole(string role) public virtual string PasswordHash { get; set; } // todo move to a type I manage - and check for changes to UserLoginInfo for migration purposes - // todo I know that displayName was added [BsonIgnoreIfNull] public virtual List Logins { get; set; } @@ -117,7 +110,6 @@ public virtual void ReplaceClaim(Claim existingClaim, Claim newClaim) AddClaim(newClaim); } - // todo testing? [BsonIgnoreIfNull] public virtual List Tokens { get; set; } diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/README.md b/src/Microsoft.AspNetCore.Identity.MongoDB/README.md index 6e4cb60..3e83133 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/README.md +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/README.md @@ -7,4 +7,5 @@ - add IdentityRole.NormalizedName - add IdentityUser.NormalizedUserName, IdentityUser.NormalizedEmail - LockoutEndDateUtc - type changed in code, but I think it is still the same in db -- \ No newline at end of file + +- Should't cause a problem, but FYI, UserLoginInfo.ProviderDisplayName was added diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs index 453410e..700a0ff 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs @@ -229,7 +229,6 @@ public virtual Task GetTwoFactorEnabledAsync(TUser user, CancellationToken public virtual Task GetLockoutEndDateAsync(TUser user, CancellationToken token) { - // todo migration? return Task.FromResult(user.LockoutEndDateUtc); } From 5856bbcf6df943297d6e9dadffacb2320f238be1 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Sat, 24 Sep 2016 23:40:53 -0400 Subject: [PATCH 38/56] Add IdentityUserLogin type to get away from UserLoginInfo in Identity fwk closes #19 --- .../UserIntegrationTestsBase.cs | 1 + .../IdentityUser.cs | 7 ++--- .../IdentityUserLogin.cs | 28 +++++++++++++++++++ .../README.md | 2 +- .../UserStore.cs | 4 ++- 5 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserLogin.cs diff --git a/src/CoreIntegrationTests/UserIntegrationTestsBase.cs b/src/CoreIntegrationTests/UserIntegrationTestsBase.cs index 90fe76b..66a4b96 100644 --- a/src/CoreIntegrationTests/UserIntegrationTestsBase.cs +++ b/src/CoreIntegrationTests/UserIntegrationTestsBase.cs @@ -27,6 +27,7 @@ public void BeforeEachTest() var client = new MongoClient("mongodb://localhost:27017"); var identityTesting = "identity-testing"; + // todo move away from GetServer which could be deprecated at some point Database = client.GetServer().GetDatabase(identityTesting); Users = Database.GetCollection("users"); Roles = Database.GetCollection("roles"); diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs index f760491..9eb34a0 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs @@ -13,7 +13,7 @@ public IdentityUser() { Id = ObjectId.GenerateNewId().ToString(); Roles = new List(); - Logins = new List(); + Logins = new List(); Claims = new List(); Tokens = new List(); } @@ -65,13 +65,12 @@ public virtual void RemoveRole(string role) [BsonIgnoreIfNull] public virtual string PasswordHash { get; set; } - // todo move to a type I manage - and check for changes to UserLoginInfo for migration purposes [BsonIgnoreIfNull] - public virtual List Logins { get; set; } + public virtual List Logins { get; set; } public virtual void AddLogin(UserLoginInfo login) { - Logins.Add(login); + Logins.Add(new IdentityUserLogin(login)); } public virtual void RemoveLogin(string loginProvider, string providerKey) diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserLogin.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserLogin.cs new file mode 100644 index 0000000..b17f4ed --- /dev/null +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUserLogin.cs @@ -0,0 +1,28 @@ +namespace Microsoft.AspNetCore.Identity.MongoDB +{ + public class IdentityUserLogin + { + public IdentityUserLogin(string loginProvider, string providerKey, string providerDisplayName) + { + LoginProvider = loginProvider; + ProviderDisplayName = providerDisplayName; + ProviderKey = providerKey; + } + + public IdentityUserLogin(UserLoginInfo login) + { + LoginProvider = login.LoginProvider; + ProviderDisplayName = login.ProviderDisplayName; + ProviderKey = login.ProviderKey; + } + + public string LoginProvider { get; set; } + public string ProviderDisplayName { get; set; } + public string ProviderKey { get; set; } + + public UserLoginInfo ToUserLoginInfo() + { + return new UserLoginInfo(LoginProvider, ProviderKey, ProviderDisplayName); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/README.md b/src/Microsoft.AspNetCore.Identity.MongoDB/README.md index 3e83133..6dc6796 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/README.md +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/README.md @@ -8,4 +8,4 @@ - add IdentityUser.NormalizedUserName, IdentityUser.NormalizedEmail - LockoutEndDateUtc - type changed in code, but I think it is still the same in db -- Should't cause a problem, but FYI, UserLoginInfo.ProviderDisplayName was added +- Should't cause a problem, but FYI, IdentityUserLogin.ProviderDisplayName, I believe this was a change to Identity v2 diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs index 700a0ff..bb6d781 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs @@ -122,7 +122,9 @@ public virtual async Task AddLoginAsync(TUser user, UserLoginInfo login, Cancell => user.RemoveLogin(loginProvider, providerKey); public virtual async Task> GetLoginsAsync(TUser user, CancellationToken token) - => user.Logins; + => user.Logins + .Select(l => l.ToUserLoginInfo()) + .ToList(); public virtual Task FindByLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken)) => _Users From ea5af3f4e23b9a99dbb029e18f06284c272af001 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Tue, 27 Sep 2016 09:54:38 -0400 Subject: [PATCH 39/56] Figure out lowest common denominators for what frameworks I want to support. Because this is ASP.NET Core, that's what I'm looking at as far as what frameworks to support. Not like UWP would make sense for this adapter. Add net451 as a testing framework also Bump package versions and remove driver - it's brough to test projects as a transitive dependency Fixes #21 --- src/CoreIntegrationTests/project.json | 6 ++---- src/CoreTests/project.json | 4 +--- .../README.md | 14 +++++++++++++- .../project.json | 8 +++++--- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/CoreIntegrationTests/project.json b/src/CoreIntegrationTests/project.json index b225fbc..9c6ad56 100644 --- a/src/CoreIntegrationTests/project.json +++ b/src/CoreIntegrationTests/project.json @@ -6,17 +6,15 @@ "dependencies": { "CoreTests": "1.0.0-*", "dotnet-test-nunit": "3.4.0-beta-2", - "Microsoft.AspNetCore.Identity": "1.0.0", "Microsoft.AspNetCore.Identity.MongoDB": "1.0.0-*", "Microsoft.Extensions.DependencyInjection": "1.0.0", "Microsoft.Extensions.Logging": "1.0.0", - "mongocsharpdriver": "2.3.0-rc1", - "MongoDB.Driver": "2.3.0-rc1", - "NETStandard.Library": "1.6.0", + "mongocsharpdriver": "2.3.0", "NUnit": "3.4.1" }, "frameworks": { + "net451": {}, "netcoreapp1.0": { "imports": "portable-net45+win8", "dependencies": { diff --git a/src/CoreTests/project.json b/src/CoreTests/project.json index fe4f65d..4b5f407 100644 --- a/src/CoreTests/project.json +++ b/src/CoreTests/project.json @@ -5,14 +5,12 @@ "dependencies": { "dotnet-test-nunit": "3.4.0-beta-2", - "Microsoft.AspNetCore.Identity": "1.0.0", "Microsoft.AspNetCore.Identity.MongoDB": "1.0.0-*", - "MongoDB.Driver": "2.3.0-rc1", - "NETStandard.Library": "1.6.0", "NUnit": "3.4.1" }, "frameworks": { + "net451": {}, "netcoreapp1.0": { "imports": "portable-net45+win8", "dependencies": { diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/README.md b/src/Microsoft.AspNetCore.Identity.MongoDB/README.md index 6dc6796..57d599e 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/README.md +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/README.md @@ -1,4 +1,16 @@ -## Migrating from ASP.NET Identity 2.0 +## Microsoft.AspNetCore.Identity.MongoDB + +This is a MongoDB provider for the ASP.NET Core Identity framework. + +What frameworks are targeted, with rationale: + +- Microsoft.AspNetCore.Identity - supports net451 and netstandard1.3 +- MongoDB.Driver v2.3 - supports net45 and netstandard1.5 +- Thus, the lowest common denominators are net451 (of net45 and net451) and netstandard1.5 (of netstandard1.3 and netstandard1.5) +- FYI net451 supports netstandard1.2, that's obviously too low for a single target + +## Migrating from ASP.NET Identity 2.0 + - roles names need to be normalized (user.roles) - Default uppercase - tell people if they customize this they have to deal with custom migration diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/project.json b/src/Microsoft.AspNetCore.Identity.MongoDB/project.json index 5ebf0ee..ad2d465 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/project.json +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/project.json @@ -33,13 +33,15 @@ "dependencies": { "Microsoft.AspNetCore.Identity": "1.0.0", - "MongoDB.Driver": "2.3.0-rc1", - "NETStandard.Library": "1.6.0" + "MongoDB.Driver": "2.3.0" }, "frameworks": { + "net451": {}, "netstandard1.5": { - + "dependencies": { + "NETStandard.Library": "1.6.0" + } } } } From 806a1f74fecc88cf864d09eafc0b974418b4bb3b Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Tue, 27 Sep 2016 11:24:58 -0400 Subject: [PATCH 40/56] Add normalized field unique indexes Move old indexes so people have to decide if they want to keep these when migrating from ASP.NET Identity v2 - they might do this if they have their own queries on non normalized fields, that's why I didn't obsolete these methods. Fixes #22 --- src/CoreIntegrationTests/IndexChecksTests.cs | 59 +++++++------------ .../IndexChecks.cs | 45 +++++++++++--- 2 files changed, 57 insertions(+), 47 deletions(-) diff --git a/src/CoreIntegrationTests/IndexChecksTests.cs b/src/CoreIntegrationTests/IndexChecksTests.cs index 8552a1e..9912a3d 100644 --- a/src/CoreIntegrationTests/IndexChecksTests.cs +++ b/src/CoreIntegrationTests/IndexChecksTests.cs @@ -1,61 +1,42 @@ namespace IntegrationTests { + using System; using System.Linq; using Microsoft.AspNetCore.Identity.MongoDB; + using MongoDB.Driver; using NUnit.Framework; [TestFixture] public class IndexChecksTests : UserIntegrationTestsBase { [Test] - public void EnsureUniqueIndexOnUserName_NoIndexOnUserName_AddsUniqueIndexOnUserName() + public void EnsureUniqueIndexes() { - var userCollectionName = "userindextest"; - Database.DropCollection(userCollectionName); - var usersNewApi = DatabaseNewApi.GetCollection(userCollectionName); + EnsureUniqueIndex(IndexChecks.OptionalIndexChecks.EnsureUniqueIndexOnUserName, "UserName"); + EnsureUniqueIndex(IndexChecks.OptionalIndexChecks.EnsureUniqueIndexOnEmail, "Email"); + EnsureUniqueIndex(IndexChecks.OptionalIndexChecks.EnsureUniqueIndexOnRoleName, "Name"); - IndexChecks.EnsureUniqueIndexOnUserName(usersNewApi); - - var users = Database.GetCollection(userCollectionName); - var index = users.GetIndexes() - .Where(i => i.IsUnique) - .Where(i => i.Key.Count() == 1) - .First(i => i.Key.Contains("UserName")); - Expect(index.Key.Count(), Is.EqualTo(1)); - } - - [Test] - public void EnsureEmailUniqueIndex_NoIndexOnEmail_AddsUniqueIndexOnEmail() - { - var userCollectionName = "userindextest"; - Database.DropCollection(userCollectionName); - var usersNewApi = DatabaseNewApi.GetCollection(userCollectionName); - - IndexChecks.EnsureUniqueIndexOnEmail(usersNewApi); - - var users = Database.GetCollection(userCollectionName); - var index = users.GetIndexes() - .Where(i => i.IsUnique) - .Where(i => i.Key.Count() == 1) - .First(i => i.Key.Contains("Email")); - Expect(index.Key.Count(), Is.EqualTo(1)); + EnsureUniqueIndex(IndexChecks.EnsureUniqueIndexOnNormalizedUserName, "NormalizedUserName"); + EnsureUniqueIndex(IndexChecks.EnsureUniqueIndexOnNormalizedEmail, "NormalizedEmail"); + EnsureUniqueIndex(IndexChecks.EnsureUniqueIndexOnNormalizedRoleName, "NormalizedName"); } - [Test] - public void EnsureUniqueIndexOnRoleName_NoIndexOnRoleName_AddsUniqueIndexOnRoleName() + private void EnsureUniqueIndex(Action> addIndex, string indexedField) { - var roleCollectionName = "roleindextest"; - Database.DropCollection(roleCollectionName); - var rolesNewApi = DatabaseNewApi.GetCollection(roleCollectionName); + var testCollectionName = "indextest"; + Database.DropCollection(testCollectionName); + var testCollection = DatabaseNewApi.GetCollection(testCollectionName); - IndexChecks.EnsureUniqueIndexOnRoleName(rolesNewApi); + addIndex(testCollection); - var roles = Database.GetCollection(roleCollectionName); - var index = roles.GetIndexes() + var legacyCollectionInterface = Database.GetCollection(testCollectionName); + var index = legacyCollectionInterface.GetIndexes() .Where(i => i.IsUnique) .Where(i => i.Key.Count() == 1) - .First(i => i.Key.Contains("Name")); - Expect(index.Key.Count(), Is.EqualTo(1)); + .FirstOrDefault(i => i.Key.Contains(indexedField)); + var failureMessage = $"No unique index found on {indexedField}"; + Expect(index, Is.Not.Null, failureMessage); + Expect(index.Key.Count(), Is.EqualTo(1), failureMessage); } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IndexChecks.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IndexChecks.cs index f37ed27..f377110 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IndexChecks.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IndexChecks.cs @@ -2,32 +2,61 @@ { using global::MongoDB.Driver; - public class IndexChecks + public static class IndexChecks { - public static void EnsureUniqueIndexOnUserName(IMongoCollection users) + public static void EnsureUniqueIndexOnNormalizedUserName(IMongoCollection users) where TUser : IdentityUser { - var userName = Builders.IndexKeys.Ascending(t => t.UserName); + var userName = Builders.IndexKeys.Ascending(t => t.NormalizedUserName); var unique = new CreateIndexOptions {Unique = true}; users.Indexes.CreateOneAsync(userName, unique); } - public static void EnsureUniqueIndexOnRoleName(IMongoCollection roles) + public static void EnsureUniqueIndexOnNormalizedRoleName(IMongoCollection roles) where TRole : IdentityRole { - var roleName = Builders.IndexKeys.Ascending(t => t.Name); + var roleName = Builders.IndexKeys.Ascending(t => t.NormalizedName); var unique = new CreateIndexOptions {Unique = true}; roles.Indexes.CreateOneAsync(roleName, unique); } - public static void EnsureUniqueIndexOnEmail(IMongoCollection users) + public static void EnsureUniqueIndexOnNormalizedEmail(IMongoCollection users) where TUser : IdentityUser { - var email = Builders.IndexKeys.Ascending(t => t.Email); + var email = Builders.IndexKeys.Ascending(t => t.NormalizedEmail); var unique = new CreateIndexOptions {Unique = true}; users.Indexes.CreateOneAsync(email, unique); } - // todo indexes for normalized role name, user name and emails? + /// + /// ASP.NET Core Identity now searches on normalized fields so these indexes are no longer required, replace with + /// normalized checks. + /// + public static class OptionalIndexChecks + { + public static void EnsureUniqueIndexOnUserName(IMongoCollection users) + where TUser : IdentityUser + { + var userName = Builders.IndexKeys.Ascending(t => t.UserName); + var unique = new CreateIndexOptions {Unique = true}; + users.Indexes.CreateOneAsync(userName, unique); + } + + public static void EnsureUniqueIndexOnRoleName(IMongoCollection roles) + where TRole : IdentityRole + { + var roleName = Builders.IndexKeys.Ascending(t => t.Name); + var unique = new CreateIndexOptions {Unique = true}; + roles.Indexes.CreateOneAsync(roleName, unique); + } + + public static void EnsureUniqueIndexOnEmail(IMongoCollection users) + where TUser : IdentityUser + { + var email = Builders.IndexKeys.Ascending(t => t.Email); + var unique = new CreateIndexOptions {Unique = true}; + users.Indexes.CreateOneAsync(email, unique); + } + } } } \ No newline at end of file From 0c58cb9e7f531575aa7c99bfd68d58bbcd63e732 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Tue, 27 Sep 2016 11:28:51 -0400 Subject: [PATCH 41/56] Override ToString to be helpful when debugging Fixes #16 --- src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs | 2 ++ src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs index 2979839..b61f65e 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityRole.cs @@ -21,5 +21,7 @@ public IdentityRole(string roleName) : this() public string Name { get; set; } public string NormalizedName { get; set; } + + public override string ToString() => Name; } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs index 9eb34a0..c69f97f 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs @@ -142,5 +142,7 @@ public virtual void RemoveToken(string loginProvider, string name) { Tokens.RemoveAll(t => t.LoginProvider == loginProvider && t.Name == name); } + + public override string ToString() => UserName; } } \ No newline at end of file From d9fa923f57cdc97b91bbbcd770be14bafdc09ded Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Tue, 27 Sep 2016 13:48:31 -0400 Subject: [PATCH 42/56] Create AddMongoStores extension method to inject Store services Fixes #23 --- .../UserIntegrationTestsBase.cs | 44 ++--------- .../MongoIdentityBuilderExtensionsTests.cs | 75 +++++++++++++++++++ src/CoreTests/project.json | 2 + .../MongoIdentityBuilderExtensions.cs | 69 +++++++++++++++++ 4 files changed, 153 insertions(+), 37 deletions(-) create mode 100644 src/CoreTests/MongoIdentityBuilderExtensionsTests.cs create mode 100644 src/Microsoft.AspNetCore.Identity.MongoDB/MongoIdentityBuilderExtensions.cs diff --git a/src/CoreIntegrationTests/UserIntegrationTestsBase.cs b/src/CoreIntegrationTests/UserIntegrationTestsBase.cs index 66a4b96..28d0f41 100644 --- a/src/CoreIntegrationTests/UserIntegrationTestsBase.cs +++ b/src/CoreIntegrationTests/UserIntegrationTestsBase.cs @@ -5,7 +5,6 @@ using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.MongoDB; using Microsoft.Extensions.DependencyInjection; - using Microsoft.Extensions.Logging; using MongoDB.Driver; using NUnit.Framework; @@ -17,24 +16,21 @@ public class UserIntegrationTestsBase : AssertionHelper // note: for now we'll have interfaces to both the new and old apis for MongoDB, that way we don't have to update all the tests at once and risk introducing bugs protected IMongoDatabase DatabaseNewApi; - private IMongoCollection _UsersNewApi; - private IMongoCollection _RolesNewApi; protected IServiceProvider ServiceProvider; + private readonly string _TestingConnectionString = $"mongodb://localhost:27017/{IdentityTesting}"; + private const string IdentityTesting = "identity-testing"; [SetUp] public void BeforeEachTest() { - var client = new MongoClient("mongodb://localhost:27017"); - var identityTesting = "identity-testing"; + var client = new MongoClient(_TestingConnectionString); // todo move away from GetServer which could be deprecated at some point - Database = client.GetServer().GetDatabase(identityTesting); + Database = client.GetServer().GetDatabase(IdentityTesting); Users = Database.GetCollection("users"); Roles = Database.GetCollection("roles"); - DatabaseNewApi = client.GetDatabase(identityTesting); - _UsersNewApi = DatabaseNewApi.GetCollection("users"); - _RolesNewApi = DatabaseNewApi.GetCollection("roles"); + DatabaseNewApi = client.GetDatabase(IdentityTesting); Database.DropCollection("users"); Database.DropCollection("roles"); @@ -55,38 +51,12 @@ protected IServiceProvider CreateServiceProvider(Action { }); services.AddIdentity(optionsProvider) - .AddDefaultTokenProviders(); - - var roles = DatabaseNewApi.GetCollection("roles"); - var roleStore = new RoleStore(roles); - services.AddSingleton>(roleStore); - - var users = DatabaseNewApi.GetCollection("users"); - var userStore = new UserStore(users); - services.AddSingleton>(userStore); + .AddDefaultTokenProviders() + .AddMongoStores(_TestingConnectionString); services.AddLogging(); - services.AddSingleton>>(new TestLogger>()); - services.AddSingleton>>(new TestLogger>()); return services.BuildServiceProvider(); } - - public class TestLogger : ILogger - { - public IDisposable BeginScope(TState state) - { - return null; - } - - public bool IsEnabled(LogLevel logLevel) - { - return true; - } - - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) - { - } - } } } \ No newline at end of file diff --git a/src/CoreTests/MongoIdentityBuilderExtensionsTests.cs b/src/CoreTests/MongoIdentityBuilderExtensionsTests.cs new file mode 100644 index 0000000..54ba3ca --- /dev/null +++ b/src/CoreTests/MongoIdentityBuilderExtensionsTests.cs @@ -0,0 +1,75 @@ +namespace CoreTests +{ + using Microsoft.AspNetCore.Identity; + using Microsoft.AspNetCore.Identity.MongoDB; + using Microsoft.Extensions.DependencyInjection; + using NUnit.Framework; + + [TestFixture] + public class MongoIdentityBuilderExtensionsTests : AssertionHelper + { + private const string FakeConnectionStringWithDatabase = "mongodb://fakehost:27017/database"; + + [Test] + public void AddMongoStores_ResolvesStoresAndManagers() + { + var services = new ServiceCollection(); + + services + .AddIdentity() + .AddMongoStores(FakeConnectionStringWithDatabase); + + // note: UserManager and RoleManager use logging + services.AddLogging(); + + var provider = services.BuildServiceProvider(); + var resolvedUserStore = provider.GetService>(); + Expect(resolvedUserStore, Is.Not.Null, "User store did not resolve"); + + var resolvedRoleStore = provider.GetService>(); + Expect(resolvedRoleStore, Is.Not.Null, "Role store did not resolve"); + + var resolvedUserManager = provider.GetService>(); + Expect(resolvedUserManager, Is.Not.Null, "User manager did not resolve"); + } + + [Test] + public void AddMongoStores_ConnectionStringWithoutDatabase_Throws() + { + var connectionStringWithoutDatabase = "mongodb://fakehost"; + + TestDelegate addMongoStores = () => new ServiceCollection() + .AddIdentity() + .AddMongoStores(connectionStringWithoutDatabase); + + Expect(addMongoStores, Throws.Exception + .With.Message.Contains("Your connection string must contain a database name")); + } + + protected class WrongUser : IdentityUser + { + } + + protected class WrongRole : IdentityRole + { + } + + [Test] + public void AddMongoStores_MismatchedTypes_ThrowsWarningToHelpUsers() + { + Expect(() => new ServiceCollection() + .AddIdentity() + .AddMongoStores(FakeConnectionStringWithDatabase), + Throws.Exception.With.Message + .EqualTo("User type passed to AddMongoStores must match user type passed to AddIdentity. You passed Microsoft.AspNetCore.Identity.MongoDB.IdentityUser to AddIdentity and CoreTests.MongoIdentityBuilderExtensionsTests+WrongUser to AddMongoStores, these do not match.") + ); + + Expect(() => new ServiceCollection() + .AddIdentity() + .AddMongoStores(FakeConnectionStringWithDatabase), + Throws.Exception.With.Message + .EqualTo("Role type passed to AddMongoStores must match role type passed to AddIdentity. You passed Microsoft.AspNetCore.Identity.MongoDB.IdentityRole to AddIdentity and CoreTests.MongoIdentityBuilderExtensionsTests+WrongRole to AddMongoStores, these do not match.") + ); + } + } +} \ No newline at end of file diff --git a/src/CoreTests/project.json b/src/CoreTests/project.json index 4b5f407..2653e9e 100644 --- a/src/CoreTests/project.json +++ b/src/CoreTests/project.json @@ -6,6 +6,8 @@ "dependencies": { "dotnet-test-nunit": "3.4.0-beta-2", "Microsoft.AspNetCore.Identity.MongoDB": "1.0.0-*", + "Microsoft.Extensions.DependencyInjection": "1.0.0", + "Microsoft.Extensions.Logging": "1.0.0", "NUnit": "3.4.1" }, diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/MongoIdentityBuilderExtensions.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/MongoIdentityBuilderExtensions.cs new file mode 100644 index 0000000..20c6fd2 --- /dev/null +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/MongoIdentityBuilderExtensions.cs @@ -0,0 +1,69 @@ +// ReSharper disable once CheckNamespace - Common convention to locate extensions in Microsoft namespaces for simplifying autocompletion as a consumer. + +namespace Microsoft.Extensions.DependencyInjection +{ + using System; + using AspNetCore.Identity; + using AspNetCore.Identity.MongoDB; + using MongoDB.Driver; + + public static class MongoIdentityBuilderExtensions + { + /// + /// Uses "users" collection and "roles" collections. + /// + /// + /// Must contain the database name + public static IdentityBuilder AddMongoStores(this IdentityBuilder builder, string connectionString) + where TRole : IdentityRole + where TUser : IdentityUser + { + var url = new MongoUrl(connectionString); + var client = new MongoClient(url); + if (url.DatabaseName == null) + { + throw new ArgumentException("Your connection string must contain a database name", connectionString); + } + var database = client.GetDatabase(url.DatabaseName); + return builder.AddMongoStores( + p => database.GetCollection("users"), + p => database.GetCollection("roles")); + } + + /// + /// If you want control over creating the users and roles collections, use this overload. + /// + /// + /// + /// + /// + /// + /// + private static IdentityBuilder AddMongoStores(this IdentityBuilder builder, + Func> usersCollectionFactory, + Func> rolesCollectionFactory) + where TRole : IdentityRole + where TUser : IdentityUser + { + if (typeof(TUser) != builder.UserType) + { + var message = "User type passed to AddMongoStores must match user type passed to AddIdentity. " + + $"You passed {builder.UserType} to AddIdentity and {typeof(TUser)} to AddMongoStores, " + + "these do not match."; + + throw new ArgumentException(message); + } + if (typeof(TRole) != builder.RoleType) + { + var message = "Role type passed to AddMongoStores must match role type passed to AddIdentity. " + + $"You passed {builder.RoleType} to AddIdentity and {typeof(TRole)} to AddMongoStores, " + + "these do not match."; + + throw new ArgumentException(message); + } + builder.Services.AddSingleton>(p => new UserStore(usersCollectionFactory(p))); + builder.Services.AddSingleton>(p => new RoleStore(rolesCollectionFactory(p))); + return builder; + } + } +} \ No newline at end of file From ad6055a2e09e7298ecccce82209179e1b94d1fdd Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Tue, 27 Sep 2016 13:53:31 -0400 Subject: [PATCH 43/56] Continue working on AddMongoStores --- .../MongoIdentityBuilderExtensionsTests.cs | 39 ++++++++++++++++++- .../MongoIdentityBuilderExtensions.cs | 1 + .../README.md | 13 ++++++- 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/CoreTests/MongoIdentityBuilderExtensionsTests.cs b/src/CoreTests/MongoIdentityBuilderExtensionsTests.cs index 54ba3ca..e9c82b1 100644 --- a/src/CoreTests/MongoIdentityBuilderExtensionsTests.cs +++ b/src/CoreTests/MongoIdentityBuilderExtensionsTests.cs @@ -11,10 +11,9 @@ public class MongoIdentityBuilderExtensionsTests : AssertionHelper private const string FakeConnectionStringWithDatabase = "mongodb://fakehost:27017/database"; [Test] - public void AddMongoStores_ResolvesStoresAndManagers() + public void AddMongoStores_WithDefaultTypes_ResolvesStoresAndManagers() { var services = new ServiceCollection(); - services .AddIdentity() .AddMongoStores(FakeConnectionStringWithDatabase); @@ -31,6 +30,42 @@ public void AddMongoStores_ResolvesStoresAndManagers() var resolvedUserManager = provider.GetService>(); Expect(resolvedUserManager, Is.Not.Null, "User manager did not resolve"); + + var resolvedRoleManager = provider.GetService>(); + Expect(resolvedRoleManager, Is.Not.Null, "Role manager did not resolve"); + } + + protected class CustomUser : IdentityUser + { + } + + protected class CustomRole : IdentityRole + { + } + + [Test] + public void AddMongoStores_WithCustomTypes_ThisShouldLookReasonableForUsers() + { + // this test is just to make sure I consider the interface for using custom types + // so that it's not a horrible experience even though it should be rarely used + var services = new ServiceCollection(); + services + .AddIdentity() + .AddMongoStores(FakeConnectionStringWithDatabase); + services.AddLogging(); + + var provider = services.BuildServiceProvider(); + var resolvedUserStore = provider.GetService>(); + Expect(resolvedUserStore, Is.Not.Null, "User store did not resolve"); + + var resolvedRoleStore = provider.GetService>(); + Expect(resolvedRoleStore, Is.Not.Null, "Role store did not resolve"); + + var resolvedUserManager = provider.GetService>(); + Expect(resolvedUserManager, Is.Not.Null, "User manager did not resolve"); + + var resolvedRoleManager = provider.GetService>(); + Expect(resolvedRoleManager, Is.Not.Null, "Role manager did not resolve"); } [Test] diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/MongoIdentityBuilderExtensions.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/MongoIdentityBuilderExtensions.cs index 20c6fd2..6f7c244 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/MongoIdentityBuilderExtensions.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/MongoIdentityBuilderExtensions.cs @@ -10,6 +10,7 @@ namespace Microsoft.Extensions.DependencyInjection public static class MongoIdentityBuilderExtensions { /// + /// If you want the default collections, use this method. /// Uses "users" collection and "roles" collections. /// /// diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/README.md b/src/Microsoft.AspNetCore.Identity.MongoDB/README.md index 57d599e..3716b39 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/README.md +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/README.md @@ -1,4 +1,15 @@ -## Microsoft.AspNetCore.Identity.MongoDB +## Instructions + +- `Install-Package Microsoft.AspNetCore.Identity.MongoDB` +- Register identity services as follows. Make sure the types passed to AddIdentity and AddMongoStores match. +```csharp +services + .AddIdentity() + .AddMongoStores("mongodb://localhost/myDB"); +``` + + +## Microsoft.AspNetCore.Identity.MongoDB This is a MongoDB provider for the ASP.NET Core Identity framework. From 6dd8bccdf0077d9af14c21041c063f64e9ba32d0 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Tue, 27 Sep 2016 15:50:50 -0400 Subject: [PATCH 44/56] Considering perspective of consumer, roll up registration into one method --- .../MongoIdentityBuilderExtensionsTests.cs | 9 ++--- .../MongoIdentityBuilderExtensions.cs | 36 +++++++++++++++---- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/CoreTests/MongoIdentityBuilderExtensionsTests.cs b/src/CoreTests/MongoIdentityBuilderExtensionsTests.cs index e9c82b1..c1cd76e 100644 --- a/src/CoreTests/MongoIdentityBuilderExtensionsTests.cs +++ b/src/CoreTests/MongoIdentityBuilderExtensionsTests.cs @@ -14,10 +14,7 @@ public class MongoIdentityBuilderExtensionsTests : AssertionHelper public void AddMongoStores_WithDefaultTypes_ResolvesStoresAndManagers() { var services = new ServiceCollection(); - services - .AddIdentity() - .AddMongoStores(FakeConnectionStringWithDatabase); - + services.AddIdentityWithMongoStores(FakeConnectionStringWithDatabase); // note: UserManager and RoleManager use logging services.AddLogging(); @@ -49,9 +46,7 @@ public void AddMongoStores_WithCustomTypes_ThisShouldLookReasonableForUsers() // this test is just to make sure I consider the interface for using custom types // so that it's not a horrible experience even though it should be rarely used var services = new ServiceCollection(); - services - .AddIdentity() - .AddMongoStores(FakeConnectionStringWithDatabase); + services.AddIdentityWithMongoStoresUsingCustomTypes(FakeConnectionStringWithDatabase); services.AddLogging(); var provider = services.BuildServiceProvider(); diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/MongoIdentityBuilderExtensions.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/MongoIdentityBuilderExtensions.cs index 6f7c244..d2a136d 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/MongoIdentityBuilderExtensions.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/MongoIdentityBuilderExtensions.cs @@ -10,8 +10,8 @@ namespace Microsoft.Extensions.DependencyInjection public static class MongoIdentityBuilderExtensions { /// - /// If you want the default collections, use this method. - /// Uses "users" collection and "roles" collections. + /// This method only registers mongo stores, you also need to call AddIdentity. + /// Consider using AddIdentityWithMongoStores. /// /// /// Must contain the database name @@ -33,14 +33,14 @@ public static IdentityBuilder AddMongoStores(this IdentityBuilder /// /// If you want control over creating the users and roles collections, use this overload. + /// This method only registers mongo stores, you also need to call AddIdentity. /// /// /// /// /// /// - /// - private static IdentityBuilder AddMongoStores(this IdentityBuilder builder, + public static IdentityBuilder AddMongoStores(this IdentityBuilder builder, Func> usersCollectionFactory, Func> rolesCollectionFactory) where TRole : IdentityRole @@ -51,7 +51,6 @@ private static IdentityBuilder AddMongoStores(this IdentityBuilder var message = "User type passed to AddMongoStores must match user type passed to AddIdentity. " + $"You passed {builder.UserType} to AddIdentity and {typeof(TUser)} to AddMongoStores, " + "these do not match."; - throw new ArgumentException(message); } if (typeof(TRole) != builder.RoleType) @@ -59,12 +58,37 @@ private static IdentityBuilder AddMongoStores(this IdentityBuilder var message = "Role type passed to AddMongoStores must match role type passed to AddIdentity. " + $"You passed {builder.RoleType} to AddIdentity and {typeof(TRole)} to AddMongoStores, " + "these do not match."; - throw new ArgumentException(message); } builder.Services.AddSingleton>(p => new UserStore(usersCollectionFactory(p))); builder.Services.AddSingleton>(p => new RoleStore(rolesCollectionFactory(p))); return builder; } + + /// + /// This method registers identity services and MongoDB stores using the IdentityUser and IdentityRole types. + /// + /// + /// Connection string must contain the database name + public static void AddIdentityWithMongoStores(this IServiceCollection services, string connectionString) + { + services.AddIdentityWithMongoStoresUsingCustomTypes(connectionString); + } + + /// + /// This method allows you to customize the user and role type when registering identity services + /// and MongoDB stores. + /// + /// + /// + /// + /// Connection string must contain the database name + public static void AddIdentityWithMongoStoresUsingCustomTypes(this IServiceCollection services, string connectionString) + where TUser : IdentityUser + where TRole : IdentityRole + { + services.AddIdentity() + .AddMongoStores(connectionString); + } } } \ No newline at end of file From 631d30156e4adcc5c64cfc5f3a28ed0cfa498111 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Tue, 27 Sep 2016 15:56:14 -0400 Subject: [PATCH 45/56] Rename AddMongoStores to RegisterMongoStores so it's not in the conventional spot with Add, this is for advanced scenarios and I don't want people to see it unless they go looking for it for a specific use case. This will avoid confusion for people that want to use defaults with AddIdentityWithMongoStores methods. --- .../UserIntegrationTestsBase.cs | 2 +- .../MongoIdentityBuilderExtensionsTests.cs | 10 +++++----- .../MongoIdentityBuilderExtensions.cs | 16 ++++++++-------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/CoreIntegrationTests/UserIntegrationTestsBase.cs b/src/CoreIntegrationTests/UserIntegrationTestsBase.cs index 28d0f41..ff819cb 100644 --- a/src/CoreIntegrationTests/UserIntegrationTestsBase.cs +++ b/src/CoreIntegrationTests/UserIntegrationTestsBase.cs @@ -52,7 +52,7 @@ protected IServiceProvider CreateServiceProvider(Action { }); services.AddIdentity(optionsProvider) .AddDefaultTokenProviders() - .AddMongoStores(_TestingConnectionString); + .RegisterMongoStores(_TestingConnectionString); services.AddLogging(); diff --git a/src/CoreTests/MongoIdentityBuilderExtensionsTests.cs b/src/CoreTests/MongoIdentityBuilderExtensionsTests.cs index c1cd76e..0da23da 100644 --- a/src/CoreTests/MongoIdentityBuilderExtensionsTests.cs +++ b/src/CoreTests/MongoIdentityBuilderExtensionsTests.cs @@ -70,7 +70,7 @@ public void AddMongoStores_ConnectionStringWithoutDatabase_Throws() TestDelegate addMongoStores = () => new ServiceCollection() .AddIdentity() - .AddMongoStores(connectionStringWithoutDatabase); + .RegisterMongoStores(connectionStringWithoutDatabase); Expect(addMongoStores, Throws.Exception .With.Message.Contains("Your connection string must contain a database name")); @@ -89,16 +89,16 @@ public void AddMongoStores_MismatchedTypes_ThrowsWarningToHelpUsers() { Expect(() => new ServiceCollection() .AddIdentity() - .AddMongoStores(FakeConnectionStringWithDatabase), + .RegisterMongoStores(FakeConnectionStringWithDatabase), Throws.Exception.With.Message - .EqualTo("User type passed to AddMongoStores must match user type passed to AddIdentity. You passed Microsoft.AspNetCore.Identity.MongoDB.IdentityUser to AddIdentity and CoreTests.MongoIdentityBuilderExtensionsTests+WrongUser to AddMongoStores, these do not match.") + .EqualTo("User type passed to RegisterMongoStores must match user type passed to AddIdentity. You passed Microsoft.AspNetCore.Identity.MongoDB.IdentityUser to AddIdentity and CoreTests.MongoIdentityBuilderExtensionsTests+WrongUser to RegisterMongoStores, these do not match.") ); Expect(() => new ServiceCollection() .AddIdentity() - .AddMongoStores(FakeConnectionStringWithDatabase), + .RegisterMongoStores(FakeConnectionStringWithDatabase), Throws.Exception.With.Message - .EqualTo("Role type passed to AddMongoStores must match role type passed to AddIdentity. You passed Microsoft.AspNetCore.Identity.MongoDB.IdentityRole to AddIdentity and CoreTests.MongoIdentityBuilderExtensionsTests+WrongRole to AddMongoStores, these do not match.") + .EqualTo("Role type passed to RegisterMongoStores must match role type passed to AddIdentity. You passed Microsoft.AspNetCore.Identity.MongoDB.IdentityRole to AddIdentity and CoreTests.MongoIdentityBuilderExtensionsTests+WrongRole to RegisterMongoStores, these do not match.") ); } } diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/MongoIdentityBuilderExtensions.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/MongoIdentityBuilderExtensions.cs index d2a136d..f08ff89 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/MongoIdentityBuilderExtensions.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/MongoIdentityBuilderExtensions.cs @@ -15,7 +15,7 @@ public static class MongoIdentityBuilderExtensions /// /// /// Must contain the database name - public static IdentityBuilder AddMongoStores(this IdentityBuilder builder, string connectionString) + public static IdentityBuilder RegisterMongoStores(this IdentityBuilder builder, string connectionString) where TRole : IdentityRole where TUser : IdentityUser { @@ -26,7 +26,7 @@ public static IdentityBuilder AddMongoStores(this IdentityBuilder throw new ArgumentException("Your connection string must contain a database name", connectionString); } var database = client.GetDatabase(url.DatabaseName); - return builder.AddMongoStores( + return builder.RegisterMongoStores( p => database.GetCollection("users"), p => database.GetCollection("roles")); } @@ -40,7 +40,7 @@ public static IdentityBuilder AddMongoStores(this IdentityBuilder /// /// /// - public static IdentityBuilder AddMongoStores(this IdentityBuilder builder, + public static IdentityBuilder RegisterMongoStores(this IdentityBuilder builder, Func> usersCollectionFactory, Func> rolesCollectionFactory) where TRole : IdentityRole @@ -48,15 +48,15 @@ public static IdentityBuilder AddMongoStores(this IdentityBuilder { if (typeof(TUser) != builder.UserType) { - var message = "User type passed to AddMongoStores must match user type passed to AddIdentity. " - + $"You passed {builder.UserType} to AddIdentity and {typeof(TUser)} to AddMongoStores, " + var message = "User type passed to RegisterMongoStores must match user type passed to AddIdentity. " + + $"You passed {builder.UserType} to AddIdentity and {typeof(TUser)} to RegisterMongoStores, " + "these do not match."; throw new ArgumentException(message); } if (typeof(TRole) != builder.RoleType) { - var message = "Role type passed to AddMongoStores must match role type passed to AddIdentity. " - + $"You passed {builder.RoleType} to AddIdentity and {typeof(TRole)} to AddMongoStores, " + var message = "Role type passed to RegisterMongoStores must match role type passed to AddIdentity. " + + $"You passed {builder.RoleType} to AddIdentity and {typeof(TRole)} to RegisterMongoStores, " + "these do not match."; throw new ArgumentException(message); } @@ -88,7 +88,7 @@ public static void AddIdentityWithMongoStoresUsingCustomTypes(this where TRole : IdentityRole { services.AddIdentity() - .AddMongoStores(connectionString); + .RegisterMongoStores(connectionString); } } } \ No newline at end of file From 733a5c7e1d7f8c7ef6ec78e7e216016494af3e57 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Tue, 27 Sep 2016 16:11:16 -0400 Subject: [PATCH 46/56] Update root README for project --- README.md | 79 ++++++++++--------- .../README.md | 34 -------- 2 files changed, 40 insertions(+), 73 deletions(-) delete mode 100644 src/Microsoft.AspNetCore.Identity.MongoDB/README.md diff --git a/README.md b/README.md index 6dd46d0..09f56f3 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,59 @@ -AspNet.Identity.Mongo -===================== +## Microsoft.AspNetCore.Identity.MongoDB -A mongodb provider for the new ASP.NET Identity framework. My aim is to ensure this project is well tested and configurable. +This is a MongoDB provider for the ASP.NET Core Identity framework. This was ported from the v2 Identity framework that was a part of ASP.NET (AspNet.Identity.Mongo NuGet package) -## Usage - - var client = new MongoClient("mongodb://localhost:27017"); - var database = client.GetDatabase("mydb"); - var users = database.GetCollection("users"); - - var store = new UserStore(users); - var manager = new UserManager(store); +I've released a new package for the ASP.NET Core Identity framework for the following reasons: +- Discoverability - named AspNetCore +- Continue to support the old package for ASP.NET (not Core) +- ASP.NET Core is a rewrite of ASP.NET, this Core Identity framework won't run on traditional ASP.NET - // if you want roles too: - var roles = database.GetCollection("roles"); - var roleStore = new RoleStore(roles); +This project has extensive test coverage. - // at some point in application startup it would be good to ensure unique indexes on user and role names exist: +This point of this adapter is to provide a simple wrapper to work with MongoDB. I do not intende to cover every possible desirable configuration, if you don't like my decisions, write your own adapter. These adapters are not complicated, but trying to make them configurable would become a complicated mess and would confuse the majority of people that want something simple to use, so I'm favoring simplicity over making every last person happy. - IndexChecks.EnsureUniqueIndexOnUserName(users); - IndexChecks.EnsureUniqueIndexOnEmail(users); - - IndexChecks.EnsureUniqueIndexOnRoleName(roles); +## Usage -OR +- `Install-Package Microsoft.AspNetCore.Identity.MongoDB` +- Then, in ConfigureServices--or wherever you are registering services--include the following to register both the Identity services and MongoDB stores: -a sample [aspnet-identity-mongo-sample](https://github.com/g0t4/aspnet-identity-mongo-sample) based on [Microsoft ASP.NET Identity Samples](http://www.nuget.org/packages/Microsoft.AspNet.Identity.Samples). +```csharp +services.AddIdentityWithMongoStores("mongodb://localhost/myDB"); +``` -## Installation +- If you want to customize what is registered, refer to the tests for further options (CoreTests/MongoIdentityBuilderExtensionsTests.cs) +- Remember with the Identity framework, the whole point is that both a `UserManager` and `RoleManager` are provided for you to use, here's how you can resolve instances: -via nuget: +```csharp +var userManager = provider.GetService>(); +var roleManager = provider.GetService>(); +``` - Install-Package AspNet.Identity.MongoDB +- The following methods help create indexes that will boost lookups by UserName, Email and role Name, by the way these have changed since Identity v2 to refer to Normalized fields. I dislike this aspect of Core Identity, but it is what it is. Basically these three fields are stored in uppercase format for case insensitive searches. -## Building and Testing +```csharp + IndexChecks.EnsureUniqueIndexOnNormalizedUserName(users); + IndexChecks.EnsureUniqueIndexOnNormalizedEmail(users); + IndexChecks.EnsureUniqueIndexOnNormalizedRoleName(roles); +``` -I'm using the albacore project with rake. +- Here is a sample project, review the commit log for the steps taken to port the default template from EntityFramework MSSQL to MongoDB. [aspnet-identity-mongo-sample](https://github.com/g0t4/aspnet-identity-mongo-sample). -To build: +What frameworks are targeted, with rationale: - rake msbuild - -To test: +- Microsoft.AspNetCore.Identity - supports net451 and netstandard1.3 +- MongoDB.Driver v2.3 - supports net45 and netstandard1.5 +- Thus, the lowest common denominators are net451 (of net45 and net451) and netstandard1.5 (of netstandard1.3 and netstandard1.5) +- FYI net451 supports netstandard1.2, that's obviously too low for a single target - rake tests - rake integration_tests +## Migrating from ASP.NET Identity 2.0 -To package: - - rake package -## Documentation +- roles names need to be normalized (user.roles) + - Default uppercase - tell people if they customize this they have to deal with custom migration -I'm writing about my design decisions on my blog: +- normalization by uppercase: + - add IdentityRole.NormalizedName + - add IdentityUser.NormalizedUserName, IdentityUser.NormalizedEmail +- LockoutEndDateUtc - type changed in code, but I think it is still the same in db -- [Building a mongodb provider for the new ASP.NET Identity framework - Part 1](http://devblog.wesmcclure.com/posts/building-a-mongodb-provider-for-the-new-asp.net-identity-framework-part-1) -- [Building a mongodb provider for the new ASP.NET Identity framework - Part 2 RoleStore And Sample](http://devblog.wesmcclure.com/posts/building-a-mongodb-provider-for-the-new-asp.net-identity-framework-part-2-rolestore-and-sample) \ No newline at end of file +- Should't cause a problem, but FYI, IdentityUserLogin.ProviderDisplayName, I believe this was a change to Identity v2 diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/README.md b/src/Microsoft.AspNetCore.Identity.MongoDB/README.md deleted file mode 100644 index 3716b39..0000000 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/README.md +++ /dev/null @@ -1,34 +0,0 @@ -## Instructions - -- `Install-Package Microsoft.AspNetCore.Identity.MongoDB` -- Register identity services as follows. Make sure the types passed to AddIdentity and AddMongoStores match. -```csharp -services - .AddIdentity() - .AddMongoStores("mongodb://localhost/myDB"); -``` - - -## Microsoft.AspNetCore.Identity.MongoDB - -This is a MongoDB provider for the ASP.NET Core Identity framework. - -What frameworks are targeted, with rationale: - -- Microsoft.AspNetCore.Identity - supports net451 and netstandard1.3 -- MongoDB.Driver v2.3 - supports net45 and netstandard1.5 -- Thus, the lowest common denominators are net451 (of net45 and net451) and netstandard1.5 (of netstandard1.3 and netstandard1.5) -- FYI net451 supports netstandard1.2, that's obviously too low for a single target - -## Migrating from ASP.NET Identity 2.0 - - -- roles names need to be normalized (user.roles) - - Default uppercase - tell people if they customize this they have to deal with custom migration - -- normalization by uppercase: - - add IdentityRole.NormalizedName - - add IdentityUser.NormalizedUserName, IdentityUser.NormalizedEmail -- LockoutEndDateUtc - type changed in code, but I think it is still the same in db - -- Should't cause a problem, but FYI, IdentityUserLogin.ProviderDisplayName, I believe this was a change to Identity v2 From 350c9e2c3eb98f481fe917c95856b1c2e100f2bb Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Tue, 27 Sep 2016 16:21:55 -0400 Subject: [PATCH 47/56] Tweaks to docs --- README.md | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 09f56f3..0e497f8 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,13 @@ This is a MongoDB provider for the ASP.NET Core Identity framework. This was ported from the v2 Identity framework that was a part of ASP.NET (AspNet.Identity.Mongo NuGet package) I've released a new package for the ASP.NET Core Identity framework for the following reasons: -- Discoverability - named AspNetCore -- Continue to support the old package for ASP.NET (not Core) -- ASP.NET Core is a rewrite of ASP.NET, this Core Identity framework won't run on traditional ASP.NET +- Discoverability - named AspNetCore. +- ASP.NET Core is a rewrite of ASP.NET, this Core Identity framework won't run on traditional ASP.NET. +- Migrating isn't a matter of updating dependencies. This project has extensive test coverage. -This point of this adapter is to provide a simple wrapper to work with MongoDB. I do not intende to cover every possible desirable configuration, if you don't like my decisions, write your own adapter. These adapters are not complicated, but trying to make them configurable would become a complicated mess and would confuse the majority of people that want something simple to use, so I'm favoring simplicity over making every last person happy. +If you want something easy to setup, this adapter is for you. I do not intend to cover every possible desirable configuration, if you don't like my decisions, write your own adapter. Use this as a learning tool to make your own adapter. These adapters are not complicated, but trying to make them configurable would become a complicated mess. And would confuse the majority of people that want something simple to use. So I'm favoring simplicity over making every last person happy. ## Usage @@ -21,14 +21,14 @@ services.AddIdentityWithMongoStores("mongodb://localhost/myDB"); ``` - If you want to customize what is registered, refer to the tests for further options (CoreTests/MongoIdentityBuilderExtensionsTests.cs) -- Remember with the Identity framework, the whole point is that both a `UserManager` and `RoleManager` are provided for you to use, here's how you can resolve instances: +- Remember with the Identity framework, the whole point is that both a `UserManager` and `RoleManager` are provided for you to use, here's how you can resolve instances manually. Of course, constructor injection is also available. ```csharp var userManager = provider.GetService>(); var roleManager = provider.GetService>(); ``` -- The following methods help create indexes that will boost lookups by UserName, Email and role Name, by the way these have changed since Identity v2 to refer to Normalized fields. I dislike this aspect of Core Identity, but it is what it is. Basically these three fields are stored in uppercase format for case insensitive searches. +- The following methods help create indexes that will boost lookups by UserName, Email and role Name. These have changed since Identity v2 to refer to Normalized fields. I dislike this aspect of Core Identity, but it is what it is. Basically these three fields are stored in uppercase format for case insensitive searches. ```csharp IndexChecks.EnsureUniqueIndexOnNormalizedUserName(users); @@ -47,13 +47,9 @@ What frameworks are targeted, with rationale: ## Migrating from ASP.NET Identity 2.0 - -- roles names need to be normalized (user.roles) - - Default uppercase - tell people if they customize this they have to deal with custom migration - -- normalization by uppercase: - - add IdentityRole.NormalizedName - - add IdentityUser.NormalizedUserName, IdentityUser.NormalizedEmail -- LockoutEndDateUtc - type changed in code, but I think it is still the same in db - -- Should't cause a problem, but FYI, IdentityUserLogin.ProviderDisplayName, I believe this was a change to Identity v2 +- Roles names need to be normalized as follows + - On IdentityRole documents, create a NormalizedName field = uppercase(Name). Leave Name as is. + - On IdentityUser documents, convert the values in the Roles array to uppercase +- User names need to be normalized as follows + - On IdentityUser documents, create a NormalizedUserName field = uppercase(UserName) and create a NormalizedEmail field = uppercase(Email). Leave UserName and Email as is. +- LockoutEndDateUtc - type changed in code, but I think it is still the same in db, I have yet to verify if this requires a migration. From 2b5db3e2bcd1edb72cae52e6d99a8d0ed5c4495f Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Tue, 27 Sep 2016 17:15:18 -0400 Subject: [PATCH 48/56] Simplify description --- .gitignore | 3 ++- src/Microsoft.AspNetCore.Identity.MongoDB/project.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 20805f3..2b6903c 100644 --- a/.gitignore +++ b/.gitignore @@ -154,7 +154,8 @@ $RECYCLE.BIN/ # Mac desktop service store files .DS_Store -/TestResult.xml + +TestResult.xml project.lock.json diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/project.json b/src/Microsoft.AspNetCore.Identity.MongoDB/project.json index ad2d465..f45099a 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/project.json +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/project.json @@ -3,7 +3,7 @@ "version": "1.0.0-*", "description": - "A MongoDB provider for ASP.NET Core Identity. NET Core Identity fwk. My aim is to ensure this project is well tested and configurable.", + "A MongoDB provider for ASP.NET Core Identity framework.", "authors": ["Wes Higbee"], From fc92365698cf9d757ed548e093207c02896ca60e Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Tue, 27 Sep 2016 17:15:50 -0400 Subject: [PATCH 49/56] Update build instructions --- README.md | 4 ++++ Rakefile.rb | 23 ----------------------- src/build.sh | 22 ++++++++++++++++++++++ 3 files changed, 26 insertions(+), 23 deletions(-) delete mode 100644 Rakefile.rb create mode 100644 src/build.sh diff --git a/README.md b/README.md index 0e497f8..aa0ecf5 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,10 @@ What frameworks are targeted, with rationale: - Thus, the lowest common denominators are net451 (of net45 and net451) and netstandard1.5 (of netstandard1.3 and netstandard1.5) - FYI net451 supports netstandard1.2, that's obviously too low for a single target +## Building instructions + +run commands in [](build.sh) + ## Migrating from ASP.NET Identity 2.0 - Roles names need to be normalized as follows diff --git a/Rakefile.rb b/Rakefile.rb deleted file mode 100644 index 1b1329e..0000000 --- a/Rakefile.rb +++ /dev/null @@ -1,23 +0,0 @@ -require 'albacore' - -msbuild :msbuild do |msb| - msb.solution = 'src/AspNet.Identity.MongoDB.sln' - msb.properties = { :configuration => :Release } - msb.targets = [ :Clean, :Build ] -end - -nunit :tests => [:msbuild] do |tests| - tests.command = 'src/packages/NUnit.Runners.2.6.3/tools/nunit-console.exe' - tests.assemblies = ['build/tests/Tests.dll'] -end - -nunit :integration_tests => [:msbuild] do |tests| - tests.command = 'src/packages/NUnit.Runners.2.6.3/tools/nunit-console.exe' - tests.assemblies = ['build/integrationTests/IntegrationTests.dll'] -end - -task :all_tests => [:tests, :integration_tests] - -task :package => [:all_tests] do - sh 'src/.nuget/nuget.exe pack src/AspNet.Identity.MongoDB/AspNet.Identity.MongoDB.csproj' -end \ No newline at end of file diff --git a/src/build.sh b/src/build.sh new file mode 100644 index 0000000..4578a70 --- /dev/null +++ b/src/build.sh @@ -0,0 +1,22 @@ +// clean +rm -rf CoreTests/bin CoreTests/obj CoreIntegrationTests/bin CoreIntegrationTests/obj Microsoft.AspNetCore.Identity.MongoDB/bin Microsoft.AspNetCore.Identity.MongoDB/obj + +// restore nuget package dependencies +dotnet restore + +// run tree command to see existing directory structure, not much + +// build all matched projects via project.json +// should be 6 projects in total (3 project * 2 frameworks each) +dotnet build -c Release */project.json +// debug build with -c Debug + +// run tree command, should see 6 new folders under 3 bin/Release/framework + +// run tests (each will run for both frameworks targeted) +dotnet test -c Release CoreTests +dotnet test -c Release CoreIntegrationTests + +// create NuGet package +dotnet pack -c Release Microsoft.AspNetCore.Identity.MongoDB + From 4e2c4c8b1629802287ce4103a8fa34d9f3783bdb Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Wed, 28 Sep 2016 01:37:37 -0400 Subject: [PATCH 50/56] Return IdentityBuilder so consumers can chain subsequent Identity registrations - Update docs --- README.md | 2 +- .../MongoIdentityBuilderExtensions.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index aa0ecf5..4c9cf83 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ If you want something easy to setup, this adapter is for you. I do not intend to ## Usage -- `Install-Package Microsoft.AspNetCore.Identity.MongoDB` +- Reference this package in project.json: Microsoft.AspNetCore.Identity.MongoDB - Then, in ConfigureServices--or wherever you are registering services--include the following to register both the Identity services and MongoDB stores: ```csharp diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/MongoIdentityBuilderExtensions.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/MongoIdentityBuilderExtensions.cs index f08ff89..21abf43 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/MongoIdentityBuilderExtensions.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/MongoIdentityBuilderExtensions.cs @@ -70,9 +70,9 @@ public static IdentityBuilder RegisterMongoStores(this IdentityBui /// /// /// Connection string must contain the database name - public static void AddIdentityWithMongoStores(this IServiceCollection services, string connectionString) + public static IdentityBuilder AddIdentityWithMongoStores(this IServiceCollection services, string connectionString) { - services.AddIdentityWithMongoStoresUsingCustomTypes(connectionString); + return services.AddIdentityWithMongoStoresUsingCustomTypes(connectionString); } /// @@ -83,11 +83,11 @@ public static void AddIdentityWithMongoStores(this IServiceCollection services, /// /// /// Connection string must contain the database name - public static void AddIdentityWithMongoStoresUsingCustomTypes(this IServiceCollection services, string connectionString) + public static IdentityBuilder AddIdentityWithMongoStoresUsingCustomTypes(this IServiceCollection services, string connectionString) where TUser : IdentityUser where TRole : IdentityRole { - services.AddIdentity() + return services.AddIdentity() .RegisterMongoStores(connectionString); } } From 337e10b6b2e042d1322bb8d67b5bdd9e1cf34ac6 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Wed, 28 Sep 2016 01:39:37 -0400 Subject: [PATCH 51/56] Command for publishing to my local nuget feed --- src/build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/src/build.sh b/src/build.sh index 4578a70..c3d3660 100644 --- a/src/build.sh +++ b/src/build.sh @@ -20,3 +20,4 @@ dotnet test -c Release CoreIntegrationTests // create NuGet package dotnet pack -c Release Microsoft.AspNetCore.Identity.MongoDB +nuget add Microsoft.AspNetCore.Identity.MongoDB\bin\Release\Microsoft.AspNetCore.Identity.MongoDB.1.0.0.nupkg -Source C:\Code\scratch\localnugetfeedtesting From e6f1e41b7a2dc813ebc2c96c5354e3cc6639a3ef Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Wed, 28 Sep 2016 01:39:58 -0400 Subject: [PATCH 52/56] Release v1.0.1 Closes #24 --- src/Microsoft.AspNetCore.Identity.MongoDB/project.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/project.json b/src/Microsoft.AspNetCore.Identity.MongoDB/project.json index f45099a..240a7d6 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/project.json +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/project.json @@ -1,6 +1,6 @@ { "name": "Microsoft.AspNetCore.Identity.MongoDB", - "version": "1.0.0-*", + "version": "1.0.1", "description": "A MongoDB provider for ASP.NET Core Identity framework.", From fb63d340cc40951c9edb075513f49829a7ab1efe Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Wed, 28 Sep 2016 16:49:15 -0400 Subject: [PATCH 53/56] If an invalid objectid is passed to FindById, we return null as if the user doesn't exist. Previously was throwing an exception under the cover by the MongoDB Linq provider when converting id from string to objectid. This caused some people with v2 of Identity to have issues with users that have cookies that have old user ids before migrating to MongoDB Identity Stores. Small edge case but seems reasonable to do this, as this method already returns null if a user doesn't exist. Not a huge perf overhead either, and if it is a perf reason for someone they may want to write their own provider anyways. Fixes #14 --- src/CoreIntegrationTests/UserStoreTests.cs | 10 ++++++++++ .../UserStore.cs | 11 ++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/CoreIntegrationTests/UserStoreTests.cs b/src/CoreIntegrationTests/UserStoreTests.cs index 348821c..dfabf17 100644 --- a/src/CoreIntegrationTests/UserStoreTests.cs +++ b/src/CoreIntegrationTests/UserStoreTests.cs @@ -72,6 +72,16 @@ public async Task FindById_NoUser_ReturnsNull() Expect(foundUser, Is.Null); } + [Test] + public async Task FindById_IdIsNotAnObjectId_ReturnsNull() + { + var manager = GetUserManager(); + + var foundUser = await manager.FindByIdAsync("notanobjectid"); + + Expect(foundUser, Is.Null); + } + [Test] public async Task Delete_ExistingUser_Removes() { diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs index bb6d781..1053361 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs @@ -10,6 +10,7 @@ namespace Microsoft.AspNetCore.Identity.MongoDB using System.Security.Claims; using System.Threading; using System.Threading.Tasks; + using global::MongoDB.Bson; using global::MongoDB.Driver; /// @@ -80,7 +81,15 @@ public virtual async Task SetNormalizedUserNameAsync(TUser user, string normaliz => user.NormalizedUserName = normalizedUserName; public virtual Task FindByIdAsync(string userId, CancellationToken token) - => _Users.Find(u => u.Id == userId).FirstOrDefaultAsync(token); + => IsObjectId(userId) + ? _Users.Find(u => u.Id == userId).FirstOrDefaultAsync(token) + : Task.FromResult(null); + + private bool IsObjectId(string id) + { + ObjectId temp; + return ObjectId.TryParse(id, out temp); + } public virtual Task FindByNameAsync(string normalizedUserName, CancellationToken token) // todo low priority exception on duplicates? or better to enforce unique index to ensure this From 443687146b8acd37be71cd4dbeb7bc3953e849d9 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Wed, 28 Sep 2016 17:05:07 -0400 Subject: [PATCH 54/56] Cleaning up build script --- .gitignore | 2 ++ build.bat | 10 ++++++++++ src/build.sh | 23 ----------------------- 3 files changed, 12 insertions(+), 23 deletions(-) create mode 100644 build.bat delete mode 100644 src/build.sh diff --git a/.gitignore b/.gitignore index 2b6903c..c18f14f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +/artifacts/ + ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..2c5ce70 --- /dev/null +++ b/build.bat @@ -0,0 +1,10 @@ +REM optional clean +REM rm -rf artifacts CoreTests/bin CoreTests/obj CoreIntegrationTests/bin CoreIntegrationTests/obj Microsoft.AspNetCore.Identity.MongoDB/bin Microsoft.AspNetCore.Identity.MongoDB/obj + +dotnet restore src +dotnet test -c Release src/CoreTests +dotnet test -c Release src/CoreIntegrationTests +dotnet pack -c Release -o artifacts src/Microsoft.AspNetCore.Identity.MongoDB + +REM nuget add artifacts\X.nupkg -Source C:\Code\scratch\localnugetfeedtesting +REM nuget publish artifacts\X.nupkg \ No newline at end of file diff --git a/src/build.sh b/src/build.sh deleted file mode 100644 index c3d3660..0000000 --- a/src/build.sh +++ /dev/null @@ -1,23 +0,0 @@ -// clean -rm -rf CoreTests/bin CoreTests/obj CoreIntegrationTests/bin CoreIntegrationTests/obj Microsoft.AspNetCore.Identity.MongoDB/bin Microsoft.AspNetCore.Identity.MongoDB/obj - -// restore nuget package dependencies -dotnet restore - -// run tree command to see existing directory structure, not much - -// build all matched projects via project.json -// should be 6 projects in total (3 project * 2 frameworks each) -dotnet build -c Release */project.json -// debug build with -c Debug - -// run tree command, should see 6 new folders under 3 bin/Release/framework - -// run tests (each will run for both frameworks targeted) -dotnet test -c Release CoreTests -dotnet test -c Release CoreIntegrationTests - -// create NuGet package -dotnet pack -c Release Microsoft.AspNetCore.Identity.MongoDB - -nuget add Microsoft.AspNetCore.Identity.MongoDB\bin\Release\Microsoft.AspNetCore.Identity.MongoDB.1.0.0.nupkg -Source C:\Code\scratch\localnugetfeedtesting From 43f749cc9077df5cf1803e75863b836e1ce583e4 Mon Sep 17 00:00:00 2001 From: Wes Higbee Date: Sat, 1 Oct 2016 18:00:05 -0400 Subject: [PATCH 55/56] Go back to storing LockoutEndDateUtc as DateTime for sorting purposes if people query this directly, DateTimeOffset? doesn't serialize the same and we don't really need this type under the covers. --- README.md | 3 +-- src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs | 2 +- src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs | 5 +++-- src/Microsoft.AspNetCore.Identity.MongoDB/project.json | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4c9cf83..7fc92f4 100644 --- a/README.md +++ b/README.md @@ -55,5 +55,4 @@ run commands in [](build.sh) - On IdentityRole documents, create a NormalizedName field = uppercase(Name). Leave Name as is. - On IdentityUser documents, convert the values in the Roles array to uppercase - User names need to be normalized as follows - - On IdentityUser documents, create a NormalizedUserName field = uppercase(UserName) and create a NormalizedEmail field = uppercase(Email). Leave UserName and Email as is. -- LockoutEndDateUtc - type changed in code, but I think it is still the same in db, I have yet to verify if this requires a migration. + - On IdentityUser documents, create a NormalizedUserName field = uppercase(UserName) and create a NormalizedEmail field = uppercase(Email). Leave UserName and Email as is. \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs index c69f97f..7b5f6a5 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/IdentityUser.cs @@ -43,7 +43,7 @@ public IdentityUser() public virtual bool TwoFactorEnabled { get; set; } - public virtual DateTimeOffset? LockoutEndDateUtc { get; set; } + public virtual DateTime? LockoutEndDateUtc { get; set; } public virtual bool LockoutEnabled { get; set; } diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs index 1053361..405aaec 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/UserStore.cs @@ -240,12 +240,13 @@ public virtual Task GetTwoFactorEnabledAsync(TUser user, CancellationToken public virtual Task GetLockoutEndDateAsync(TUser user, CancellationToken token) { - return Task.FromResult(user.LockoutEndDateUtc); + DateTimeOffset? dateTimeOffset = user.LockoutEndDateUtc; + return Task.FromResult(dateTimeOffset); } public virtual Task SetLockoutEndDateAsync(TUser user, DateTimeOffset? lockoutEnd, CancellationToken token) { - user.LockoutEndDateUtc = lockoutEnd; + user.LockoutEndDateUtc = lockoutEnd?.UtcDateTime; return Task.FromResult(0); } diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/project.json b/src/Microsoft.AspNetCore.Identity.MongoDB/project.json index 240a7d6..f1321db 100644 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/project.json +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/project.json @@ -1,6 +1,6 @@ { "name": "Microsoft.AspNetCore.Identity.MongoDB", - "version": "1.0.1", + "version": "1.0.2", "description": "A MongoDB provider for ASP.NET Core Identity framework.", @@ -14,7 +14,7 @@ "packOptions": { "summary": "A MongoDB provider for ASP.NET Core Identity", "owners": ["Wes Higbee"], - "releaseNotes": "Port of AspNet.Identity.MongoDB to ASP.NET Core", + "releaseNotes": "Convert back to using DateTime to store LockoutEndDate, DateTimeOffset serializes to an array of values which could make it hard for people to sort on this and query on this. Also DateTime was used in the v2 driver, so this makes the upgrade story easier.", "tags": [ "aspnetcore", From 88318b7869e4f42dd698bfb92d13e2217cde6867 Mon Sep 17 00:00:00 2001 From: Romain Preston Date: Thu, 31 Aug 2017 17:19:51 +0100 Subject: [PATCH 56/56] VS 15.x & .Net standard 2.0 --- src/AspNet.Identity.MongoDB.sln | 13 +++-- .../CoreIntegrationTests.csproj | 34 ++++++++++++++ .../CoreIntegrationTests.xproj | 22 --------- src/CoreIntegrationTests/project.json | 28 ----------- src/CoreTests/CoreTests.csproj | 32 +++++++++++++ src/CoreTests/CoreTests.xproj | 22 --------- src/CoreTests/project.json | 26 ---------- ...crosoft.AspNetCore.Identity.MongoDB.csproj | 44 +++++++++++++++++ ...icrosoft.AspNetCore.Identity.MongoDB.xproj | 21 --------- .../project.json | 47 ------------------- 10 files changed, 118 insertions(+), 171 deletions(-) create mode 100644 src/CoreIntegrationTests/CoreIntegrationTests.csproj delete mode 100644 src/CoreIntegrationTests/CoreIntegrationTests.xproj delete mode 100644 src/CoreIntegrationTests/project.json create mode 100644 src/CoreTests/CoreTests.csproj delete mode 100644 src/CoreTests/CoreTests.xproj delete mode 100644 src/CoreTests/project.json create mode 100644 src/Microsoft.AspNetCore.Identity.MongoDB/Microsoft.AspNetCore.Identity.MongoDB.csproj delete mode 100644 src/Microsoft.AspNetCore.Identity.MongoDB/Microsoft.AspNetCore.Identity.MongoDB.xproj delete mode 100644 src/Microsoft.AspNetCore.Identity.MongoDB/project.json diff --git a/src/AspNet.Identity.MongoDB.sln b/src/AspNet.Identity.MongoDB.sln index 6712285..28f20a8 100644 --- a/src/AspNet.Identity.MongoDB.sln +++ b/src/AspNet.Identity.MongoDB.sln @@ -1,13 +1,13 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.12 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "CoreTests", "CoreTests\CoreTests.xproj", "{EAC53866-6DBF-40B7-900A-22267FD0634A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoreTests", "CoreTests\CoreTests.csproj", "{EAC53866-6DBF-40B7-900A-22267FD0634A}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Identity.MongoDB", "Microsoft.AspNetCore.Identity.MongoDB\Microsoft.AspNetCore.Identity.MongoDB.xproj", "{6DFF5058-E107-459E-87C3-DA41B2C1463C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.MongoDB", "Microsoft.AspNetCore.Identity.MongoDB\Microsoft.AspNetCore.Identity.MongoDB.csproj", "{6DFF5058-E107-459E-87C3-DA41B2C1463C}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "CoreIntegrationTests", "CoreIntegrationTests\CoreIntegrationTests.xproj", "{836F4635-9B6B-4090-8CDB-9CD9F7BEA829}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoreIntegrationTests", "CoreIntegrationTests\CoreIntegrationTests.csproj", "{836F4635-9B6B-4090-8CDB-9CD9F7BEA829}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -31,4 +31,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F4D69225-2814-4DFF-93D6-7AA341335C1C} + EndGlobalSection EndGlobal diff --git a/src/CoreIntegrationTests/CoreIntegrationTests.csproj b/src/CoreIntegrationTests/CoreIntegrationTests.csproj new file mode 100644 index 0000000..292762b --- /dev/null +++ b/src/CoreIntegrationTests/CoreIntegrationTests.csproj @@ -0,0 +1,34 @@ + + + + net451;netcoreapp1.0 + CoreIntegrationTests + CoreIntegrationTests + true + $(PackageTargetFallback);portable-net45+win8 + 1.0.4 + false + false + false + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/CoreIntegrationTests/CoreIntegrationTests.xproj b/src/CoreIntegrationTests/CoreIntegrationTests.xproj deleted file mode 100644 index 7737bec..0000000 --- a/src/CoreIntegrationTests/CoreIntegrationTests.xproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - 836f4635-9b6b-4090-8cdb-9cd9f7bea829 - CoreIntegrationTests - .\obj - .\bin\ - v4.6 - - - 2.0 - - - - - - \ No newline at end of file diff --git a/src/CoreIntegrationTests/project.json b/src/CoreIntegrationTests/project.json deleted file mode 100644 index 9c6ad56..0000000 --- a/src/CoreIntegrationTests/project.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "version": "1.0.0-*", - - "testRunner": "nunit", - - "dependencies": { - "CoreTests": "1.0.0-*", - "dotnet-test-nunit": "3.4.0-beta-2", - "Microsoft.AspNetCore.Identity.MongoDB": "1.0.0-*", - "Microsoft.Extensions.DependencyInjection": "1.0.0", - "Microsoft.Extensions.Logging": "1.0.0", - "mongocsharpdriver": "2.3.0", - "NUnit": "3.4.1" - }, - - "frameworks": { - "net451": {}, - "netcoreapp1.0": { - "imports": "portable-net45+win8", - "dependencies": { - "Microsoft.NETCore.App": { - "version": "1.0.0-*", - "type": "platform" - } - } - } - } -} diff --git a/src/CoreTests/CoreTests.csproj b/src/CoreTests/CoreTests.csproj new file mode 100644 index 0000000..b612c1e --- /dev/null +++ b/src/CoreTests/CoreTests.csproj @@ -0,0 +1,32 @@ + + + + net451;netcoreapp1.0 + CoreTests + CoreTests + true + $(PackageTargetFallback);portable-net45+win8 + 1.0.4 + false + false + false + + + + + + + + + + + + + + + + + + + + diff --git a/src/CoreTests/CoreTests.xproj b/src/CoreTests/CoreTests.xproj deleted file mode 100644 index 08832b8..0000000 --- a/src/CoreTests/CoreTests.xproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - eac53866-6dbf-40b7-900a-22267fd0634a - CoreTests - .\obj - .\bin\ - v4.6 - - - 2.0 - - - - - - \ No newline at end of file diff --git a/src/CoreTests/project.json b/src/CoreTests/project.json deleted file mode 100644 index 2653e9e..0000000 --- a/src/CoreTests/project.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "version": "1.0.0-*", - - "testRunner": "nunit", - - "dependencies": { - "dotnet-test-nunit": "3.4.0-beta-2", - "Microsoft.AspNetCore.Identity.MongoDB": "1.0.0-*", - "Microsoft.Extensions.DependencyInjection": "1.0.0", - "Microsoft.Extensions.Logging": "1.0.0", - "NUnit": "3.4.1" - }, - - "frameworks": { - "net451": {}, - "netcoreapp1.0": { - "imports": "portable-net45+win8", - "dependencies": { - "Microsoft.NETCore.App": { - "version": "1.0.0-*", - "type": "platform" - } - } - } - } -} diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/Microsoft.AspNetCore.Identity.MongoDB.csproj b/src/Microsoft.AspNetCore.Identity.MongoDB/Microsoft.AspNetCore.Identity.MongoDB.csproj new file mode 100644 index 0000000..a5a895d --- /dev/null +++ b/src/Microsoft.AspNetCore.Identity.MongoDB/Microsoft.AspNetCore.Identity.MongoDB.csproj @@ -0,0 +1,44 @@ + + + + A MongoDB provider for ASP.NET Core Identity framework. + 1.0.2 + Wes Higbee + net451;netstandard1.5;netstandard2.0 + true + Microsoft.AspNetCore.Identity.MongoDB + Microsoft.AspNetCore.Identity.MongoDB + aspnetcore;mongo;mongodb;identity;membership + Convert back to using DateTime to store LockoutEndDate, DateTimeOffset serializes to an array of values which could make it hard for people to sort on this and query on this. Also DateTime was used in the v2 driver, so this makes the upgrade story easier. + https://github.com/g0t4/aspnet-identity-mongo + https://github.com/g0t4/aspnet-identity-mongo/blob/master/LICENSE + git + https://github.com/g0t4/aspnet-identity-mongo + 1.6.0 + + false + false + false + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/Microsoft.AspNetCore.Identity.MongoDB.xproj b/src/Microsoft.AspNetCore.Identity.MongoDB/Microsoft.AspNetCore.Identity.MongoDB.xproj deleted file mode 100644 index fdfda80..0000000 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/Microsoft.AspNetCore.Identity.MongoDB.xproj +++ /dev/null @@ -1,21 +0,0 @@ - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - - 6dff5058-e107-459e-87c3-da41b2c1463c - Microsoft.AspNetCore.Identity.MongoDB - .\obj - .\bin\ - v4.6 - - - - 2.0 - - - diff --git a/src/Microsoft.AspNetCore.Identity.MongoDB/project.json b/src/Microsoft.AspNetCore.Identity.MongoDB/project.json deleted file mode 100644 index f1321db..0000000 --- a/src/Microsoft.AspNetCore.Identity.MongoDB/project.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "Microsoft.AspNetCore.Identity.MongoDB", - "version": "1.0.2", - - "description": - "A MongoDB provider for ASP.NET Core Identity framework.", - - "authors": ["Wes Higbee"], - - "buildOptions": { - "warningsAsErrors": true - }, - - "packOptions": { - "summary": "A MongoDB provider for ASP.NET Core Identity", - "owners": ["Wes Higbee"], - "releaseNotes": "Convert back to using DateTime to store LockoutEndDate, DateTimeOffset serializes to an array of values which could make it hard for people to sort on this and query on this. Also DateTime was used in the v2 driver, so this makes the upgrade story easier.", - - "tags": [ - "aspnetcore", - "mongo", - "mongodb", - "identity", - "membership" - ], - "repository": { - "type": "git", - "url": "https://github.com/g0t4/aspnet-identity-mongo" - }, - "licenseUrl": "https://github.com/g0t4/aspnet-identity-mongo/blob/master/LICENSE", - "projectUrl": "https://github.com/g0t4/aspnet-identity-mongo" - }, - - "dependencies": { - "Microsoft.AspNetCore.Identity": "1.0.0", - "MongoDB.Driver": "2.3.0" - }, - - "frameworks": { - "net451": {}, - "netstandard1.5": { - "dependencies": { - "NETStandard.Library": "1.6.0" - } - } - } -}