From bc4c53f086d6fb340b5c4bb35ab64cb77803e5b1 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 23 Jun 2014 14:53:06 -0700 Subject: [PATCH] Sql implementation + moving in memory stores to test - Identity.Entity now means only Sql Server EF Identity implementation - Identity.Entity.InMemory moved to a test project (Identity.Entity.InMemory.Test) - Identity.InMemory which was the identity in memory implementation also moved to a test project (Identity.InMemory.Test) - IClaimsFactory was promoted to a top level service and now adds any claims found in a Role that a user belongs to. - EF implementation now supports logins, claims, roles, two factor, lockouts - Initial pass at style cop (mostly line endings since resharper no worky :() --- Identity.sln | 26 +- .../HttpAuthenticationManager.cs | 3 +- .../IdentityBuilderExtensions.cs | 6 +- .../EntityIdentityBuilderExtensions.cs | 21 +- .../EntityRole.cs | 66 - .../EntityRoleStore.cs | 189 -- .../IdentityContext.cs | 86 - .../IdentityDbContext.cs | 106 + .../IdentitySqlContext.cs | 60 - ...soft.AspNet.Identity.EntityFramework.kproj | 11 +- .../Properties/Resources.Designer.cs | 62 + .../Resources.resx | 128 ++ .../RoleStore.cs | 76 +- .../SqlUserStore.cs | 384 ---- .../User.cs | 67 +- .../UserStore.cs | 852 ++++++++ .../project.json | 1 - .../project.json | 22 - .../ClaimTypeOptions.cs | 3 - .../ClaimsIdentityFactory.cs | 61 +- src/Microsoft.AspNet.Identity/Crypto.cs | 4 +- .../DefaultAuthenticationTypes.cs | 2 +- .../IClaimsIdentityFactory.cs | 7 +- .../IPasswordValidator.cs | 3 +- .../IRoleClaimStore.cs | 45 + src/Microsoft.AspNet.Identity/IRoleStore.cs | 3 +- .../IRoleValidator.cs | 5 +- .../IUserClaimStore.cs | 8 +- .../IUserEmailStore.cs | 6 +- .../IUserLockoutStore.cs | 28 +- .../IUserLoginStore.cs | 12 +- .../IUserPasswordStore.cs | 6 +- .../IUserPhoneNumberStore.cs | 9 +- .../IUserRoleStore.cs | 12 +- .../IUserSecurityStampStore.cs | 6 +- src/Microsoft.AspNet.Identity/IUserStore.cs | 5 +- .../IUserTokenProvider.cs | 12 +- .../IUserTwoFactorStore.cs | 6 +- .../IUserValidator.cs | 3 +- .../IdentityBuilder.cs | 29 +- .../IdentityOptions.cs | 6 +- .../IdentityResult.cs | 4 +- src/Microsoft.AspNet.Identity/IdentityRole.cs | 6 + .../IdentityRoleClaim.cs | 36 + .../IdentityServiceCollectionExtensions.cs | 33 +- .../IdentityServices.cs | 11 +- src/Microsoft.AspNet.Identity/IdentityUser.cs | 3 +- .../IdentityUserClaim.cs | 2 +- .../IdentityUserLogin.cs | 7 - .../IdentityUserRole.cs | 7 - .../LockoutOptions.cs | 13 - .../Microsoft.AspNet.Identity.kproj | 4 +- .../PasswordOptions.cs | 2 - .../PasswordValidator.cs | 6 +- .../Properties/Resources.Designer.cs | 590 ++++++ .../Resources.Designer.cs | 382 ---- src/Microsoft.AspNet.Identity/Resources.resx | 4 + src/Microsoft.AspNet.Identity/RoleManager.cs | 120 +- .../RoleValidator.cs | 6 +- .../SignInManager.cs | 14 +- src/Microsoft.AspNet.Identity/UserManager.cs | 254 +-- src/Microsoft.AspNet.Identity/UserOptions.cs | 2 - .../UserValidator.cs | 17 +- src/Microsoft.AspNet.Identity/project.json | 10 +- .../HttpSignInTest.cs | 57 +- .../IdentityExtensionsTest.cs | 2 +- .../EntityIdentityBuilderExtensions.cs | 24 + .../InMemoryContext.cs | 82 + .../InMemoryUser.cs | 106 + .../InMemoryUserStore.cs | 883 +++++++++ .../InMemoryUserStoreTest.cs | 1661 ++++++++++++++++ ...entity.EntityFramework.InMemory.Test.kproj | 33 + .../RoleStoreTest.cs | 99 + .../TestIdentityFactory.cs | 46 + .../project.json | 30 + ...AspNet.Identity.EntityFramework.Test.kproj | 8 +- .../SqlUserStoreTest.cs | 224 --- .../StartupTest.cs | 77 - .../UserStoreTest.cs | 1748 +++++++++++++++++ .../IdentityBuilderExtensions.cs | 22 + .../InMemoryRoleStore.cs | 105 + .../InMemoryUserStore.cs | 329 ++++ ...rosoft.AspNet.Identity.InMemory.Test.kproj | 5 +- .../project.json | 1 - .../ClaimsIdentityFactoryTest.cs | 67 +- .../IdentityBuilderTest.cs | 9 - .../UserManagerTest.cs | 25 +- test/Shared/IdentityConfig.cs | 4 +- test/Shared/MockHelpers.cs | 10 +- test/Shared/UserManagerTestBase.cs | 44 +- 90 files changed, 7738 insertions(+), 1943 deletions(-) delete mode 100644 src/Microsoft.AspNet.Identity.EntityFramework/EntityRole.cs delete mode 100644 src/Microsoft.AspNet.Identity.EntityFramework/EntityRoleStore.cs delete mode 100644 src/Microsoft.AspNet.Identity.EntityFramework/IdentityContext.cs create mode 100644 src/Microsoft.AspNet.Identity.EntityFramework/IdentityDbContext.cs delete mode 100644 src/Microsoft.AspNet.Identity.EntityFramework/IdentitySqlContext.cs create mode 100644 src/Microsoft.AspNet.Identity.EntityFramework/Properties/Resources.Designer.cs create mode 100644 src/Microsoft.AspNet.Identity.EntityFramework/Resources.resx delete mode 100644 src/Microsoft.AspNet.Identity.EntityFramework/SqlUserStore.cs create mode 100644 src/Microsoft.AspNet.Identity.EntityFramework/UserStore.cs delete mode 100644 src/Microsoft.AspNet.Identity.InMemory/project.json create mode 100644 src/Microsoft.AspNet.Identity/IRoleClaimStore.cs create mode 100644 src/Microsoft.AspNet.Identity/IdentityRoleClaim.cs create mode 100644 src/Microsoft.AspNet.Identity/Properties/Resources.Designer.cs delete mode 100644 src/Microsoft.AspNet.Identity/Resources.Designer.cs create mode 100644 test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/EntityIdentityBuilderExtensions.cs create mode 100644 test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryContext.cs create mode 100644 test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryUser.cs create mode 100644 test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryUserStore.cs create mode 100644 test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryUserStoreTest.cs create mode 100644 test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test.kproj create mode 100644 test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/RoleStoreTest.cs create mode 100644 test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/TestIdentityFactory.cs create mode 100644 test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/project.json delete mode 100644 test/Microsoft.AspNet.Identity.EntityFramework.Test/SqlUserStoreTest.cs delete mode 100644 test/Microsoft.AspNet.Identity.EntityFramework.Test/StartupTest.cs create mode 100644 test/Microsoft.AspNet.Identity.EntityFramework.Test/UserStoreTest.cs create mode 100644 test/Microsoft.AspNet.Identity.InMemory.Test/IdentityBuilderExtensions.cs create mode 100644 test/Microsoft.AspNet.Identity.InMemory.Test/InMemoryRoleStore.cs create mode 100644 test/Microsoft.AspNet.Identity.InMemory.Test/InMemoryUserStore.cs diff --git a/Identity.sln b/Identity.sln index b987927f6e..3195524ced 100644 --- a/Identity.sln +++ b/Identity.sln @@ -7,8 +7,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0F647068-660 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{52D59F18-62D2-4D17-8CF2-BE192445AF8E}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Identity.InMemory", "src\Microsoft.AspNet.Identity.InMemory\Microsoft.AspNet.Identity.InMemory.kproj", "{01A9DA71-5C42-4ED5-AD0C-D6FAF11C0A74}" -EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Identity", "src\Microsoft.AspNet.Identity\Microsoft.AspNet.Identity.kproj", "{1729302E-A58E-4652-B639-5B6B68DA2748}" EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Identity.EntityFramework", "src\Microsoft.AspNet.Identity.EntityFramework\Microsoft.AspNet.Identity.EntityFramework.kproj", "{AD42BAFB-1993-4FAF-A280-3711A9F33E2F}" @@ -23,6 +21,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Identity.I EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Identity.EntityFramework.Test", "test\Microsoft.AspNet.Identity.EntityFramework.Test\Microsoft.AspNet.Identity.EntityFramework.Test.kproj", "{B4C067C1-F934-493C-9DBC-19E8CA305613}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNet.Identity.EntityFramework.InMemory.Test", "test\Microsoft.AspNet.Identity.EntityFramework.InMemory.Test\Microsoft.AspNet.Identity.EntityFramework.InMemory.Test.kproj", "{813B36FE-BBA5-4449-B157-6EBBA5ED02CA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -33,16 +33,6 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {01A9DA71-5C42-4ED5-AD0C-D6FAF11C0A74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {01A9DA71-5C42-4ED5-AD0C-D6FAF11C0A74}.Debug|Any CPU.Build.0 = Debug|Any CPU - {01A9DA71-5C42-4ED5-AD0C-D6FAF11C0A74}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {01A9DA71-5C42-4ED5-AD0C-D6FAF11C0A74}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {01A9DA71-5C42-4ED5-AD0C-D6FAF11C0A74}.Debug|x86.ActiveCfg = Debug|Any CPU - {01A9DA71-5C42-4ED5-AD0C-D6FAF11C0A74}.Release|Any CPU.ActiveCfg = Release|Any CPU - {01A9DA71-5C42-4ED5-AD0C-D6FAF11C0A74}.Release|Any CPU.Build.0 = Release|Any CPU - {01A9DA71-5C42-4ED5-AD0C-D6FAF11C0A74}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {01A9DA71-5C42-4ED5-AD0C-D6FAF11C0A74}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {01A9DA71-5C42-4ED5-AD0C-D6FAF11C0A74}.Release|x86.ActiveCfg = Release|Any CPU {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|Any CPU.Build.0 = Debug|Any CPU {1729302E-A58E-4652-B639-5B6B68DA2748}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU @@ -113,12 +103,21 @@ Global {B4C067C1-F934-493C-9DBC-19E8CA305613}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {B4C067C1-F934-493C-9DBC-19E8CA305613}.Release|Mixed Platforms.Build.0 = Release|Any CPU {B4C067C1-F934-493C-9DBC-19E8CA305613}.Release|x86.ActiveCfg = Release|Any CPU + {813B36FE-BBA5-4449-B157-6EBBA5ED02CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {813B36FE-BBA5-4449-B157-6EBBA5ED02CA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {813B36FE-BBA5-4449-B157-6EBBA5ED02CA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {813B36FE-BBA5-4449-B157-6EBBA5ED02CA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {813B36FE-BBA5-4449-B157-6EBBA5ED02CA}.Debug|x86.ActiveCfg = Debug|Any CPU + {813B36FE-BBA5-4449-B157-6EBBA5ED02CA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {813B36FE-BBA5-4449-B157-6EBBA5ED02CA}.Release|Any CPU.Build.0 = Release|Any CPU + {813B36FE-BBA5-4449-B157-6EBBA5ED02CA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {813B36FE-BBA5-4449-B157-6EBBA5ED02CA}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {813B36FE-BBA5-4449-B157-6EBBA5ED02CA}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {01A9DA71-5C42-4ED5-AD0C-D6FAF11C0A74} = {0F647068-6602-4E24-B1DC-8ED91481A50A} {1729302E-A58E-4652-B639-5B6B68DA2748} = {0F647068-6602-4E24-B1DC-8ED91481A50A} {AD42BAFB-1993-4FAF-A280-3711A9F33E2F} = {0F647068-6602-4E24-B1DC-8ED91481A50A} {7B4CFF5A-1948-45EC-B170-6EB7C039B2F9} = {0F647068-6602-4E24-B1DC-8ED91481A50A} @@ -126,5 +125,6 @@ Global {823453CC-5846-4D49-B343-15BC0074CA60} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E} {65161409-C4C4-4D63-A73B-231FCFF4D503} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E} {B4C067C1-F934-493C-9DBC-19E8CA305613} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E} + {813B36FE-BBA5-4449-B157-6EBBA5ED02CA} = {52D59F18-62D2-4D17-8CF2-BE192445AF8E} EndGlobalSection EndGlobal diff --git a/src/Microsoft.AspNet.Identity.Authentication/HttpAuthenticationManager.cs b/src/Microsoft.AspNet.Identity.Authentication/HttpAuthenticationManager.cs index 1bb1017d3d..28cf3c3f53 100644 --- a/src/Microsoft.AspNet.Identity.Authentication/HttpAuthenticationManager.cs +++ b/src/Microsoft.AspNet.Identity.Authentication/HttpAuthenticationManager.cs @@ -1,14 +1,13 @@ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Security; using Microsoft.Framework.DependencyInjection; -namespace Microsoft.AspNet.Identity.Security +namespace Microsoft.AspNet.Identity.Authentication { public class HttpAuthenticationManager : IAuthenticationManager { diff --git a/src/Microsoft.AspNet.Identity.Authentication/IdentityBuilderExtensions.cs b/src/Microsoft.AspNet.Identity.Authentication/IdentityBuilderExtensions.cs index 0ef31f0d96..2f56d6ccac 100644 --- a/src/Microsoft.AspNet.Identity.Authentication/IdentityBuilderExtensions.cs +++ b/src/Microsoft.AspNet.Identity.Authentication/IdentityBuilderExtensions.cs @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.AspNet.Identity.Security; -using Microsoft.Framework.DependencyInjection; +using Microsoft.AspNet.Identity.Authentication; +using Microsoft.AspNet.Identity; -namespace Microsoft.AspNet.Identity +namespace Microsoft.Framework.DependencyInjection { public static class IdentityBuilderExtensions { diff --git a/src/Microsoft.AspNet.Identity.EntityFramework/EntityIdentityBuilderExtensions.cs b/src/Microsoft.AspNet.Identity.EntityFramework/EntityIdentityBuilderExtensions.cs index 7412967337..7f5752e4d3 100644 --- a/src/Microsoft.AspNet.Identity.EntityFramework/EntityIdentityBuilderExtensions.cs +++ b/src/Microsoft.AspNet.Identity.EntityFramework/EntityIdentityBuilderExtensions.cs @@ -1,31 +1,20 @@ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.AspNet.Identity.Entity; +using Microsoft.AspNet.Identity; +using Microsoft.AspNet.Identity.EntityFramework; using Microsoft.Data.Entity; -using Microsoft.Framework.DependencyInjection; -// Move to DI namespace? -namespace Microsoft.AspNet.Identity +namespace Microsoft.Framework.DependencyInjection { public static class EntityIdentityBuilderExtensions { - public static IdentityBuilder AddEntityFrameworkInMemory(this IdentityBuilder builder) - where TUser : EntityUser - where TRole : EntityRole - where TDbContext : DbContext - { - builder.Services.AddScoped(); - builder.Services.AddScoped, InMemoryUserStore>(); - builder.Services.AddScoped, EntityRoleStore>(); - return builder; - } - // todo: add overloads public static IdentityBuilder AddEntityFramework(this IdentityBuilder builder) where TUser : User where TContext : DbContext { - builder.Services.AddScoped, UserStore>(); + builder.Services.AddScoped, UserStore>(); + builder.Services.AddScoped, RoleStore>(); builder.Services.AddScoped(); return builder; } diff --git a/src/Microsoft.AspNet.Identity.EntityFramework/EntityRole.cs b/src/Microsoft.AspNet.Identity.EntityFramework/EntityRole.cs deleted file mode 100644 index ca95899ce0..0000000000 --- a/src/Microsoft.AspNet.Identity.EntityFramework/EntityRole.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; - -namespace Microsoft.AspNet.Identity.Entity -{ - /// - /// Represents a Role entity - /// - public class EntityRole : EntityRole - { - /// - /// Constructor - /// - public EntityRole() - { - Id = Guid.NewGuid().ToString(); - } - - /// - /// Constructor - /// - /// - public EntityRole(string roleName) - : this() - { - Name = roleName; - } - } - - /// - /// Represents a Role entity - /// - /// - /// - public class EntityRole - where TUserRole : IdentityUserRole - where TKey : IEquatable - { - /// - /// Constructor - /// - public EntityRole() - { - Users = new List(); - } - - /// - /// Navigation property for users in the role - /// - public virtual ICollection Users { get; private set; } - - /// - /// Role id - /// - public virtual TKey Id { get; set; } - - /// - /// Role name - /// - public virtual string Name { get; set; } - } -} - diff --git a/src/Microsoft.AspNet.Identity.EntityFramework/EntityRoleStore.cs b/src/Microsoft.AspNet.Identity.EntityFramework/EntityRoleStore.cs deleted file mode 100644 index 2fd53e7429..0000000000 --- a/src/Microsoft.AspNet.Identity.EntityFramework/EntityRoleStore.cs +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Linq; -using System.Linq.Expressions; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Data.Entity; - -namespace Microsoft.AspNet.Identity.Entity -{ - public class EntityRoleStore : EntityRoleStore - where TRole : EntityRole - { - public EntityRoleStore(IdentityContext context) : base(context) { } - } - - public class EntityRoleStore : EntityRoleStore - where TRole : EntityRole - where TContext : DbContext - { - public EntityRoleStore(TContext context) : base(context) { } - } - - public class EntityRoleStore : - IQueryableRoleStore - where TRole : EntityRole - where TKey : IEquatable - where TContext : DbContext - { - private bool _disposed; - - public EntityRoleStore(TContext context) - { - if (context == null) - { - throw new ArgumentNullException("context"); - } - Context = context; - AutoSaveChanges = true; - } - - public DbContext Context { get; private set; } - - /// - /// If true will call SaveChanges after CreateAsync/UpdateAsync/DeleteAsync - /// - public bool AutoSaveChanges { get; set; } - - private async Task SaveChanges(CancellationToken cancellationToken) - { - if (AutoSaveChanges) - { - await Context.SaveChangesAsync(cancellationToken); - } - } - - public virtual Task GetRoleAggregate(Expression> filter, CancellationToken cancellationToken = default(CancellationToken)) - { - // TODO: return Roles.SingleOrDefaultAsync(filter, cancellationToken); - return Task.FromResult(Roles.SingleOrDefault(filter)); - } - - public async virtual Task CreateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) - { - cancellationToken.ThrowIfCancellationRequested(); - ThrowIfDisposed(); - if (role == null) - { - throw new ArgumentNullException("role"); - } - await Context.AddAsync(role, cancellationToken); - await SaveChanges(cancellationToken); - } - - public async virtual Task UpdateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) - { - cancellationToken.ThrowIfCancellationRequested(); - ThrowIfDisposed(); - if (role == null) - { - throw new ArgumentNullException("role"); - } - await Context.UpdateAsync(role, cancellationToken); - await SaveChanges(cancellationToken); - } - - public async virtual Task DeleteAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) - { - cancellationToken.ThrowIfCancellationRequested(); - ThrowIfDisposed(); - if (role == null) - { - throw new ArgumentNullException("role"); - } - Context.Delete(role); - await SaveChanges(cancellationToken); - } - - public Task GetRoleIdAsync(TRole role, CancellationToken cancellationToken = new CancellationToken()) - { - cancellationToken.ThrowIfCancellationRequested(); - ThrowIfDisposed(); - if (role == null) - { - throw new ArgumentNullException("role"); - } - return Task.FromResult(role.Id); - } - - public Task GetRoleNameAsync(TRole role, CancellationToken cancellationToken = new CancellationToken()) - { - cancellationToken.ThrowIfCancellationRequested(); - ThrowIfDisposed(); - if (role == null) - { - throw new ArgumentNullException("role"); - } - return Task.FromResult(role.Name); - } - - public Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken = new CancellationToken()) - { - cancellationToken.ThrowIfCancellationRequested(); - ThrowIfDisposed(); - if (role == null) - { - throw new ArgumentNullException("role"); - } - role.Name = roleName; - return Task.FromResult(0); - } - - - public virtual TKey ConvertId(string userId) - { - return (TKey)Convert.ChangeType(userId, typeof(TKey)); - } - - /// - /// Find a role by id - /// - /// - /// - /// - public virtual Task FindByIdAsync(string id, CancellationToken cancellationToken = default(CancellationToken)) - { - cancellationToken.ThrowIfCancellationRequested(); - ThrowIfDisposed(); - var roleId = ConvertId(id); - return GetRoleAggregate(u => u.Id.Equals(roleId), cancellationToken); - } - - /// - /// Find a role by name - /// - /// - /// - /// - public virtual Task FindByNameAsync(string name, CancellationToken cancellationToken = default(CancellationToken)) - { - cancellationToken.ThrowIfCancellationRequested(); - ThrowIfDisposed(); - return GetRoleAggregate(u => u.Name.ToUpper() == name.ToUpper(), cancellationToken); - } - - private void ThrowIfDisposed() - { - if (_disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - } - - /// - /// Dispose the store - /// - public void Dispose() - { - _disposed = true; - } - - public IQueryable Roles - { - get { return Context.Set(); } - } - } -} diff --git a/src/Microsoft.AspNet.Identity.EntityFramework/IdentityContext.cs b/src/Microsoft.AspNet.Identity.EntityFramework/IdentityContext.cs deleted file mode 100644 index a533407cbd..0000000000 --- a/src/Microsoft.AspNet.Identity.EntityFramework/IdentityContext.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using Microsoft.Data.Entity; -using Microsoft.Data.Entity.SqlServer; -using Microsoft.Data.Entity.InMemory; -using Microsoft.Data.Entity.Metadata; -using Microsoft.Framework.DependencyInjection; -using Microsoft.Framework.DependencyInjection.Fallback; - -namespace Microsoft.AspNet.Identity.Entity -{ - public class IdentityContext : - IdentityContext - { - public IdentityContext() { } - public IdentityContext(IServiceProvider serviceProvider) : base(serviceProvider) { } - } - - public class IdentityContext : - IdentityContext - where TUser : EntityUser - { - public IdentityContext() { } - public IdentityContext(IServiceProvider serviceProvider) : base(serviceProvider) { } - } - - public class IdentityContext : DbContext - where TUser : EntityUser - where TRole : EntityRole - where TUserLogin : IdentityUserLogin - where TUserRole : IdentityUserRole - where TUserClaim : IdentityUserClaim - where TKey : IEquatable - { - - public DbSet Users { get; set; } - public DbSet Roles { get; set; } - - public IdentityContext(IServiceProvider serviceProvider) - : base(serviceProvider) { } - - public IdentityContext() { } - - protected override void OnConfiguring(DbContextOptions builder) - { -//#if NET45 -// builder.SqlServerConnectionString(@"Server=(localdb)\v11.0;Database=IdentityEF5-5-1;Trusted_Connection=True;"); -//#else - builder.UseInMemoryStore(); -//#endif - } - - protected override void OnModelCreating(ModelBuilder builder) - { - builder.Entity() - .Key(u => u.Id) - .Properties(ps => ps.Property(u => u.UserName)); - //.ToTable("AspNetUsers"); - builder.Entity() - .Key(r => r.Id); - //.ToTable("AspNetRoles"); - - builder.Entity() - .Key(u => u.Id) - //TODO: .Key(r => new { r.UserId, r.RoleId }) - .ForeignKeys(fk => fk.ForeignKey(f => f.UserId)) - .ForeignKeys(fk => fk.ForeignKey(f => f.RoleId)); - //.ToTable("AspNetUserRoles"); - - builder.Entity() - .Key(u => u.Id) - //TODO: .Key(l => new { l.LoginProvider, l.ProviderKey, l.UserId }) - .ForeignKeys(fk => fk.ForeignKey(f => f.UserId)); - //.ToTable("AspNetUserLogins"); - - builder.Entity() - .Key(c => c.Id) - .ForeignKeys(fk => fk.ForeignKey(f => f.UserId)); - //.ToTable("AspNetUserClaims"); - - } - - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity.EntityFramework/IdentityDbContext.cs b/src/Microsoft.AspNet.Identity.EntityFramework/IdentityDbContext.cs new file mode 100644 index 0000000000..d1627e7b57 --- /dev/null +++ b/src/Microsoft.AspNet.Identity.EntityFramework/IdentityDbContext.cs @@ -0,0 +1,106 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.Data.Entity; +using Microsoft.Data.Entity.Metadata; + +namespace Microsoft.AspNet.Identity.EntityFramework +{ + public class IdentityDbContext : + IdentityDbContext + { + public IdentityDbContext() { } + public IdentityDbContext(IServiceProvider serviceProvider) : base(serviceProvider) { } + public IdentityDbContext(IServiceProvider serviceProvider, string nameOrConnectionString) : base(serviceProvider, nameOrConnectionString) { } + public IdentityDbContext(DbContextOptions options) : base(options) { } + public IdentityDbContext(IServiceProvider serviceProvider, DbContextOptions options) : base(serviceProvider, options) { } + } + + public class IdentityDbContext : + IdentityDbContext + where TUser : User + { + public IdentityDbContext() { } + public IdentityDbContext(IServiceProvider serviceProvider) : base(serviceProvider) { } + public IdentityDbContext(IServiceProvider serviceProvider, string nameOrConnectionString) : base(serviceProvider, nameOrConnectionString) { } + public IdentityDbContext(DbContextOptions options) : base(options) { } + public IdentityDbContext(IServiceProvider serviceProvider, DbContextOptions options) : base(serviceProvider, options) { } + } + + public class IdentityDbContext : DbContext + where TUser : User + where TRole : IdentityRole + { + public DbSet Users { get; set; } + public DbSet UserClaims { get; set; } + public DbSet UserLogins { get; set; } + public DbSet UserRoles { get; set; } + public DbSet Roles { get; set; } + public DbSet RoleClaims { get; set; } + + private readonly string _nameOrConnectionString; + + public IdentityDbContext() { } + public IdentityDbContext(IServiceProvider serviceProvider, string nameOrConnectionString) : base(serviceProvider) + { + _nameOrConnectionString = nameOrConnectionString; + } + public IdentityDbContext(IServiceProvider serviceProvider) : base(serviceProvider) { } + public IdentityDbContext(DbContextOptions options) : base(options) { } + public IdentityDbContext(IServiceProvider serviceProvider, DbContextOptions options) : base(serviceProvider, options) { } + + protected override void OnConfiguring(DbContextOptions builder) + { + if (!string.IsNullOrEmpty(_nameOrConnectionString)) + { + builder.UseSqlServer(_nameOrConnectionString); + } + } + + protected override void OnModelCreating(ModelBuilder builder) + { + builder.Entity() + .Key(u => u.Id) + .Properties(ps => ps.Property(u => u.UserName)) + .ToTable("AspNetUsers"); + + builder.Entity() + .Key(r => r.Id) + .Properties(ps => ps.Property(r => r.Name)) + .ToTable("AspNetRoles"); + + builder.Entity() + .Key(uc => uc.Id) + .ToTable("AspNetUserClaims"); + + var userType = builder.Model.GetEntityType(typeof(TUser)); + var roleType = builder.Model.GetEntityType(typeof(TRole)); + var userClaimType = builder.Model.GetEntityType(typeof(IdentityUserClaim)); + var roleClaimType = builder.Model.GetEntityType(typeof(IdentityRoleClaim)); + var userRoleType = builder.Model.GetEntityType(typeof(IdentityUserRole)); + var ucfk = userClaimType.AddForeignKey(userType.GetKey(), new[] { userClaimType.GetProperty("UserId") }); + userType.AddNavigation(new Navigation(ucfk, "Claims", false)); + //userClaimType.AddNavigation(new Navigation(ucfk, "User", true)); + //var urfk = userRoleType.AddForeignKey(userType.GetKey(), new[] { userRoleType.GetProperty("UserId") }); + //userType.AddNavigation(new Navigation(urfk, "Roles", false)); + + //var urfk2 = userRoleType.AddForeignKey(roleType.GetKey(), new[] { userRoleType.GetProperty("RoleId") }); + //roleType.AddNavigation(new Navigation(urfk2, "Users", false)); + + var rcfk = roleClaimType.AddForeignKey(roleType.GetKey(), new[] { roleClaimType.GetProperty("RoleId") }); + roleType.AddNavigation(new Navigation(rcfk, "Claims", false)); + + builder.Entity() + .Key(r => new { r.UserId, r.RoleId }) + //.ForeignKeys(fk => fk.ForeignKey(f => f.UserId)) + //.ForeignKeys(fk => fk.ForeignKey(f => f.RoleId)); + .ToTable("AspNetUserRoles"); + + builder.Entity() + .Key(l => new { l.LoginProvider, l.ProviderKey, l.UserId }) + //.ForeignKeys(fk => fk.ForeignKey(f => f.UserId)); + .ToTable("AspNetUserLogins"); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity.EntityFramework/IdentitySqlContext.cs b/src/Microsoft.AspNet.Identity.EntityFramework/IdentitySqlContext.cs deleted file mode 100644 index 28990af88f..0000000000 --- a/src/Microsoft.AspNet.Identity.EntityFramework/IdentitySqlContext.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using Microsoft.Data.Entity; -using Microsoft.Data.Entity.Metadata; - -namespace Microsoft.AspNet.Identity.Entity -{ - public class IdentitySqlContext : - IdentitySqlContext - { - public IdentitySqlContext() { } - public IdentitySqlContext(IServiceProvider serviceProvider) : base(serviceProvider) { } - public IdentitySqlContext(IServiceProvider serviceProvider, string nameOrConnectionString) : base(serviceProvider, nameOrConnectionString) { } - public IdentitySqlContext(DbContextOptions options) : base(options) { } - public IdentitySqlContext(IServiceProvider serviceProvider, DbContextOptions options) : base(serviceProvider, options) { } - } - - public class IdentitySqlContext : DbContext - where TUser : User - { - public DbSet Users { get; set; } - public DbSet UserClaims { get; set; } - //public DbSet Roles { get; set; } - - private readonly string _nameOrConnectionString; - - public IdentitySqlContext() { } - public IdentitySqlContext(IServiceProvider serviceProvider, string nameOrConnectionString) : base(serviceProvider) - { - _nameOrConnectionString = nameOrConnectionString; - } - public IdentitySqlContext(IServiceProvider serviceProvider) : base(serviceProvider) { } - public IdentitySqlContext(DbContextOptions options) : base(options) { } - public IdentitySqlContext(IServiceProvider serviceProvider, DbContextOptions options) : base(serviceProvider, options) { } - - protected override void OnConfiguring(DbContextOptions builder) - { - if (!string.IsNullOrEmpty(_nameOrConnectionString)) - { - builder.UseSqlServer(_nameOrConnectionString); - } - } - - protected override void OnModelCreating(ModelBuilder builder) - { - builder.Entity() - .Key(u => u.Id) - .Properties(ps => ps.Property(u => u.UserName)) - .ToTable("AspNetUsers"); - - builder.Entity() - .Key(uc => uc.Id) - // TODO: this throws a length exception currently, investigate - //.ForeignKeys(fk => fk.ForeignKey(f => f.UserId)) - .ToTable("AspNetUserClaims"); - } - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity.EntityFramework/Microsoft.AspNet.Identity.EntityFramework.kproj b/src/Microsoft.AspNet.Identity.EntityFramework/Microsoft.AspNet.Identity.EntityFramework.kproj index ed254fa09f..d9cdbd94b5 100644 --- a/src/Microsoft.AspNet.Identity.EntityFramework/Microsoft.AspNet.Identity.EntityFramework.kproj +++ b/src/Microsoft.AspNet.Identity.EntityFramework/Microsoft.AspNet.Identity.EntityFramework.kproj @@ -18,18 +18,15 @@ + - - - - - - + + - + \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity.EntityFramework/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Identity.EntityFramework/Properties/Resources.Designer.cs new file mode 100644 index 0000000000..540771d085 --- /dev/null +++ b/src/Microsoft.AspNet.Identity.EntityFramework/Properties/Resources.Designer.cs @@ -0,0 +1,62 @@ +// +namespace Microsoft.AspNet.Identity.EntityFramework +{ + using System.Globalization; + using System.Reflection; + using System.Resources; + + internal static class Resources + { + private static readonly ResourceManager _resourceManager + = new ResourceManager("Microsoft.AspNet.Identity.EntityFramework.Resources", typeof(Resources).GetTypeInfo().Assembly); + + /// + /// Role {0} does not exist. + /// + internal static string RoleNotFound + { + get { return GetString("RoleNotFound"); } + } + + /// + /// Role {0} does not exist. + /// + internal static string FormatRoleNotFound(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("RoleNotFound"), p0); + } + + /// + /// Value cannot be null or empty. + /// + internal static string ValueCannotBeNullOrEmpty + { + get { return GetString("ValueCannotBeNullOrEmpty"); } + } + + /// + /// Value cannot be null or empty. + /// + internal static string FormatValueCannotBeNullOrEmpty() + { + return GetString("ValueCannotBeNullOrEmpty"); + } + + private static string GetString(string name, params string[] formatterNames) + { + var value = _resourceManager.GetString(name); + + System.Diagnostics.Debug.Assert(value != null); + + if (formatterNames != null) + { + for (var i = 0; i < formatterNames.Length; i++) + { + value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); + } + } + + return value; + } + } +} diff --git a/src/Microsoft.AspNet.Identity.EntityFramework/Resources.resx b/src/Microsoft.AspNet.Identity.EntityFramework/Resources.resx new file mode 100644 index 0000000000..64377b6996 --- /dev/null +++ b/src/Microsoft.AspNet.Identity.EntityFramework/Resources.resx @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Role {0} does not exist. + error when a role does not exist + + + Value cannot be null or empty. + error when something cannot be null or empty + + \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity.EntityFramework/RoleStore.cs b/src/Microsoft.AspNet.Identity.EntityFramework/RoleStore.cs index c7f0858a1b..2a0b21303b 100644 --- a/src/Microsoft.AspNet.Identity.EntityFramework/RoleStore.cs +++ b/src/Microsoft.AspNet.Identity.EntityFramework/RoleStore.cs @@ -2,27 +2,38 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using System.Security.Claims; using System.Threading; using System.Threading.Tasks; using Microsoft.Data.Entity; -namespace Microsoft.AspNet.Identity.Entity +namespace Microsoft.AspNet.Identity.EntityFramework { - public class RoleStore : RoleStore where TRole : EntityRole + public class RoleStore : RoleStore where TRole : IdentityRole { public RoleStore(DbContext context) : base(context) { } } - public class RoleStore : - IQueryableRoleStore - where TRole : EntityRole + public class RoleStore : RoleStore + where TRole : IdentityRole + where TContext : DbContext + { + public RoleStore(TContext context) : base(context) { } + } + + public class RoleStore : + IQueryableRoleStore, + IRoleClaimStore + where TRole : IdentityRole where TKey : IEquatable + where TContext : DbContext { private bool _disposed; - public RoleStore(DbContext context) + public RoleStore(TContext context) { if (context == null) { @@ -32,7 +43,7 @@ namespace Microsoft.AspNet.Identity.Entity AutoSaveChanges = true; } - public DbContext Context { get; private set; } + public TContext Context { get; private set; } /// /// If true will call SaveChanges after CreateAsync/UpdateAsync/DeleteAsync @@ -49,8 +60,7 @@ namespace Microsoft.AspNet.Identity.Entity public virtual Task GetRoleAggregate(Expression> filter, CancellationToken cancellationToken = default(CancellationToken)) { - // TODO: return Roles.SingleOrDefaultAsync(filter, cancellationToken); - return Task.FromResult(Roles.SingleOrDefault(filter)); + return Task.FromResult(Roles.FirstOrDefault(filter)); } public async virtual Task CreateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) @@ -123,7 +133,6 @@ namespace Microsoft.AspNet.Identity.Entity return Task.FromResult(0); } - public virtual TKey ConvertId(string userId) { return (TKey)Convert.ChangeType(userId, typeof(TKey)); @@ -172,9 +181,56 @@ namespace Microsoft.AspNet.Identity.Entity _disposed = true; } + public Task> GetClaimsAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException("role"); + } + IList result = RoleClaims.Where(rc => rc.RoleId == role.Id).Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToList(); + return Task.FromResult(result); + } + + public Task AddClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken)) + { + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException("role"); + } + if (claim == null) + { + throw new ArgumentNullException("claim"); + } + RoleClaims.Add(new IdentityRoleClaim { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value }); + return Task.FromResult(0); + } + + public Task RemoveClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken)) + { + ThrowIfDisposed(); + if (role == null) + { + throw new ArgumentNullException("role"); + } + if (claim == null) + { + throw new ArgumentNullException("claim"); + } + var claims = RoleClaims.Where(uc => uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToList(); + foreach (var c in claims) + { + RoleClaims.Remove(c); + } + return Task.FromResult(0); + } + public IQueryable Roles { get { return Context.Set(); } } + private DbSet RoleClaims { get { return Context.Set(); } } + } } diff --git a/src/Microsoft.AspNet.Identity.EntityFramework/SqlUserStore.cs b/src/Microsoft.AspNet.Identity.EntityFramework/SqlUserStore.cs deleted file mode 100644 index ce7b1e9fcb..0000000000 --- a/src/Microsoft.AspNet.Identity.EntityFramework/SqlUserStore.cs +++ /dev/null @@ -1,384 +0,0 @@ -// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Linq.Expressions; -using System.Security.Claims; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Data.Entity; - -namespace Microsoft.AspNet.Identity.Entity -{ - public class UserStore : UserStore where TUser : User - { - public UserStore(DbContext context) : base(context) { } - } - - public class UserStore : - //IUserRoleStore, - IUserPasswordStore, - IQueryableUserStore, - IUserClaimStore - where TUser : User - where TContext : DbContext - { - private bool _disposed; - - public UserStore(TContext context) - { - if (context == null) - { - throw new ArgumentNullException("context"); - } - Context = context; - AutoSaveChanges = true; - } - - public TContext Context { get; private set; } - - /// - /// If true will call SaveChanges after CreateAsync/UpdateAsync/DeleteAsync - /// - public bool AutoSaveChanges { get; set; } - - private Task SaveChanges(CancellationToken cancellationToken) - { - return AutoSaveChanges ? Context.SaveChangesAsync(cancellationToken) : Task.FromResult(0); - } - - protected virtual Task GetUserAggregate(Expression> filter, CancellationToken cancellationToken = default(CancellationToken)) - { - return Task.FromResult(Users.SingleOrDefault(filter)); - // TODO: return Users.SingleOrDefaultAsync(filter, cancellationToken); - //Include(u => u.Roles) - //.Include(u => u.Claims) - //.Include(u => u.Logins) - } - - public Task GetUserIdAsync(TUser user, CancellationToken cancellationToken = new CancellationToken()) - { - cancellationToken.ThrowIfCancellationRequested(); - ThrowIfDisposed(); - if (user == null) - { - throw new ArgumentNullException("user"); - } - return Task.FromResult(Convert.ToString(user.Id, CultureInfo.InvariantCulture)); - } - - public Task GetUserNameAsync(TUser user, CancellationToken cancellationToken = new CancellationToken()) - { - cancellationToken.ThrowIfCancellationRequested(); - ThrowIfDisposed(); - if (user == null) - { - throw new ArgumentNullException("user"); - } - return Task.FromResult(user.UserName); - } - - public Task SetUserNameAsync(TUser user, string userName, CancellationToken cancellationToken = new CancellationToken()) - { - cancellationToken.ThrowIfCancellationRequested(); - ThrowIfDisposed(); - if (user == null) - { - throw new ArgumentNullException("user"); - } - user.UserName = userName; - return Task.FromResult(0); - } - - public async virtual Task CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) - { - cancellationToken.ThrowIfCancellationRequested(); - ThrowIfDisposed(); - if (user == null) - { - throw new ArgumentNullException("user"); - } - await Context.AddAsync(user, cancellationToken); - await SaveChanges(cancellationToken); - } - - public async virtual Task UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) - { - cancellationToken.ThrowIfCancellationRequested(); - ThrowIfDisposed(); - if (user == null) - { - throw new ArgumentNullException("user"); - } - await Context.UpdateAsync(user, cancellationToken); - await SaveChanges(cancellationToken); - } - - public async virtual Task DeleteAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) - { - cancellationToken.ThrowIfCancellationRequested(); - ThrowIfDisposed(); - if (user == null) - { - throw new ArgumentNullException("user"); - } - Context.Delete(user); - await SaveChanges(cancellationToken); - } - - /// - /// Find a user by id - /// - /// - /// - /// - public virtual Task FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken)) - { - cancellationToken.ThrowIfCancellationRequested(); - ThrowIfDisposed(); - return GetUserAggregate(u => u.Id.Equals(userId), cancellationToken); - } - - /// - /// Find a user by name - /// - /// - /// - /// - public virtual Task FindByNameAsync(string userName, CancellationToken cancellationToken = default(CancellationToken)) - { - cancellationToken.ThrowIfCancellationRequested(); - ThrowIfDisposed(); - return GetUserAggregate(u => u.UserName.ToUpper() == userName.ToUpper(), cancellationToken); - } - - public IQueryable Users - { - get { return Context.Set(); } - } - - /// - /// Set the password hash for a user - /// - /// - /// - /// - /// - public virtual Task SetPasswordHashAsync(TUser user, string passwordHash, CancellationToken cancellationToken = default(CancellationToken)) - { - cancellationToken.ThrowIfCancellationRequested(); - ThrowIfDisposed(); - if (user == null) - { - throw new ArgumentNullException("user"); - } - user.PasswordHash = passwordHash; - return Task.FromResult(0); - } - - /// - /// Get the password hash for a user - /// - /// - /// - /// - public virtual Task GetPasswordHashAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) - { - cancellationToken.ThrowIfCancellationRequested(); - ThrowIfDisposed(); - if (user == null) - { - throw new ArgumentNullException("user"); - } - return Task.FromResult(user.PasswordHash); - } - - /// - /// Returns true if the user has a password set - /// - /// - /// - /// - public virtual Task HasPasswordAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) - { - cancellationToken.ThrowIfCancellationRequested(); - return Task.FromResult(user.PasswordHash != null); - } - - ///// - ///// Add a user to a role - ///// - ///// - ///// - ///// - ///// - //public virtual Task AddToRoleAsync(TUser user, string roleName, CancellationToken cancellationToken = default(CancellationToken)) - //{ - // cancellationToken.ThrowIfCancellationRequested(); - // ThrowIfDisposed(); - // if (user == null) - // { - // throw new ArgumentNullException("user"); - // } - // // TODO: - // //if (String.IsNullOrWhiteSpace(roleName)) - // //{ - // // throw new ArgumentException(IdentityResources.ValueCannotBeNullOrEmpty, "roleName"); - // //} - // var roleEntity = Context.Set().SingleOrDefault(r => r.Name.ToUpper() == roleName.ToUpper()); - // if (roleEntity == null) - // { - // throw new InvalidOperationException("Role Not Found"); - // //TODO: String.Format(CultureInfo.CurrentCulture, IdentityResources.RoleNotFound, roleName)); - // } - // var ur = new TUserRole { UserId = user.Id, RoleId = roleEntity.Id }; - // user.Roles.Add(ur); - // roleEntity.Users.Add(ur); - // return Task.FromResult(0); - //} - - ///// - ///// Remove a user from a role - ///// - ///// - ///// - ///// - ///// - //public virtual Task RemoveFromRoleAsync(TUser user, string roleName, CancellationToken cancellationToken = default(CancellationToken)) - //{ - // cancellationToken.ThrowIfCancellationRequested(); - // ThrowIfDisposed(); - // if (user == null) - // { - // throw new ArgumentNullException("user"); - // } - // //if (String.IsNullOrWhiteSpace(roleName)) - // //{ - // // throw new ArgumentException(IdentityResources.ValueCannotBeNullOrEmpty, "roleName"); - // //} - // var roleEntity = Context.Set().SingleOrDefault(r => r.Name.ToUpper() == roleName.ToUpper()); - // if (roleEntity != null) - // { - // var userRole = user.Roles.FirstOrDefault(r => roleEntity.Id.Equals(r.RoleId)); - // if (userRole != null) - // { - // user.Roles.Remove(userRole); - // roleEntity.Users.Remove(userRole); - // } - // } - // return Task.FromResult(0); - //} - - ///// - ///// Get the names of the roles a user is a member of - ///// - ///// - ///// - ///// - //public virtual Task> GetRolesAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) - //{ - // cancellationToken.ThrowIfCancellationRequested(); - // ThrowIfDisposed(); - // if (user == null) - // { - // throw new ArgumentNullException("user"); - // } - // var query = from userRoles in user.Roles - // join roles in Context.Set() - // on userRoles.RoleId equals roles.Id - // select roles.Name; - // return Task.FromResult>(query.ToList()); - //} - - ///// - ///// Returns true if the user is in the named role - ///// - ///// - ///// - ///// - ///// - //public virtual Task IsInRoleAsync(TUser user, string roleName, CancellationToken cancellationToken = default(CancellationToken)) - //{ - // cancellationToken.ThrowIfCancellationRequested(); - // ThrowIfDisposed(); - // if (user == null) - // { - // throw new ArgumentNullException("user"); - // } - // //if (String.IsNullOrWhiteSpace(roleName)) - // //{ - // // throw new ArgumentException(IdentityResources.ValueCannotBeNullOrEmpty, "roleName"); - // //} - // var any = - // Context.Set().Where(r => r.Name.ToUpper() == roleName.ToUpper()) - // .Where(r => r.Users.Any(ur => ur.UserId.Equals(user.Id))) - // .Count() > 0; - // return Task.FromResult(any); - //} - private void ThrowIfDisposed() - { - if (_disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - } - - /// - /// Dispose the store - /// - public void Dispose() - { - _disposed = true; - } - - private DbSet UserClaims { get { return Context.Set(); } } - - public Task> GetClaimsAsync(TUser user, CancellationToken cancellationToken = new CancellationToken()) - { - ThrowIfDisposed(); - if (user == null) - { - throw new ArgumentNullException("user"); - } - IList result = UserClaims.Where(uc => uc.UserId == user.Id).Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToList(); - return Task.FromResult(result); - } - - public Task AddClaimAsync(TUser user, Claim claim, CancellationToken cancellationToken = new CancellationToken()) - { - ThrowIfDisposed(); - if (user == null) - { - throw new ArgumentNullException("user"); - } - if (claim == null) - { - throw new ArgumentNullException("claim"); - } - UserClaims.Add(new IdentityUserClaim { UserId = user.Id, ClaimType = claim.Type, ClaimValue = claim.Value }); - return Task.FromResult(0); - } - - public Task RemoveClaimAsync(TUser user, Claim claim, CancellationToken cancellationToken = new CancellationToken()) - { - ThrowIfDisposed(); - if (user == null) - { - throw new ArgumentNullException("user"); - } - if (claim == null) - { - throw new ArgumentNullException("claim"); - } - var claims = UserClaims.Where(uc => uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToList(); - foreach (var c in claims) - { - UserClaims.Remove(c); - } - return Task.FromResult(0); - } - } -} diff --git a/src/Microsoft.AspNet.Identity.EntityFramework/User.cs b/src/Microsoft.AspNet.Identity.EntityFramework/User.cs index e0e44f30fd..b653ed9da9 100644 --- a/src/Microsoft.AspNet.Identity.EntityFramework/User.cs +++ b/src/Microsoft.AspNet.Identity.EntityFramework/User.cs @@ -2,14 +2,18 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; -namespace Microsoft.AspNet.Identity.Entity +namespace Microsoft.AspNet.Identity.EntityFramework { public class User { public User() { Id = Guid.NewGuid().ToString(); + Claims = new List(); + Roles = new List(); + Logins = new List(); } public User(string userName) : this() @@ -20,10 +24,71 @@ namespace Microsoft.AspNet.Identity.Entity public virtual string Id { get; set; } public virtual string UserName { get; set; } + /// + /// Email + /// + public virtual string Email { get; set; } + + /// + /// True if the email is confirmed, default is false + /// + public virtual bool EmailConfirmed { get; set; } + /// /// The salted/hashed form of the user password /// public virtual string PasswordHash { get; set; } + + /// + /// A random value that should change whenever a users credentials have changed (password changed, login removed) + /// + public virtual string SecurityStamp { get; set; } + + /// + /// PhoneNumber for the user + /// + public virtual string PhoneNumber { get; set; } + + /// + /// True if the phone number is confirmed, default is false + /// + public virtual bool PhoneNumberConfirmed { get; set; } + + /// + /// Is two factor enabled for the user + /// + public virtual bool TwoFactorEnabled { get; set; } + + /// + /// DateTime in UTC when lockout ends, any time in the past is considered not locked out. + /// + public virtual DateTime? LockoutEnd { get; set; } + + /// + /// Is lockout enabled for this user + /// + public virtual bool LockoutEnabled { get; set; } + + /// + /// Used to record failures for the purposes of lockout + /// + public virtual int AccessFailedCount { get; set; } + + /// + /// Roles for the user + /// + public virtual ICollection Roles { get; private set; } + + /// + /// Claims for the user + /// + public virtual ICollection Claims { get; private set; } + + /// + /// Associated logins for the user + /// + public virtual ICollection Logins { get; private set; } + } } diff --git a/src/Microsoft.AspNet.Identity.EntityFramework/UserStore.cs b/src/Microsoft.AspNet.Identity.EntityFramework/UserStore.cs new file mode 100644 index 0000000000..cc50f1dc42 --- /dev/null +++ b/src/Microsoft.AspNet.Identity.EntityFramework/UserStore.cs @@ -0,0 +1,852 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Linq.Expressions; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Data.Entity; + +namespace Microsoft.AspNet.Identity.EntityFramework +{ + public class UserStore : UserStore + { + public UserStore(DbContext context) : base(context) { } + } + + public class UserStore : UserStore where TUser : User + { + public UserStore(DbContext context) : base(context) { } + } + + public class UserStore : + IUserLoginStore, + IUserRoleStore, + IUserClaimStore, + IUserPasswordStore, + IUserSecurityStampStore, + IUserEmailStore, + IUserLockoutStore, + IUserPhoneNumberStore, + IQueryableUserStore, + IUserTwoFactorStore + where TUser : User + where TRole : IdentityRole + where TContext : DbContext + { + private bool _disposed; + + public UserStore(TContext context) + { + if (context == null) + { + throw new ArgumentNullException("context"); + } + Context = context; + AutoSaveChanges = true; + } + + public TContext Context { get; private set; } + + /// + /// If true will call SaveChanges after CreateAsync/UpdateAsync/DeleteAsync + /// + public bool AutoSaveChanges { get; set; } + + private Task SaveChanges(CancellationToken cancellationToken) + { + return AutoSaveChanges ? Context.SaveChangesAsync(cancellationToken) : Task.FromResult(0); + } + + protected virtual Task GetUserAggregate(Expression> filter, CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult(Users.FirstOrDefault(filter)); + // TODO: return Users.FirstOrDefaultAsync(filter, cancellationToken); + //Include(u => u.Roles) + //.Include(u => u.Claims) + //.Include(u => u.Logins) + } + + public Task GetUserIdAsync(TUser user, CancellationToken cancellationToken = new CancellationToken()) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(Convert.ToString(user.Id, CultureInfo.InvariantCulture)); + } + + public Task GetUserNameAsync(TUser user, CancellationToken cancellationToken = new CancellationToken()) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(user.UserName); + } + + public Task SetUserNameAsync(TUser user, string userName, CancellationToken cancellationToken = new CancellationToken()) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.UserName = userName; + return Task.FromResult(0); + } + + public async virtual Task CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + await Context.AddAsync(user, cancellationToken); + await SaveChanges(cancellationToken); + } + + public async virtual Task UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + await Context.UpdateAsync(user, cancellationToken); + await SaveChanges(cancellationToken); + } + + public async virtual Task DeleteAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + Context.Delete(user); + await SaveChanges(cancellationToken); + } + + /// + /// Find a user by id + /// + /// + /// + /// + public virtual Task FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + return GetUserAggregate(u => u.Id.Equals(userId), cancellationToken); + } + + /// + /// Find a user by name + /// + /// + /// + /// + public virtual Task FindByNameAsync(string userName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + return GetUserAggregate(u => u.UserName.ToUpper() == userName.ToUpper(), cancellationToken); + } + + public IQueryable Users + { + get { return Context.Set(); } + } + + /// + /// Set the password hash for a user + /// + /// + /// + /// + /// + public virtual Task SetPasswordHashAsync(TUser user, string passwordHash, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.PasswordHash = passwordHash; + return Task.FromResult(0); + } + + /// + /// Get the password hash for a user + /// + /// + /// + /// + public virtual Task GetPasswordHashAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(user.PasswordHash); + } + + /// + /// Returns true if the user has a password set + /// + /// + /// + /// + public virtual Task HasPasswordAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.FromResult(user.PasswordHash != null); + } + + /// + /// Add a user to a role + /// + /// + /// + /// + /// + public virtual Task AddToRoleAsync(TUser user, string roleName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + if (String.IsNullOrWhiteSpace(roleName)) + { + throw new ArgumentException(Resources.ValueCannotBeNullOrEmpty, "roleName"); + } + var roleEntity = Roles.SingleOrDefault(r => r.Name.ToUpper() == roleName.ToUpper()); + if (roleEntity == null) + { + throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.RoleNotFound, roleName)); + } + var ur = new IdentityUserRole { UserId = user.Id, RoleId = roleEntity.Id }; + // TODO: rely on fixup? + UserRoles.Add(ur); + user.Roles.Add(ur); + roleEntity.Users.Add(ur); + return Task.FromResult(0); + } + + /// + /// Remove a user from a role + /// + /// + /// + /// + /// + public virtual Task RemoveFromRoleAsync(TUser user, string roleName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + if (String.IsNullOrWhiteSpace(roleName)) + { + throw new ArgumentException(Resources.ValueCannotBeNullOrEmpty, "roleName"); + } + var roleEntity = Roles.SingleOrDefault(r => r.Name.ToUpper() == roleName.ToUpper()); + if (roleEntity != null) + { + var userRole = UserRoles.FirstOrDefault(r => roleEntity.Id.Equals(r.RoleId) && r.UserId == user.Id); + if (userRole != null) + { + UserRoles.Remove(userRole); + user.Roles.Remove(userRole); + } + } + return Task.FromResult(0); + } + + /// + /// Get the names of the roles a user is a member of + /// + /// + /// + /// + public virtual async Task> GetRolesAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + var userId = user.Id; +// TODO: var query = from userRole in UserRoles + var query = from userRole in user.Roles + join role in Roles on userRole.RoleId equals role.Id + select role.Name; + //return await query.ToListAsync(); + return query.ToList(); + } + + /// + /// Returns true if the user is in the named role + /// + /// + /// + /// + /// + public virtual async Task IsInRoleAsync(TUser user, string roleName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + if (String.IsNullOrWhiteSpace(roleName)) + { + throw new ArgumentException(Resources.ValueCannotBeNullOrEmpty, "roleName"); + } + //var role = await Roles.SingleOrDefaultAsync(r => r.Name.ToUpper() == roleName.ToUpper()); + var role = Roles.SingleOrDefault(r => r.Name.ToUpper() == roleName.ToUpper()); + if (role != null) + { + var userId = user.Id; + var roleId = role.Id; + return user.Roles.Any(ur => ur.RoleId.Equals(roleId)); + //return await UserRoles.AnyAsync(ur => ur.RoleId.Equals(roleId) && ur.UserId.Equals(userId)); + //return UserRoles.Any(ur => ur.RoleId.Equals(roleId) && ur.UserId.Equals(userId)); + } + return false; + } + + private void ThrowIfDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(GetType().Name); + } + } + + /// + /// Dispose the store + /// + public void Dispose() + { + _disposed = true; + } + + private DbSet Roles { get { return Context.Set(); } } + private DbSet UserClaims { get { return Context.Set(); } } + private DbSet UserRoles { get { return Context.Set(); } } + private DbSet UserLogins { get { return Context.Set(); } } + + public Task> GetClaimsAsync(TUser user, CancellationToken cancellationToken = new CancellationToken()) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + IList result = UserClaims.Where(uc => uc.UserId == user.Id).Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToList(); + return Task.FromResult(result); + } + + public Task AddClaimAsync(TUser user, Claim claim, CancellationToken cancellationToken = new CancellationToken()) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + if (claim == null) + { + throw new ArgumentNullException("claim"); + } + UserClaims.Add(new IdentityUserClaim { UserId = user.Id, ClaimType = claim.Type, ClaimValue = claim.Value }); + return Task.FromResult(0); + } + + public Task RemoveClaimAsync(TUser user, Claim claim, CancellationToken cancellationToken = new CancellationToken()) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + if (claim == null) + { + throw new ArgumentNullException("claim"); + } + var claims = UserClaims.Where(uc => uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToList(); + foreach (var c in claims) + { + UserClaims.Remove(c); + } + return Task.FromResult(0); + } + + public async virtual Task AddLoginAsync(TUser user, UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + if (login == null) + { + throw new ArgumentNullException("login"); + } + var l = new IdentityUserLogin + { + UserId = user.Id, + ProviderKey = login.ProviderKey, + LoginProvider = login.LoginProvider + }; + // TODO: fixup so we don't have to update both + UserLogins.Add(l); + user.Logins.Add(l); + } + + public virtual Task RemoveLoginAsync(TUser user, UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + if (login == null) + { + throw new ArgumentNullException("login"); + } + var provider = login.LoginProvider; + var key = login.ProviderKey; + var userId = user.Id; + // todo: ensure logins loaded + var entry = UserLogins.SingleOrDefault(l => l.UserId == userId && l.LoginProvider == provider && l.ProviderKey == key); + if (entry != null) + { + UserLogins.Remove(entry); + user.Logins.Remove(entry); + } + return Task.FromResult(0); + } + + public virtual Task> GetLoginsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + // todo: ensure logins loaded + IList result = + user.Logins.Select(l => new UserLoginInfo(l.LoginProvider, l.ProviderKey)).ToList(); + return Task.FromResult(result); + } + + public async virtual Task FindByLoginAsync(UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (login == null) + { + throw new ArgumentNullException("login"); + } + // todo: ensure logins loaded + var provider = login.LoginProvider; + var key = login.ProviderKey; + // TODO: use FirstOrDefaultAsync + var userLogin = + UserLogins.FirstOrDefault(l => l.LoginProvider == provider && l.ProviderKey == key); + if (userLogin != null) + { + return await GetUserAggregate(u => u.Id.Equals(userLogin.UserId), cancellationToken); + } + return null; + } + + /// + /// Returns whether the user email is confirmed + /// + /// + /// + /// + public virtual Task GetEmailConfirmedAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(user.EmailConfirmed); + } + + /// + /// Set IsConfirmed on the user + /// + /// + /// + /// + /// + public virtual Task SetEmailConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.EmailConfirmed = confirmed; + return Task.FromResult(0); + } + + /// + /// Set the user email + /// + /// + /// + /// + /// + public virtual Task SetEmailAsync(TUser user, string email, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.Email = email; + return Task.FromResult(0); + } + + /// + /// Get the user's email + /// + /// + /// + /// + public virtual Task GetEmailAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(user.Email); + } + + /// + /// Find an user by email + /// + /// + /// + /// + public virtual Task FindByEmailAsync(string email, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + return GetUserAggregate(u => u.Email == email, cancellationToken); + // todo: ToUpper blows up with Null Ref + //return GetUserAggregate(u => u.Email.ToUpper() == email.ToUpper(), cancellationToken); + } + + /// + /// Returns the DateTimeOffset that represents the end of a user's lockout, any time in the past should be considered + /// not locked out. + /// + /// + /// + /// + public virtual Task GetLockoutEndDateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return + Task.FromResult(user.LockoutEnd.HasValue + ? new DateTimeOffset(DateTime.SpecifyKind(user.LockoutEnd.Value, DateTimeKind.Utc)) + : new DateTimeOffset()); + } + + /// + /// Locks a user out until the specified end date (set to a past date, to unlock a user) + /// + /// + /// + /// + /// + public virtual Task SetLockoutEndDateAsync(TUser user, DateTimeOffset lockoutEnd, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.LockoutEnd = lockoutEnd == DateTimeOffset.MinValue ? (DateTime?)null : lockoutEnd.UtcDateTime; + return Task.FromResult(0); + } + + /// + /// Used to record when an attempt to access the user has failed + /// + /// + /// + /// + public virtual Task IncrementAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.AccessFailedCount++; + return Task.FromResult(user.AccessFailedCount); + } + + /// + /// Used to reset the account access count, typically after the account is successfully accessed + /// + /// + /// + /// + public virtual Task ResetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.AccessFailedCount = 0; + return Task.FromResult(0); + } + + /// + /// Returns the current number of failed access attempts. This number usually will be reset whenever the password is + /// verified or the account is locked out. + /// + /// + /// + /// + public virtual Task GetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(user.AccessFailedCount); + } + + /// + /// Returns whether the user can be locked out. + /// + /// + /// + /// + public virtual Task GetLockoutEnabledAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(user.LockoutEnabled); + } + + /// + /// Sets whether the user can be locked out. + /// + /// + /// + /// + /// + public virtual Task SetLockoutEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.LockoutEnabled = enabled; + return Task.FromResult(0); + } + + /// + /// Set the user's phone number + /// + /// + /// + /// + /// + public virtual Task SetPhoneNumberAsync(TUser user, string phoneNumber, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.PhoneNumber = phoneNumber; + return Task.FromResult(0); + } + + /// + /// Get a user's phone number + /// + /// + /// + /// + public virtual Task GetPhoneNumberAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(user.PhoneNumber); + } + + /// + /// Returns whether the user phoneNumber is confirmed + /// + /// + /// + /// + public virtual Task GetPhoneNumberConfirmedAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(user.PhoneNumberConfirmed); + } + + /// + /// Set PhoneNumberConfirmed on the user + /// + /// + /// + /// + /// + public virtual Task SetPhoneNumberConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.PhoneNumberConfirmed = confirmed; + return Task.FromResult(0); + } + + /// + /// Set the security stamp for the user + /// + /// + /// + /// + /// + public virtual Task SetSecurityStampAsync(TUser user, string stamp, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.SecurityStamp = stamp; + return Task.FromResult(0); + } + + /// + /// Get the security stamp for a user + /// + /// + /// + /// + public virtual Task GetSecurityStampAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(user.SecurityStamp); + } + + /// + /// Set whether two factor authentication is enabled for the user + /// + /// + /// + /// + /// + public virtual Task SetTwoFactorEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.TwoFactorEnabled = enabled; + return Task.FromResult(0); + } + + /// + /// Gets whether two factor authentication is enabled for the user + /// + /// + /// + /// + public virtual Task GetTwoFactorEnabledAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(user.TwoFactorEnabled); + } + } +} diff --git a/src/Microsoft.AspNet.Identity.EntityFramework/project.json b/src/Microsoft.AspNet.Identity.EntityFramework/project.json index f89490ee1c..52bb017611 100644 --- a/src/Microsoft.AspNet.Identity.EntityFramework/project.json +++ b/src/Microsoft.AspNet.Identity.EntityFramework/project.json @@ -2,7 +2,6 @@ "version": "3.0.0-*", "dependencies": { "Microsoft.AspNet.Identity": "", - "EntityFramework.InMemory": "7.0.0-*", "EntityFramework.SqlServer": "7.0.0-*", "System.Security.Claims" : "1.0.0-*" }, diff --git a/src/Microsoft.AspNet.Identity.InMemory/project.json b/src/Microsoft.AspNet.Identity.InMemory/project.json deleted file mode 100644 index 83563db3fb..0000000000 --- a/src/Microsoft.AspNet.Identity.InMemory/project.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": "3.0.0-*", - "dependencies": { - "Microsoft.AspNet.Identity": "", - "Microsoft.Framework.DependencyInjection" : "1.0.0-*", - "System.Security.Claims": "1.0.0-*" - }, - "configurations": { - "net45": {}, - "k10": { - "dependencies": { - "System.Collections": "4.0.0.0", - "System.Linq": "4.0.0.0", - "System.Linq.Expressions": "4.0.0.0", - "System.Linq.Queryable": "4.0.0.0", - "System.Reflection": "4.0.10.0", - "System.Runtime": "4.0.20.0", - "System.Threading.Tasks": "4.0.10.0" - } - } - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/ClaimTypeOptions.cs b/src/Microsoft.AspNet.Identity/ClaimTypeOptions.cs index 0a584a369a..a86a4ec295 100644 --- a/src/Microsoft.AspNet.Identity/ClaimTypeOptions.cs +++ b/src/Microsoft.AspNet.Identity/ClaimTypeOptions.cs @@ -1,10 +1,7 @@ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.ComponentModel; using System.Security.Claims; -using Microsoft.Framework.ConfigurationModel; namespace Microsoft.AspNet.Identity { diff --git a/src/Microsoft.AspNet.Identity/ClaimsIdentityFactory.cs b/src/Microsoft.AspNet.Identity/ClaimsIdentityFactory.cs index 3660dfb7fe..1d3deac288 100644 --- a/src/Microsoft.AspNet.Identity/ClaimsIdentityFactory.cs +++ b/src/Microsoft.AspNet.Identity/ClaimsIdentityFactory.cs @@ -5,7 +5,6 @@ using System; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; -using Microsoft.Framework.DependencyInjection; namespace Microsoft.AspNet.Identity { @@ -13,9 +12,27 @@ namespace Microsoft.AspNet.Identity /// Creates a ClaimsIdentity from a User /// /// - public class ClaimsIdentityFactory : IClaimsIdentityFactory + public class ClaimsIdentityFactory : IClaimsIdentityFactory where TUser : class + where TRole : class { + public ClaimsIdentityFactory(UserManager userManager, RoleManager roleManager) + { + if (userManager == null) + { + throw new ArgumentNullException("userManager"); + } + if (roleManager == null) + { + throw new ArgumentNullException("roleManager"); + } + UserManager = userManager; + RoleManager = roleManager; + } + + public UserManager UserManager { get; private set; } + public RoleManager RoleManager { get; private set; } + /// /// CreateAsync a ClaimsIdentity from a user /// @@ -24,37 +41,43 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task CreateAsync(UserManager manager, TUser user, + public virtual async Task CreateAsync(TUser user, string authenticationType, CancellationToken cancellationToken = default(CancellationToken)) { - if (manager == null) - { - throw new ArgumentNullException("manager"); - } if (user == null) { throw new ArgumentNullException("user"); } - var userId = await manager.GetUserIdAsync(user, cancellationToken); - var userName = await manager.GetUserNameAsync(user, cancellationToken); - var id = new ClaimsIdentity(authenticationType, manager.Options.ClaimType.UserName, manager.Options.ClaimType.Role); - id.AddClaim(new Claim(manager.Options.ClaimType.UserId, userId)); - id.AddClaim(new Claim(manager.Options.ClaimType.UserName, userName, ClaimValueTypes.String)); - if (manager.SupportsUserSecurityStamp) + var userId = await UserManager.GetUserIdAsync(user, cancellationToken); + var userName = await UserManager.GetUserNameAsync(user, cancellationToken); + var id = new ClaimsIdentity(authenticationType, UserManager.Options.ClaimType.UserName, + UserManager.Options.ClaimType.Role); + id.AddClaim(new Claim(UserManager.Options.ClaimType.UserId, userId)); + id.AddClaim(new Claim(UserManager.Options.ClaimType.UserName, userName, ClaimValueTypes.String)); + if (UserManager.SupportsUserSecurityStamp) { - id.AddClaim(new Claim(manager.Options.ClaimType.SecurityStamp, await manager.GetSecurityStampAsync(user, cancellationToken))); + id.AddClaim(new Claim(UserManager.Options.ClaimType.SecurityStamp, + await UserManager.GetSecurityStampAsync(user, cancellationToken))); } - if (manager.SupportsUserRole) + if (UserManager.SupportsUserRole) { - var roles = await manager.GetRolesAsync(user, cancellationToken); + var roles = await UserManager.GetRolesAsync(user, cancellationToken); foreach (var roleName in roles) { - id.AddClaim(new Claim(manager.Options.ClaimType.Role, roleName, ClaimValueTypes.String)); + id.AddClaim(new Claim(UserManager.Options.ClaimType.Role, roleName, ClaimValueTypes.String)); + if (RoleManager.SupportsRoleClaims) + { + var role = await RoleManager.FindByNameAsync(roleName); + if (role != null) + { + id.AddClaims(await RoleManager.GetClaimsAsync(role, cancellationToken)); + } + } } } - if (manager.SupportsUserClaim) + if (UserManager.SupportsUserClaim) { - id.AddClaims(await manager.GetClaimsAsync(user, cancellationToken)); + id.AddClaims(await UserManager.GetClaimsAsync(user, cancellationToken)); } return id; } diff --git a/src/Microsoft.AspNet.Identity/Crypto.cs b/src/Microsoft.AspNet.Identity/Crypto.cs index 0e8421378d..57913e08ae 100644 --- a/src/Microsoft.AspNet.Identity/Crypto.cs +++ b/src/Microsoft.AspNet.Identity/Crypto.cs @@ -10,8 +10,8 @@ namespace Microsoft.AspNet.Identity internal static class Crypto { private const int Pbkdf2IterCount = 1000; // default for Rfc2898DeriveBytes - private const int Pbkdf2SubkeyLength = 256/8; // 256 bits - private const int SaltSize = 128/8; // 128 bits + private const int Pbkdf2SubkeyLength = 256 / 8; // 256 bits + private const int SaltSize = 128 / 8; // 128 bits /* ======================= * HASHED PASSWORD FORMATS diff --git a/src/Microsoft.AspNet.Identity/DefaultAuthenticationTypes.cs b/src/Microsoft.AspNet.Identity/DefaultAuthenticationTypes.cs index 83a8ad8ae5..384bf8794f 100644 --- a/src/Microsoft.AspNet.Identity/DefaultAuthenticationTypes.cs +++ b/src/Microsoft.AspNet.Identity/DefaultAuthenticationTypes.cs @@ -5,6 +5,6 @@ namespace Microsoft.AspNet.Identity { public static class DefaultAuthenticationTypes { - public static readonly string ApplicationCookie = typeof(DefaultAuthenticationTypes).Namespace+".Application"; + public static readonly string ApplicationCookie = typeof(DefaultAuthenticationTypes).Namespace + ".Application"; } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IClaimsIdentityFactory.cs b/src/Microsoft.AspNet.Identity/IClaimsIdentityFactory.cs index 25ee8e325f..6d87f05a42 100644 --- a/src/Microsoft.AspNet.Identity/IClaimsIdentityFactory.cs +++ b/src/Microsoft.AspNet.Identity/IClaimsIdentityFactory.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; @@ -16,13 +15,13 @@ namespace Microsoft.AspNet.Identity where TUser : class { /// - /// Create a ClaimsIdentity from an user using a UserManager + /// Create a ClaimsIdentity from an user /// - /// /// /// /// /// - Task CreateAsync(UserManager manager, TUser user, string authenticationType, CancellationToken cancellationToken = default(CancellationToken)); + Task CreateAsync(TUser user, string authenticationType, + CancellationToken cancellationToken = default(CancellationToken)); } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IPasswordValidator.cs b/src/Microsoft.AspNet.Identity/IPasswordValidator.cs index aba033e691..50669f4c18 100644 --- a/src/Microsoft.AspNet.Identity/IPasswordValidator.cs +++ b/src/Microsoft.AspNet.Identity/IPasswordValidator.cs @@ -15,6 +15,7 @@ namespace Microsoft.AspNet.Identity /// Validate the item /// /// - Task ValidateAsync(string password, UserManager manager, CancellationToken cancellationToken = default(CancellationToken)); + Task ValidateAsync(string password, UserManager manager, + CancellationToken cancellationToken = default(CancellationToken)); } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IRoleClaimStore.cs b/src/Microsoft.AspNet.Identity/IRoleClaimStore.cs new file mode 100644 index 0000000000..b11120c58a --- /dev/null +++ b/src/Microsoft.AspNet.Identity/IRoleClaimStore.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Identity +{ + /// + /// Stores role specific claims + /// + /// + public interface IRoleClaimStore : IRoleStore where TRole : class + { + /// + /// Returns the claims for the role + /// + /// + /// + /// + Task> GetClaimsAsync(TRole role, + CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Add a new role claim + /// + /// + /// + /// + /// + Task AddClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Remove a role claim + /// + /// + /// + /// + /// + Task RemoveClaimAsync(TRole role, Claim claim, + CancellationToken cancellationToken = default(CancellationToken)); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IRoleStore.cs b/src/Microsoft.AspNet.Identity/IRoleStore.cs index eccddfcd33..4c4fc16f46 100644 --- a/src/Microsoft.AspNet.Identity/IRoleStore.cs +++ b/src/Microsoft.AspNet.Identity/IRoleStore.cs @@ -60,7 +60,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken = default(CancellationToken)); + Task SetRoleNameAsync(TRole role, string roleName, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Finds a role by id diff --git a/src/Microsoft.AspNet.Identity/IRoleValidator.cs b/src/Microsoft.AspNet.Identity/IRoleValidator.cs index 0d535d4d53..7057c69e69 100644 --- a/src/Microsoft.AspNet.Identity/IRoleValidator.cs +++ b/src/Microsoft.AspNet.Identity/IRoleValidator.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; using System.Threading; using System.Threading.Tasks; @@ -11,7 +10,6 @@ namespace Microsoft.AspNet.Identity /// Used to validate a role /// /// - /// public interface IRoleValidator where TRole : class { /// @@ -21,6 +19,7 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task ValidateAsync(RoleManager manager, TRole role, CancellationToken cancellationToken = default(CancellationToken)); + Task ValidateAsync(RoleManager manager, TRole role, + CancellationToken cancellationToken = default(CancellationToken)); } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IUserClaimStore.cs b/src/Microsoft.AspNet.Identity/IUserClaimStore.cs index 34c17ce64e..1a50c112bd 100644 --- a/src/Microsoft.AspNet.Identity/IUserClaimStore.cs +++ b/src/Microsoft.AspNet.Identity/IUserClaimStore.cs @@ -15,12 +15,13 @@ namespace Microsoft.AspNet.Identity public interface IUserClaimStore : IUserStore where TUser : class { /// - /// Returns the claims for the user with the issuer set + /// Returns the claims for the user /// /// /// /// - Task> GetClaimsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)); + Task> GetClaimsAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Add a new user claim @@ -38,6 +39,7 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task RemoveClaimAsync(TUser user, Claim claim, CancellationToken cancellationToken = default(CancellationToken)); + Task RemoveClaimAsync(TUser user, Claim claim, + CancellationToken cancellationToken = default(CancellationToken)); } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IUserEmailStore.cs b/src/Microsoft.AspNet.Identity/IUserEmailStore.cs index 9a8e1c45f8..6569548ffe 100644 --- a/src/Microsoft.AspNet.Identity/IUserEmailStore.cs +++ b/src/Microsoft.AspNet.Identity/IUserEmailStore.cs @@ -35,7 +35,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task GetEmailConfirmedAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)); + Task GetEmailConfirmedAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Sets whether the user email is confirmed @@ -44,7 +45,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task SetEmailConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken)); + Task SetEmailConfirmedAsync(TUser user, bool confirmed, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Returns the user associated with this email diff --git a/src/Microsoft.AspNet.Identity/IUserLockoutStore.cs b/src/Microsoft.AspNet.Identity/IUserLockoutStore.cs index ef1e59980f..9f4d17f30b 100644 --- a/src/Microsoft.AspNet.Identity/IUserLockoutStore.cs +++ b/src/Microsoft.AspNet.Identity/IUserLockoutStore.cs @@ -8,19 +8,20 @@ using System.Threading.Tasks; namespace Microsoft.AspNet.Identity { /// - /// Stores information which can be used to implement account lockout, including access failures and lockout status + /// Stores information which can be used to implement account lockout, including access failures and lockout status /// /// public interface IUserLockoutStore : IUserStore where TUser : class { /// - /// Returns the DateTimeOffset that represents the end of a user's lockout, any time in the past should be considered - /// not locked out. + /// Returns the DateTimeOffset that represents the end of a user's lockout, any time in the past should be + /// considered not locked out. /// /// /// /// - Task GetLockoutEndDateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)); + Task GetLockoutEndDateAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Locks a user out until the specified end date (set to a past date, to unlock a user) @@ -29,7 +30,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task SetLockoutEndDateAsync(TUser user, DateTimeOffset lockoutEnd, CancellationToken cancellationToken = default(CancellationToken)); + Task SetLockoutEndDateAsync(TUser user, DateTimeOffset lockoutEnd, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Used to record when an attempt to access the user has failed @@ -37,7 +39,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task IncrementAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)); + Task IncrementAccessFailedCountAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Used to reset the account access count, typically after the account is successfully accessed @@ -48,13 +51,14 @@ namespace Microsoft.AspNet.Identity Task ResetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)); /// - /// Returns the current number of failed access attempts. This number usually will be reset whenever the password is - /// verified or the account is locked out. + /// Returns the current number of failed access attempts. This number usually will be reset whenever the + /// password is verified or the account is locked out. /// /// /// /// - Task GetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)); + Task GetAccessFailedCountAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Returns whether the user can be locked out. @@ -62,7 +66,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task GetLockoutEnabledAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)); + Task GetLockoutEnabledAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Sets whether the user can be locked out. @@ -71,6 +76,7 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task SetLockoutEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken)); + Task SetLockoutEnabledAsync(TUser user, bool enabled, + CancellationToken cancellationToken = default(CancellationToken)); } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IUserLoginStore.cs b/src/Microsoft.AspNet.Identity/IUserLoginStore.cs index 1f09255921..7b2f78acc0 100644 --- a/src/Microsoft.AspNet.Identity/IUserLoginStore.cs +++ b/src/Microsoft.AspNet.Identity/IUserLoginStore.cs @@ -20,7 +20,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task AddLoginAsync(TUser user, UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken)); + Task AddLoginAsync(TUser user, UserLoginInfo login, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Removes the user login with the specified combination if it exists, returns true if found and removed @@ -29,7 +30,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task RemoveLoginAsync(TUser user, UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken)); + Task RemoveLoginAsync(TUser user, UserLoginInfo login, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Returns the linked accounts for this user @@ -37,7 +39,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task> GetLoginsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)); + Task> GetLoginsAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Returns the user associated with this login @@ -45,6 +48,7 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task FindByLoginAsync(UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken)); + Task FindByLoginAsync(UserLoginInfo login, + CancellationToken cancellationToken = default(CancellationToken)); } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IUserPasswordStore.cs b/src/Microsoft.AspNet.Identity/IUserPasswordStore.cs index 22763e1e12..8873e4af49 100644 --- a/src/Microsoft.AspNet.Identity/IUserPasswordStore.cs +++ b/src/Microsoft.AspNet.Identity/IUserPasswordStore.cs @@ -19,7 +19,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task SetPasswordHashAsync(TUser user, string passwordHash, CancellationToken cancellationToken = default(CancellationToken)); + Task SetPasswordHashAsync(TUser user, string passwordHash, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Get the user password hash @@ -27,7 +28,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task GetPasswordHashAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)); + Task GetPasswordHashAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Returns true if a user has a password set diff --git a/src/Microsoft.AspNet.Identity/IUserPhoneNumberStore.cs b/src/Microsoft.AspNet.Identity/IUserPhoneNumberStore.cs index e3a21535f1..a3067c379c 100644 --- a/src/Microsoft.AspNet.Identity/IUserPhoneNumberStore.cs +++ b/src/Microsoft.AspNet.Identity/IUserPhoneNumberStore.cs @@ -19,7 +19,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task SetPhoneNumberAsync(TUser user, string phoneNumber, CancellationToken cancellationToken = default(CancellationToken)); + Task SetPhoneNumberAsync(TUser user, string phoneNumber, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Get the user phoneNumber @@ -35,7 +36,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task GetPhoneNumberConfirmedAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)); + Task GetPhoneNumberConfirmedAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Sets whether the user phone number is confirmed @@ -44,6 +46,7 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task SetPhoneNumberConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken)); + Task SetPhoneNumberConfirmedAsync(TUser user, bool confirmed, + CancellationToken cancellationToken = default(CancellationToken)); } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IUserRoleStore.cs b/src/Microsoft.AspNet.Identity/IUserRoleStore.cs index 44e9373268..e74ca87fe6 100644 --- a/src/Microsoft.AspNet.Identity/IUserRoleStore.cs +++ b/src/Microsoft.AspNet.Identity/IUserRoleStore.cs @@ -20,7 +20,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task AddToRoleAsync(TUser user, string roleName, CancellationToken cancellationToken = default(CancellationToken)); + Task AddToRoleAsync(TUser user, string roleName, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Removes the role for the user @@ -29,7 +30,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task RemoveFromRoleAsync(TUser user, string roleName, CancellationToken cancellationToken = default(CancellationToken)); + Task RemoveFromRoleAsync(TUser user, string roleName, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Returns the roles for this user @@ -37,7 +39,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task> GetRolesAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)); + Task> GetRolesAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Returns true if a user is in a role @@ -46,6 +49,7 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task IsInRoleAsync(TUser user, string roleName, CancellationToken cancellationToken = default(CancellationToken)); + Task IsInRoleAsync(TUser user, string roleName, + CancellationToken cancellationToken = default(CancellationToken)); } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IUserSecurityStampStore.cs b/src/Microsoft.AspNet.Identity/IUserSecurityStampStore.cs index 237fe231bd..fc6f57b0b3 100644 --- a/src/Microsoft.AspNet.Identity/IUserSecurityStampStore.cs +++ b/src/Microsoft.AspNet.Identity/IUserSecurityStampStore.cs @@ -19,7 +19,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task SetSecurityStampAsync(TUser user, string stamp, CancellationToken cancellationToken = default(CancellationToken)); + Task SetSecurityStampAsync(TUser user, string stamp, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Get the user security stamp @@ -27,6 +28,7 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task GetSecurityStampAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)); + Task GetSecurityStampAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)); } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IUserStore.cs b/src/Microsoft.AspNet.Identity/IUserStore.cs index 167d8cbb23..581dc5d246 100644 --- a/src/Microsoft.AspNet.Identity/IUserStore.cs +++ b/src/Microsoft.AspNet.Identity/IUserStore.cs @@ -11,7 +11,6 @@ namespace Microsoft.AspNet.Identity /// Interface that exposes basic user management apis /// /// - /// public interface IUserStore : IDisposable where TUser : class { /// @@ -37,7 +36,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task SetUserNameAsync(TUser user, string userName, CancellationToken cancellationToken = default(CancellationToken)); + Task SetUserNameAsync(TUser user, string userName, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Insert a new user @@ -78,6 +78,5 @@ namespace Microsoft.AspNet.Identity /// /// Task FindByNameAsync(string name, CancellationToken cancellationToken = default(CancellationToken)); - } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IUserTokenProvider.cs b/src/Microsoft.AspNet.Identity/IUserTokenProvider.cs index 2f6f71a649..5494c0062a 100644 --- a/src/Microsoft.AspNet.Identity/IUserTokenProvider.cs +++ b/src/Microsoft.AspNet.Identity/IUserTokenProvider.cs @@ -20,7 +20,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task GenerateAsync(string purpose, UserManager manager, TUser user, CancellationToken cancellationToken = default(CancellationToken)); + Task GenerateAsync(string purpose, UserManager manager, TUser user, + CancellationToken cancellationToken = default(CancellationToken)); /// /// ValidateAsync and unprotect a token, returns null if invalid @@ -31,7 +32,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task ValidateAsync(string purpose, string token, UserManager manager, TUser user, CancellationToken cancellationToken = default(CancellationToken)); + Task ValidateAsync(string purpose, string token, UserManager manager, TUser user, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Notifies the user that a token has been generated, i.e. via email or sms, or can no-op @@ -41,7 +43,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task NotifyAsync(string token, UserManager manager, TUser user, CancellationToken cancellationToken = default(CancellationToken)); + Task NotifyAsync(string token, UserManager manager, TUser user, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Returns true if provider can be used for this user, i.e. could require a user to have an email @@ -50,6 +53,7 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task IsValidProviderForUserAsync(UserManager manager, TUser user, CancellationToken cancellationToken = default(CancellationToken)); + Task IsValidProviderForUserAsync(UserManager manager, TUser user, + CancellationToken cancellationToken = default(CancellationToken)); } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IUserTwoFactorStore.cs b/src/Microsoft.AspNet.Identity/IUserTwoFactorStore.cs index f836a71792..c8b3516602 100644 --- a/src/Microsoft.AspNet.Identity/IUserTwoFactorStore.cs +++ b/src/Microsoft.AspNet.Identity/IUserTwoFactorStore.cs @@ -19,7 +19,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task SetTwoFactorEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken)); + Task SetTwoFactorEnabledAsync(TUser user, bool enabled, + CancellationToken cancellationToken = default(CancellationToken)); /// /// Returns whether two factor is enabled for the user @@ -27,6 +28,7 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task GetTwoFactorEnabledAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)); + Task GetTwoFactorEnabledAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)); } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IUserValidator.cs b/src/Microsoft.AspNet.Identity/IUserValidator.cs index d8cbd053c5..b0d2861839 100644 --- a/src/Microsoft.AspNet.Identity/IUserValidator.cs +++ b/src/Microsoft.AspNet.Identity/IUserValidator.cs @@ -20,6 +20,7 @@ namespace Microsoft.AspNet.Identity /// /// /// - Task ValidateAsync(UserManager manager, TUser user, CancellationToken cancellationToken = default(CancellationToken)); + Task ValidateAsync(UserManager manager, TUser user, + CancellationToken cancellationToken = default(CancellationToken)); } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IdentityBuilder.cs b/src/Microsoft.AspNet.Identity/IdentityBuilder.cs index f74269f5eb..65d9ffd71c 100644 --- a/src/Microsoft.AspNet.Identity/IdentityBuilder.cs +++ b/src/Microsoft.AspNet.Identity/IdentityBuilder.cs @@ -1,9 +1,9 @@ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.OptionsModel; -using System; namespace Microsoft.AspNet.Identity { @@ -44,30 +44,9 @@ namespace Microsoft.AspNet.Identity return AddInstance(func); } - public class OptionsSetup : IOptionsSetup - { - public Action SetupAction { get; private set; } - - public OptionsSetup(Action setupAction) - { - if (setupAction == null) - { - throw new ArgumentNullException("setupAction"); - } - SetupAction = setupAction; - } - - public void Setup(TOptions options) - { - SetupAction(options); - } - - public int Order { get; set; } - } - public IdentityBuilder SetupOptions(Action action, int order) { - Services.AddSetup(new OptionsSetup(action) {Order = order}); + Services.AddSetup(new OptionsSetup(action) { Order = order }); return this; } @@ -88,10 +67,10 @@ namespace Microsoft.AspNet.Identity return this; } - //public IdentityBuilder UseTwoFactorProviders(Func>> func) + //public IdentityBuilder UseTwoFactorProviders(Func>> func) //{ // return Use(func); //} - } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IdentityOptions.cs b/src/Microsoft.AspNet.Identity/IdentityOptions.cs index d5c15c02ae..1a703d0186 100644 --- a/src/Microsoft.AspNet.Identity/IdentityOptions.cs +++ b/src/Microsoft.AspNet.Identity/IdentityOptions.cs @@ -1,17 +1,13 @@ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Reflection; -using Microsoft.Framework.ConfigurationModel; - namespace Microsoft.AspNet.Identity { /// - /// Configuration for lockout + /// Configuration for identity /// public class IdentityOptions { - public IdentityOptions() { ClaimType = new ClaimTypeOptions(); diff --git a/src/Microsoft.AspNet.Identity/IdentityResult.cs b/src/Microsoft.AspNet.Identity/IdentityResult.cs index 8e717c135a..dbe60c4756 100644 --- a/src/Microsoft.AspNet.Identity/IdentityResult.cs +++ b/src/Microsoft.AspNet.Identity/IdentityResult.cs @@ -17,7 +17,7 @@ namespace Microsoft.AspNet.Identity /// Failure constructor that takes error messages /// /// - public IdentityResult(params string[] errors) : this((IEnumerable) errors) + public IdentityResult(params string[] errors) : this((IEnumerable)errors) { } @@ -29,7 +29,7 @@ namespace Microsoft.AspNet.Identity { if (errors == null || !errors.Any()) { - errors = new[] {Resources.DefaultError}; + errors = new[] { Resources.DefaultError }; } Succeeded = false; Errors = errors; diff --git a/src/Microsoft.AspNet.Identity/IdentityRole.cs b/src/Microsoft.AspNet.Identity/IdentityRole.cs index 37f5c2882b..8c20bcd996 100644 --- a/src/Microsoft.AspNet.Identity/IdentityRole.cs +++ b/src/Microsoft.AspNet.Identity/IdentityRole.cs @@ -42,6 +42,7 @@ namespace Microsoft.AspNet.Identity public IdentityRole() { Users = new List>(); + Claims = new List>(); } /// @@ -49,6 +50,11 @@ namespace Microsoft.AspNet.Identity /// public virtual ICollection> Users { get; private set; } + /// + /// Navigation property for claims in the role + /// + public virtual ICollection> Claims { get; private set; } + /// /// Role id /// diff --git a/src/Microsoft.AspNet.Identity/IdentityRoleClaim.cs b/src/Microsoft.AspNet.Identity/IdentityRoleClaim.cs new file mode 100644 index 0000000000..c0186b17a5 --- /dev/null +++ b/src/Microsoft.AspNet.Identity/IdentityRoleClaim.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.AspNet.Identity +{ + public class IdentityRoleClaim : IdentityRoleClaim { } + + /// + /// EntityType that represents one specific role claim + /// + /// + public class IdentityRoleClaim where TKey : IEquatable + { + /// + /// Primary key + /// + public virtual int Id { get; set; } + + /// + /// User Id for the role this claim belongs to + /// + public virtual TKey RoleId { get; set; } + + /// + /// Claim type + /// + public virtual string ClaimType { get; set; } + + /// + /// Claim value + /// + public virtual string ClaimValue { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IdentityServiceCollectionExtensions.cs b/src/Microsoft.AspNet.Identity/IdentityServiceCollectionExtensions.cs index f5d0355aac..ecb064dfe2 100644 --- a/src/Microsoft.AspNet.Identity/IdentityServiceCollectionExtensions.cs +++ b/src/Microsoft.AspNet.Identity/IdentityServiceCollectionExtensions.cs @@ -1,16 +1,16 @@ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.AspNet.Identity; -using Microsoft.Framework.OptionsModel; -using Microsoft.Framework.ConfigurationModel; using System; +using Microsoft.AspNet.Identity; +using Microsoft.Framework.ConfigurationModel; namespace Microsoft.Framework.DependencyInjection { public static class IdentityServiceCollectionExtensions { - public static IdentityBuilder AddIdentity(this ServiceCollection services, IConfiguration identityConfig) + public static IdentityBuilder AddIdentity(this ServiceCollection services, + IConfiguration identityConfig) { services.SetupOptions(identityConfig); return services.AddIdentity(); @@ -21,7 +21,8 @@ namespace Microsoft.Framework.DependencyInjection return services.AddIdentity(); } - public static IdentityBuilder AddIdentity(this ServiceCollection services, IConfiguration identityConfig) + public static IdentityBuilder AddIdentity(this ServiceCollection services, + IConfiguration identityConfig) where TUser : class where TRole : class { @@ -38,25 +39,26 @@ namespace Microsoft.Framework.DependencyInjection services.AddScoped>(); services.AddScoped>(); services.AddScoped>(); + services.AddScoped, ClaimsIdentityFactory>(); return new IdentityBuilder(services); } - public static IdentityBuilder AddIdentity(this ServiceCollection services, Action> actionBuilder) + public static IdentityBuilder AddIdentity(this ServiceCollection services, + Action> actionBuilder) where TUser : class where TRole : class { - services.AddIdentity(); - var builder = new IdentityBuilder(services); + var builder = services.AddIdentity(); actionBuilder(builder); return builder; } - public static IdentityBuilder AddIdentity(this ServiceCollection services, IConfiguration identityConfig, Action> actionBuilder) + public static IdentityBuilder AddIdentity(this ServiceCollection services, + IConfiguration identityConfig, Action> actionBuilder) where TUser : class where TRole : class { - services.AddIdentity(identityConfig); - var builder = new IdentityBuilder(services); + var builder = services.AddIdentity(identityConfig); actionBuilder(builder); return builder; } @@ -67,19 +69,22 @@ namespace Microsoft.Framework.DependencyInjection return services.AddIdentity(); } - public static IdentityBuilder AddIdentity(this ServiceCollection services, IConfiguration identityConfig) + public static IdentityBuilder AddIdentity(this ServiceCollection services, + IConfiguration identityConfig) where TUser : class { return services.AddIdentity(identityConfig); } - public static IdentityBuilder AddIdentity(this ServiceCollection services, Action> actionBuilder) + public static IdentityBuilder AddIdentity(this ServiceCollection services, + Action> actionBuilder) where TUser : class { return services.AddIdentity(actionBuilder); } - public static IdentityBuilder AddIdentity(this ServiceCollection services, IConfiguration identityConfig, Action> actionBuilder) + public static IdentityBuilder AddIdentity(this ServiceCollection services, + IConfiguration identityConfig, Action> actionBuilder) where TUser : class { return services.AddIdentity(identityConfig, actionBuilder); diff --git a/src/Microsoft.AspNet.Identity/IdentityServices.cs b/src/Microsoft.AspNet.Identity/IdentityServices.cs index 33bf6b4dae..6860733550 100644 --- a/src/Microsoft.AspNet.Identity/IdentityServices.cs +++ b/src/Microsoft.AspNet.Identity/IdentityServices.cs @@ -13,21 +13,19 @@ namespace Microsoft.AspNet.Identity /// public class IdentityServices { - public static IEnumerable GetDefaultUserServices() where TUser : class { return GetDefaultUserServices(new Configuration()); } - public static IEnumerable GetDefaultUserServices(IConfiguration configuration) where TUser : class + public static IEnumerable GetDefaultUserServices(IConfiguration configuration) + where TUser : class { var describe = new ServiceDescriber(configuration); - // TODO: review defaults for validators should get picked up from config? yield return describe.Transient, UserValidator>(); yield return describe.Transient, PasswordValidator>(); yield return describe.Transient(); - yield return describe.Transient, ClaimsIdentityFactory>(); // TODO: rationalize email/sms/usertoken services } @@ -37,11 +35,10 @@ namespace Microsoft.AspNet.Identity return GetDefaultRoleServices(new Configuration()); } - public static IEnumerable GetDefaultRoleServices(IConfiguration configuration) where TRole : class + public static IEnumerable GetDefaultRoleServices(IConfiguration configuration) + where TRole : class { var describe = new ServiceDescriber(configuration); - - // TODO: review defaults for validators should get picked up from config? yield return describe.Instance>(new RoleValidator()); } } diff --git a/src/Microsoft.AspNet.Identity/IdentityUser.cs b/src/Microsoft.AspNet.Identity/IdentityUser.cs index f1cbe63977..4951c5e902 100644 --- a/src/Microsoft.AspNet.Identity/IdentityUser.cs +++ b/src/Microsoft.AspNet.Identity/IdentityUser.cs @@ -48,7 +48,7 @@ namespace Microsoft.AspNet.Identity public virtual string PasswordHash { get; set; } /// - /// A random value that should change whenever a users credentials have changed (password changed, login removed) + /// A random value that should change whenever a users credentials change (password changed, login removed) /// public virtual string SecurityStamp { get; set; } @@ -96,6 +96,5 @@ namespace Microsoft.AspNet.Identity /// Associated logins for the user /// public virtual ICollection> Logins { get; private set; } - } } diff --git a/src/Microsoft.AspNet.Identity/IdentityUserClaim.cs b/src/Microsoft.AspNet.Identity/IdentityUserClaim.cs index f3f5ec058b..3dcedc5fa4 100644 --- a/src/Microsoft.AspNet.Identity/IdentityUserClaim.cs +++ b/src/Microsoft.AspNet.Identity/IdentityUserClaim.cs @@ -19,7 +19,7 @@ namespace Microsoft.AspNet.Identity public virtual int Id { get; set; } /// - /// User Id for the user who owns this login + /// User Id for the user who owns this claim /// public virtual TKey UserId { get; set; } diff --git a/src/Microsoft.AspNet.Identity/IdentityUserLogin.cs b/src/Microsoft.AspNet.Identity/IdentityUserLogin.cs index 2860173bb2..8731ef2eff 100644 --- a/src/Microsoft.AspNet.Identity/IdentityUserLogin.cs +++ b/src/Microsoft.AspNet.Identity/IdentityUserLogin.cs @@ -13,13 +13,6 @@ namespace Microsoft.AspNet.Identity /// public class IdentityUserLogin where TKey : IEquatable { - // TODO: Remove - public virtual string Id - { - get; - set; - } - /// /// The login provider for the login (i.e. facebook, google) /// diff --git a/src/Microsoft.AspNet.Identity/IdentityUserRole.cs b/src/Microsoft.AspNet.Identity/IdentityUserRole.cs index dc14f165fa..78feb2e43d 100644 --- a/src/Microsoft.AspNet.Identity/IdentityUserRole.cs +++ b/src/Microsoft.AspNet.Identity/IdentityUserRole.cs @@ -13,13 +13,6 @@ namespace Microsoft.AspNet.Identity /// public class IdentityUserRole where TKey : IEquatable { - // TODO: Remove - public virtual string Id - { - get; - set; - } - /// /// UserId for the user that is in the role /// diff --git a/src/Microsoft.AspNet.Identity/LockoutOptions.cs b/src/Microsoft.AspNet.Identity/LockoutOptions.cs index 839ee03ccf..82ce883a11 100644 --- a/src/Microsoft.AspNet.Identity/LockoutOptions.cs +++ b/src/Microsoft.AspNet.Identity/LockoutOptions.cs @@ -13,30 +13,17 @@ namespace Microsoft.AspNet.Identity EnabledByDefault = false; MaxFailedAccessAttempts = 5; } - public void Copy(LockoutOptions options) - { - if (options == null) - { - return; - } - - DefaultLockoutTimeSpan = options.DefaultLockoutTimeSpan; - EnabledByDefault = options.EnabledByDefault; - MaxFailedAccessAttempts = options.MaxFailedAccessAttempts; - } /// /// If true, will enable user lockout when users are created /// public bool EnabledByDefault { get; set; } - /// /// Number of access attempts allowed for a user before lockout (if enabled) /// public int MaxFailedAccessAttempts { get; set; } - /// /// Default amount of time an user is locked out for after MaxFailedAccessAttempsBeforeLockout is reached /// diff --git a/src/Microsoft.AspNet.Identity/Microsoft.AspNet.Identity.kproj b/src/Microsoft.AspNet.Identity/Microsoft.AspNet.Identity.kproj index 1d4d531945..9d8451cbe3 100644 --- a/src/Microsoft.AspNet.Identity/Microsoft.AspNet.Identity.kproj +++ b/src/Microsoft.AspNet.Identity/Microsoft.AspNet.Identity.kproj @@ -32,6 +32,7 @@ + @@ -43,6 +44,7 @@ + @@ -62,7 +64,7 @@ - + diff --git a/src/Microsoft.AspNet.Identity/PasswordOptions.cs b/src/Microsoft.AspNet.Identity/PasswordOptions.cs index 05f93e3cc5..3498d08831 100644 --- a/src/Microsoft.AspNet.Identity/PasswordOptions.cs +++ b/src/Microsoft.AspNet.Identity/PasswordOptions.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.Framework.ConfigurationModel; - namespace Microsoft.AspNet.Identity { public class PasswordOptions diff --git a/src/Microsoft.AspNet.Identity/PasswordValidator.cs b/src/Microsoft.AspNet.Identity/PasswordValidator.cs index bfcc9bc05d..497da1ed08 100644 --- a/src/Microsoft.AspNet.Identity/PasswordValidator.cs +++ b/src/Microsoft.AspNet.Identity/PasswordValidator.cs @@ -22,7 +22,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual Task ValidateAsync(string password, UserManager manager, CancellationToken cancellationToken = default(CancellationToken)) + public virtual Task ValidateAsync(string password, UserManager manager, + CancellationToken cancellationToken = default(CancellationToken)) { if (password == null) { @@ -36,7 +37,8 @@ namespace Microsoft.AspNet.Identity var options = manager.Options.Password; if (string.IsNullOrWhiteSpace(password) || password.Length < options.RequiredLength) { - errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.PasswordTooShort, options.RequiredLength)); + errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.PasswordTooShort, + options.RequiredLength)); } if (options.RequireNonLetterOrDigit && password.All(IsLetterOrDigit)) { diff --git a/src/Microsoft.AspNet.Identity/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Identity/Properties/Resources.Designer.cs new file mode 100644 index 0000000000..572f49f4f4 --- /dev/null +++ b/src/Microsoft.AspNet.Identity/Properties/Resources.Designer.cs @@ -0,0 +1,590 @@ +// +namespace Microsoft.AspNet.Identity +{ + using System.Globalization; + using System.Reflection; + using System.Resources; + + internal static class Resources + { + private static readonly ResourceManager _resourceManager + = new ResourceManager("Microsoft.AspNet.Identity.Resources", typeof(Resources).GetTypeInfo().Assembly); + + /// + /// An unknown failure has occured. + /// + internal static string DefaultError + { + get { return GetString("DefaultError"); } + } + + /// + /// An unknown failure has occured. + /// + internal static string FormatDefaultError() + { + return GetString("DefaultError"); + } + + /// + /// Email '{0}' is already taken. + /// + internal static string DuplicateEmail + { + get { return GetString("DuplicateEmail"); } + } + + /// + /// Email '{0}' is already taken. + /// + internal static string FormatDuplicateEmail(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("DuplicateEmail"), p0); + } + + /// + /// Name {0} is already taken. + /// + internal static string DuplicateName + { + get { return GetString("DuplicateName"); } + } + + /// + /// Name {0} is already taken. + /// + internal static string FormatDuplicateName(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("DuplicateName"), p0); + } + + /// + /// A user with that external login already exists. + /// + internal static string ExternalLoginExists + { + get { return GetString("ExternalLoginExists"); } + } + + /// + /// A user with that external login already exists. + /// + internal static string FormatExternalLoginExists() + { + return GetString("ExternalLoginExists"); + } + + /// + /// Email '{0}' is invalid. + /// + internal static string InvalidEmail + { + get { return GetString("InvalidEmail"); } + } + + /// + /// Email '{0}' is invalid. + /// + internal static string FormatInvalidEmail(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("InvalidEmail"), p0); + } + + /// + /// Invalid token. + /// + internal static string InvalidToken + { + get { return GetString("InvalidToken"); } + } + + /// + /// Invalid token. + /// + internal static string FormatInvalidToken() + { + return GetString("InvalidToken"); + } + + /// + /// User name {0} is invalid, can only contain letters or digits. + /// + internal static string InvalidUserName + { + get { return GetString("InvalidUserName"); } + } + + /// + /// User name {0} is invalid, can only contain letters or digits. + /// + internal static string FormatInvalidUserName(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("InvalidUserName"), p0); + } + + /// + /// Lockout is not enabled for this user. + /// + internal static string LockoutNotEnabled + { + get { return GetString("LockoutNotEnabled"); } + } + + /// + /// Lockout is not enabled for this user. + /// + internal static string FormatLockoutNotEnabled() + { + return GetString("LockoutNotEnabled"); + } + + /// + /// No IUserTokenProvider is registered. + /// + internal static string NoTokenProvider + { + get { return GetString("NoTokenProvider"); } + } + + /// + /// No IUserTokenProvider is registered. + /// + internal static string FormatNoTokenProvider() + { + return GetString("NoTokenProvider"); + } + + /// + /// No IUserTwoFactorProvider for '{0}' is registered. + /// + internal static string NoTwoFactorProvider + { + get { return GetString("NoTwoFactorProvider"); } + } + + /// + /// No IUserTwoFactorProvider for '{0}' is registered. + /// + internal static string FormatNoTwoFactorProvider(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("NoTwoFactorProvider"), p0); + } + + /// + /// Incorrect password. + /// + internal static string PasswordMismatch + { + get { return GetString("PasswordMismatch"); } + } + + /// + /// Incorrect password. + /// + internal static string FormatPasswordMismatch() + { + return GetString("PasswordMismatch"); + } + + /// + /// Passwords must have at least one digit ('0'-'9'). + /// + internal static string PasswordRequireDigit + { + get { return GetString("PasswordRequireDigit"); } + } + + /// + /// Passwords must have at least one digit ('0'-'9'). + /// + internal static string FormatPasswordRequireDigit() + { + return GetString("PasswordRequireDigit"); + } + + /// + /// Passwords must have at least one lowercase ('a'-'z'). + /// + internal static string PasswordRequireLower + { + get { return GetString("PasswordRequireLower"); } + } + + /// + /// Passwords must have at least one lowercase ('a'-'z'). + /// + internal static string FormatPasswordRequireLower() + { + return GetString("PasswordRequireLower"); + } + + /// + /// Passwords must have at least one non letter or digit character. + /// + internal static string PasswordRequireNonLetterOrDigit + { + get { return GetString("PasswordRequireNonLetterOrDigit"); } + } + + /// + /// Passwords must have at least one non letter or digit character. + /// + internal static string FormatPasswordRequireNonLetterOrDigit() + { + return GetString("PasswordRequireNonLetterOrDigit"); + } + + /// + /// Passwords must have at least one uppercase ('A'-'Z'). + /// + internal static string PasswordRequireUpper + { + get { return GetString("PasswordRequireUpper"); } + } + + /// + /// Passwords must have at least one uppercase ('A'-'Z'). + /// + internal static string FormatPasswordRequireUpper() + { + return GetString("PasswordRequireUpper"); + } + + /// + /// Passwords must be at least {0} characters. + /// + internal static string PasswordTooShort + { + get { return GetString("PasswordTooShort"); } + } + + /// + /// Passwords must be at least {0} characters. + /// + internal static string FormatPasswordTooShort(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("PasswordTooShort"), p0); + } + + /// + /// {0} cannot be null or empty. + /// + internal static string PropertyTooShort + { + get { return GetString("PropertyTooShort"); } + } + + /// + /// {0} cannot be null or empty. + /// + internal static string FormatPropertyTooShort(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("PropertyTooShort"), p0); + } + + /// + /// Role {0} does not exist. + /// + internal static string RoleNotFound + { + get { return GetString("RoleNotFound"); } + } + + /// + /// Role {0} does not exist. + /// + internal static string FormatRoleNotFound(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("RoleNotFound"), p0); + } + + /// + /// Store does not implement IQueryableRoleStore<TRole>. + /// + internal static string StoreNotIQueryableRoleStore + { + get { return GetString("StoreNotIQueryableRoleStore"); } + } + + /// + /// Store does not implement IQueryableRoleStore<TRole>. + /// + internal static string FormatStoreNotIQueryableRoleStore() + { + return GetString("StoreNotIQueryableRoleStore"); + } + + /// + /// Store does not implement IQueryableUserStore<TUser>. + /// + internal static string StoreNotIQueryableUserStore + { + get { return GetString("StoreNotIQueryableUserStore"); } + } + + /// + /// Store does not implement IQueryableUserStore<TUser>. + /// + internal static string FormatStoreNotIQueryableUserStore() + { + return GetString("StoreNotIQueryableUserStore"); + } + + /// + /// Store does not implement IRoleClaimStore<TRole>. + /// + internal static string StoreNotIRoleClaimStore + { + get { return GetString("StoreNotIRoleClaimStore"); } + } + + /// + /// Store does not implement IRoleClaimStore<TRole>. + /// + internal static string FormatStoreNotIRoleClaimStore() + { + return GetString("StoreNotIRoleClaimStore"); + } + + /// + /// Store does not implement IUserClaimStore<TUser>. + /// + internal static string StoreNotIUserClaimStore + { + get { return GetString("StoreNotIUserClaimStore"); } + } + + /// + /// Store does not implement IUserClaimStore<TUser>. + /// + internal static string FormatStoreNotIUserClaimStore() + { + return GetString("StoreNotIUserClaimStore"); + } + + /// + /// Store does not implement IUserConfirmationStore<TUser>. + /// + internal static string StoreNotIUserConfirmationStore + { + get { return GetString("StoreNotIUserConfirmationStore"); } + } + + /// + /// Store does not implement IUserConfirmationStore<TUser>. + /// + internal static string FormatStoreNotIUserConfirmationStore() + { + return GetString("StoreNotIUserConfirmationStore"); + } + + /// + /// Store does not implement IUserEmailStore<TUser>. + /// + internal static string StoreNotIUserEmailStore + { + get { return GetString("StoreNotIUserEmailStore"); } + } + + /// + /// Store does not implement IUserEmailStore<TUser>. + /// + internal static string FormatStoreNotIUserEmailStore() + { + return GetString("StoreNotIUserEmailStore"); + } + + /// + /// Store does not implement IUserLockoutStore<TUser>. + /// + internal static string StoreNotIUserLockoutStore + { + get { return GetString("StoreNotIUserLockoutStore"); } + } + + /// + /// Store does not implement IUserLockoutStore<TUser>. + /// + internal static string FormatStoreNotIUserLockoutStore() + { + return GetString("StoreNotIUserLockoutStore"); + } + + /// + /// Store does not implement IUserLoginStore<TUser>. + /// + internal static string StoreNotIUserLoginStore + { + get { return GetString("StoreNotIUserLoginStore"); } + } + + /// + /// Store does not implement IUserLoginStore<TUser>. + /// + internal static string FormatStoreNotIUserLoginStore() + { + return GetString("StoreNotIUserLoginStore"); + } + + /// + /// Store does not implement IUserPasswordStore<TUser>. + /// + internal static string StoreNotIUserPasswordStore + { + get { return GetString("StoreNotIUserPasswordStore"); } + } + + /// + /// Store does not implement IUserPasswordStore<TUser>. + /// + internal static string FormatStoreNotIUserPasswordStore() + { + return GetString("StoreNotIUserPasswordStore"); + } + + /// + /// Store does not implement IUserPhoneNumberStore<TUser>. + /// + internal static string StoreNotIUserPhoneNumberStore + { + get { return GetString("StoreNotIUserPhoneNumberStore"); } + } + + /// + /// Store does not implement IUserPhoneNumberStore<TUser>. + /// + internal static string FormatStoreNotIUserPhoneNumberStore() + { + return GetString("StoreNotIUserPhoneNumberStore"); + } + + /// + /// Store does not implement IUserRoleStore<TUser>. + /// + internal static string StoreNotIUserRoleStore + { + get { return GetString("StoreNotIUserRoleStore"); } + } + + /// + /// Store does not implement IUserRoleStore<TUser>. + /// + internal static string FormatStoreNotIUserRoleStore() + { + return GetString("StoreNotIUserRoleStore"); + } + + /// + /// Store does not implement IUserSecurityStampStore<TUser>. + /// + internal static string StoreNotIUserSecurityStampStore + { + get { return GetString("StoreNotIUserSecurityStampStore"); } + } + + /// + /// Store does not implement IUserSecurityStampStore<TUser>. + /// + internal static string FormatStoreNotIUserSecurityStampStore() + { + return GetString("StoreNotIUserSecurityStampStore"); + } + + /// + /// Store does not implement IUserTwoFactorStore<TUser>. + /// + internal static string StoreNotIUserTwoFactorStore + { + get { return GetString("StoreNotIUserTwoFactorStore"); } + } + + /// + /// Store does not implement IUserTwoFactorStore<TUser>. + /// + internal static string FormatStoreNotIUserTwoFactorStore() + { + return GetString("StoreNotIUserTwoFactorStore"); + } + + /// + /// User already has a password set. + /// + internal static string UserAlreadyHasPassword + { + get { return GetString("UserAlreadyHasPassword"); } + } + + /// + /// User already has a password set. + /// + internal static string FormatUserAlreadyHasPassword() + { + return GetString("UserAlreadyHasPassword"); + } + + /// + /// User already in role. + /// + internal static string UserAlreadyInRole + { + get { return GetString("UserAlreadyInRole"); } + } + + /// + /// User already in role. + /// + internal static string FormatUserAlreadyInRole() + { + return GetString("UserAlreadyInRole"); + } + + /// + /// User {0} does not exist. + /// + internal static string UserNameNotFound + { + get { return GetString("UserNameNotFound"); } + } + + /// + /// User {0} does not exist. + /// + internal static string FormatUserNameNotFound(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("UserNameNotFound"), p0); + } + + /// + /// User is not in role. + /// + internal static string UserNotInRole + { + get { return GetString("UserNotInRole"); } + } + + /// + /// User is not in role. + /// + internal static string FormatUserNotInRole() + { + return GetString("UserNotInRole"); + } + + private static string GetString(string name, params string[] formatterNames) + { + var value = _resourceManager.GetString(name); + + System.Diagnostics.Debug.Assert(value != null); + + if (formatterNames != null) + { + for (var i = 0; i < formatterNames.Length; i++) + { + value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); + } + } + + return value; + } + } +} diff --git a/src/Microsoft.AspNet.Identity/Resources.Designer.cs b/src/Microsoft.AspNet.Identity/Resources.Designer.cs deleted file mode 100644 index 1a6cdb5338..0000000000 --- a/src/Microsoft.AspNet.Identity/Resources.Designer.cs +++ /dev/null @@ -1,382 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.34011 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Microsoft.AspNet.Identity { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { -#if NET45 - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.AspNet.Identity.Resources", typeof(Resources).Assembly); -#else - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.AspNet.Identity.Resources", System.Reflection.IntrospectionExtensions.GetTypeInfo(typeof(Resources)).Assembly); -#endif - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to An unknown failure has occured.. - /// - internal static string DefaultError { - get { - return ResourceManager.GetString("DefaultError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Email '{0}' is already taken.. - /// - internal static string DuplicateEmail { - get { - return ResourceManager.GetString("DuplicateEmail", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Name {0} is already taken.. - /// - internal static string DuplicateName { - get { - return ResourceManager.GetString("DuplicateName", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to A user with that external login already exists.. - /// - internal static string ExternalLoginExists { - get { - return ResourceManager.GetString("ExternalLoginExists", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Email '{0}' is invalid.. - /// - internal static string InvalidEmail { - get { - return ResourceManager.GetString("InvalidEmail", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Invalid token.. - /// - internal static string InvalidToken { - get { - return ResourceManager.GetString("InvalidToken", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to User name {0} is invalid, can only contain letters or digits.. - /// - internal static string InvalidUserName { - get { - return ResourceManager.GetString("InvalidUserName", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Lockout is not enabled for this user.. - /// - internal static string LockoutNotEnabled { - get { - return ResourceManager.GetString("LockoutNotEnabled", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to No IUserTokenProvider is registered.. - /// - internal static string NoTokenProvider { - get { - return ResourceManager.GetString("NoTokenProvider", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to No IUserTwoFactorProvider for '{0}' is registered.. - /// - internal static string NoTwoFactorProvider { - get { - return ResourceManager.GetString("NoTwoFactorProvider", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Incorrect password.. - /// - internal static string PasswordMismatch { - get { - return ResourceManager.GetString("PasswordMismatch", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Passwords must have at least one digit ('0'-'9').. - /// - internal static string PasswordRequireDigit { - get { - return ResourceManager.GetString("PasswordRequireDigit", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Passwords must have at least one lowercase ('a'-'z').. - /// - internal static string PasswordRequireLower { - get { - return ResourceManager.GetString("PasswordRequireLower", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Passwords must have at least one non letter or digit character.. - /// - internal static string PasswordRequireNonLetterOrDigit { - get { - return ResourceManager.GetString("PasswordRequireNonLetterOrDigit", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Passwords must have at least one uppercase ('A'-'Z').. - /// - internal static string PasswordRequireUpper { - get { - return ResourceManager.GetString("PasswordRequireUpper", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Passwords must be at least {0} characters.. - /// - internal static string PasswordTooShort { - get { - return ResourceManager.GetString("PasswordTooShort", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to {0} cannot be null or empty.. - /// - internal static string PropertyTooShort { - get { - return ResourceManager.GetString("PropertyTooShort", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Role {0} does not exist.. - /// - internal static string RoleNotFound { - get { - return ResourceManager.GetString("RoleNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Store does not implement IQueryableRoleStore<TRole>.. - /// - internal static string StoreNotIQueryableRoleStore { - get { - return ResourceManager.GetString("StoreNotIQueryableRoleStore", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Store does not implement IQueryableUserStore<TUser>.. - /// - internal static string StoreNotIQueryableUserStore { - get { - return ResourceManager.GetString("StoreNotIQueryableUserStore", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Store does not implement IUserClaimStore<TUser>.. - /// - internal static string StoreNotIUserClaimStore { - get { - return ResourceManager.GetString("StoreNotIUserClaimStore", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Store does not implement IUserConfirmationStore<TUser>.. - /// - internal static string StoreNotIUserConfirmationStore { - get { - return ResourceManager.GetString("StoreNotIUserConfirmationStore", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Store does not implement IUserEmailStore<TUser>.. - /// - internal static string StoreNotIUserEmailStore { - get { - return ResourceManager.GetString("StoreNotIUserEmailStore", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Store does not implement IUserLockoutStore<TUser>.. - /// - internal static string StoreNotIUserLockoutStore { - get { - return ResourceManager.GetString("StoreNotIUserLockoutStore", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Store does not implement IUserLoginStore<TUser>.. - /// - internal static string StoreNotIUserLoginStore { - get { - return ResourceManager.GetString("StoreNotIUserLoginStore", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Store does not implement IUserPasswordStore<TUser>.. - /// - internal static string StoreNotIUserPasswordStore { - get { - return ResourceManager.GetString("StoreNotIUserPasswordStore", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Store does not implement IUserPhoneNumberStore<TUser>.. - /// - internal static string StoreNotIUserPhoneNumberStore { - get { - return ResourceManager.GetString("StoreNotIUserPhoneNumberStore", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Store does not implement IUserRoleStore<TUser>.. - /// - internal static string StoreNotIUserRoleStore { - get { - return ResourceManager.GetString("StoreNotIUserRoleStore", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Store does not implement IUserSecurityStampStore<TUser>.. - /// - internal static string StoreNotIUserSecurityStampStore { - get { - return ResourceManager.GetString("StoreNotIUserSecurityStampStore", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Store does not implement IUserTwoFactorStore<TUser>.. - /// - internal static string StoreNotIUserTwoFactorStore { - get { - return ResourceManager.GetString("StoreNotIUserTwoFactorStore", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to User already has a password set.. - /// - internal static string UserAlreadyHasPassword { - get { - return ResourceManager.GetString("UserAlreadyHasPassword", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to User already in role.. - /// - internal static string UserAlreadyInRole { - get { - return ResourceManager.GetString("UserAlreadyInRole", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to UserId not found.. - /// - internal static string UserIdNotFound { - get { - return ResourceManager.GetString("UserIdNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to User {0} does not exist.. - /// - internal static string UserNameNotFound { - get { - return ResourceManager.GetString("UserNameNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to User is not in role.. - /// - internal static string UserNotInRole { - get { - return ResourceManager.GetString("UserNotInRole", resourceCulture); - } - } - } -} diff --git a/src/Microsoft.AspNet.Identity/Resources.resx b/src/Microsoft.AspNet.Identity/Resources.resx index a78553d9dc..5eacf05da9 100644 --- a/src/Microsoft.AspNet.Identity/Resources.resx +++ b/src/Microsoft.AspNet.Identity/Resources.resx @@ -197,6 +197,10 @@ Store does not implement IQueryableUserStore<TUser>. error when the store does not implement this interface + + Store does not implement IRoleClaimStore<TRole>. + error when the store does not implement this interface + Store does not implement IUserClaimStore<TUser>. error when the store does not implement this interface diff --git a/src/Microsoft.AspNet.Identity/RoleManager.cs b/src/Microsoft.AspNet.Identity/RoleManager.cs index 9d0b338294..0533a0fe58 100644 --- a/src/Microsoft.AspNet.Identity/RoleManager.cs +++ b/src/Microsoft.AspNet.Identity/RoleManager.cs @@ -2,11 +2,12 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Linq; +using System.Security.Claims; using System.Threading; using System.Threading.Tasks; using Microsoft.Framework.DependencyInjection; -using Microsoft.Framework.DependencyInjection.Fallback; namespace Microsoft.AspNet.Identity { @@ -21,7 +22,7 @@ namespace Microsoft.AspNet.Identity /// /// Constructor /// - /// The IRoleStore is responsible for commiting changes via the UpdateAsync/CreateAsync methods + /// The IRoleStore commits changes via the UpdateAsync/CreateAsync methods /// public RoleManager(IRoleStore store, IRoleValidator roleValidator) { @@ -75,6 +76,18 @@ namespace Microsoft.AspNet.Identity } } + /// + /// Returns true if the store is an IUserClaimStore + /// + public virtual bool SupportsRoleClaims + { + get + { + ThrowIfDisposed(); + return Store is IRoleClaimStore; + } + } + /// /// Dispose this object /// @@ -86,7 +99,8 @@ namespace Microsoft.AspNet.Identity private async Task ValidateRoleInternal(TRole role, CancellationToken cancellationToken) { - return (RoleValidator == null) ? IdentityResult.Success : await RoleValidator.ValidateAsync(this, role, cancellationToken); + return (RoleValidator == null) ? IdentityResult.Success : + await RoleValidator.ValidateAsync(this, role, cancellationToken); } /// @@ -95,7 +109,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task CreateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task CreateAsync(TRole role, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); if (role == null) @@ -118,7 +133,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task UpdateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task UpdateAsync(TRole role, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); if (role == null) @@ -141,7 +157,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task DeleteAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task DeleteAsync(TRole role, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); if (role == null) @@ -159,7 +176,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task RoleExistsAsync(string roleName, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task RoleExistsAsync(string roleName, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); if (roleName == null) @@ -176,7 +194,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task FindByIdAsync(string roleId, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task FindByIdAsync(string roleId, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); return await Store.FindByIdAsync(roleId, cancellationToken); @@ -188,7 +207,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task GetRoleNameAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task GetRoleNameAsync(TRole role, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); return await Store.GetRoleNameAsync(role, cancellationToken); @@ -201,7 +221,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task SetRoleNameAsync(TRole role, string name, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task SetRoleNameAsync(TRole role, string name, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); await Store.SetRoleNameAsync(role, name, cancellationToken); @@ -214,7 +235,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task GetRoleIdAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task GetRoleIdAsync(TRole role, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); return await Store.GetRoleIdAsync(role, cancellationToken); @@ -226,7 +248,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task FindByNameAsync(string roleName, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task FindByNameAsync(string roleName, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); if (roleName == null) @@ -237,6 +260,79 @@ namespace Microsoft.AspNet.Identity return await Store.FindByNameAsync(roleName, cancellationToken); } + // IRoleClaimStore methods + private IRoleClaimStore GetClaimStore() + { + var cast = Store as IRoleClaimStore; + if (cast == null) + { + throw new NotSupportedException(Resources.StoreNotIRoleClaimStore); + } + return cast; + } + + /// + /// Add a user claim + /// + /// + /// + /// + /// + public virtual async Task AddClaimAsync(TRole role, Claim claim, + CancellationToken cancellationToken = default(CancellationToken)) + { + ThrowIfDisposed(); + var claimStore = GetClaimStore(); + if (claim == null) + { + throw new ArgumentNullException("claim"); + } + if (role == null) + { + throw new ArgumentNullException("role"); + } + await claimStore.AddClaimAsync(role, claim, cancellationToken); + return await UpdateAsync(role, cancellationToken); + } + + /// + /// Remove a user claim + /// + /// + /// + /// + /// + public virtual async Task RemoveClaimAsync(TRole role, Claim claim, + CancellationToken cancellationToken = default(CancellationToken)) + { + ThrowIfDisposed(); + var claimStore = GetClaimStore(); + if (role == null) + { + throw new ArgumentNullException("role"); + } + await claimStore.RemoveClaimAsync(role, claim, cancellationToken); + return await UpdateAsync(role, cancellationToken); + } + + /// + /// Get a role's claims + /// + /// + /// + /// + public virtual async Task> GetClaimsAsync(TRole role, + CancellationToken cancellationToken = default(CancellationToken)) + { + ThrowIfDisposed(); + var claimStore = GetClaimStore(); + if (role == null) + { + throw new ArgumentNullException("role"); + } + return await claimStore.GetClaimsAsync(role, cancellationToken); + } + private void ThrowIfDisposed() { if (_disposed) diff --git a/src/Microsoft.AspNet.Identity/RoleValidator.cs b/src/Microsoft.AspNet.Identity/RoleValidator.cs index 92cbed5ade..244b7136f0 100644 --- a/src/Microsoft.AspNet.Identity/RoleValidator.cs +++ b/src/Microsoft.AspNet.Identity/RoleValidator.cs @@ -22,7 +22,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task ValidateAsync(RoleManager manager, TRole role, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task ValidateAsync(RoleManager manager, TRole role, + CancellationToken cancellationToken = default(CancellationToken)) { if (manager == null) { @@ -52,7 +53,8 @@ namespace Microsoft.AspNet.Identity else { var owner = await manager.FindByNameAsync(roleName); - if (owner != null && !string.Equals(await manager.GetRoleIdAsync(owner), await manager.GetRoleIdAsync(role))) + if (owner != null && + !string.Equals(await manager.GetRoleIdAsync(owner), await manager.GetRoleIdAsync(role))) { errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.DuplicateName, roleName)); } diff --git a/src/Microsoft.AspNet.Identity/SignInManager.cs b/src/Microsoft.AspNet.Identity/SignInManager.cs index d6895d2d0a..61c3122818 100644 --- a/src/Microsoft.AspNet.Identity/SignInManager.cs +++ b/src/Microsoft.AspNet.Identity/SignInManager.cs @@ -13,7 +13,8 @@ namespace Microsoft.AspNet.Identity /// public class SignInManager where TUser : class { - public SignInManager(UserManager userManager, IAuthenticationManager authenticationManager) + public SignInManager(UserManager userManager, IAuthenticationManager authenticationManager, + IClaimsIdentityFactory claimsFactory) { if (userManager == null) { @@ -23,8 +24,13 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("authenticationManager"); } + if (claimsFactory == null) + { + throw new ArgumentNullException("claimsFactory"); + } UserManager = userManager; AuthenticationManager = authenticationManager; + ClaimsFactory = claimsFactory; AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie; } @@ -33,11 +39,12 @@ namespace Microsoft.AspNet.Identity public UserManager UserManager { get; private set; } public IAuthenticationManager AuthenticationManager { get; private set; } + public IClaimsIdentityFactory ClaimsFactory { get; private set; } // Should this be a func? public virtual async Task CreateUserIdentityAsync(TUser user) { - return await UserManager.CreateIdentityAsync(user, AuthenticationType); + return await ClaimsFactory.CreateAsync(user, AuthenticationType); } public virtual async Task SignInAsync(TUser user, bool isPersistent) @@ -52,7 +59,8 @@ namespace Microsoft.AspNet.Identity AuthenticationManager.SignOut(AuthenticationType); } - public virtual async Task PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout) + public virtual async Task PasswordSignInAsync(string userName, string password, + bool isPersistent, bool shouldLockout) { var user = await UserManager.FindByNameAsync(userName); if (user == null) diff --git a/src/Microsoft.AspNet.Identity/UserManager.cs b/src/Microsoft.AspNet.Identity/UserManager.cs index cac492f9cb..23163f10e7 100644 --- a/src/Microsoft.AspNet.Identity/UserManager.cs +++ b/src/Microsoft.AspNet.Identity/UserManager.cs @@ -9,7 +9,6 @@ using System.Security.Claims; using System.Text; using System.Threading; using System.Threading.Tasks; -using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.OptionsModel; namespace Microsoft.AspNet.Identity @@ -23,7 +22,6 @@ namespace Microsoft.AspNet.Identity private readonly Dictionary> _factors = new Dictionary>(); - private IClaimsIdentityFactory _claimsFactory; private TimeSpan _defaultLockout = TimeSpan.Zero; private bool _disposed; private IPasswordHasher _passwordHasher; @@ -40,7 +38,7 @@ namespace Microsoft.AspNet.Identity /// public UserManager(IUserStore store, IOptionsAccessor optionsAccessor, IPasswordHasher passwordHasher, IUserValidator userValidator, - IPasswordValidator passwordValidator, IClaimsIdentityFactory claimsIdentityFactory) + IPasswordValidator passwordValidator) { if (store == null) { @@ -54,24 +52,11 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("passwordHasher"); } - if (userValidator == null) - { - throw new ArgumentNullException("userValidator"); - } - if (passwordValidator == null) - { - throw new ArgumentNullException("passwordValidator"); - } - if (claimsIdentityFactory == null) - { - throw new ArgumentNullException("claimsIdentityFactory"); - } Store = store; Options = optionsAccessor.Options; PasswordHasher = passwordHasher; UserValidator = userValidator; PasswordValidator = passwordValidator; - ClaimsIdentityFactory = claimsIdentityFactory; // TODO: Email/Sms/Token services } @@ -111,27 +96,6 @@ namespace Microsoft.AspNet.Identity /// public IPasswordValidator PasswordValidator { get; set; } - /// - /// Used to create claims identities from users - /// - public IClaimsIdentityFactory ClaimsIdentityFactory - { - get - { - ThrowIfDisposed(); - return _claimsFactory; - } - set - { - ThrowIfDisposed(); - if (value == null) - { - throw new ArgumentNullException("value"); - } - _claimsFactory = value; - } - } - /// /// Used to send email /// @@ -163,7 +127,6 @@ namespace Microsoft.AspNet.Identity } _options = value; } - } /// @@ -319,23 +282,6 @@ namespace Microsoft.AspNet.Identity GC.SuppressFinalize(this); } - /// - /// Creates a ClaimsIdentity representing the user - /// - /// - /// - /// - /// - public virtual Task CreateIdentityAsync(TUser user, string authenticationType, CancellationToken cancellationToken = default(CancellationToken)) - { - ThrowIfDisposed(); - if (user == null) - { - throw new ArgumentNullException("user"); - } - return ClaimsIdentityFactory.CreateAsync(this, user, authenticationType, cancellationToken); - } - private async Task ValidateUserInternal(TUser user, CancellationToken cancellationToken) { return (UserValidator == null) @@ -349,7 +295,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task CreateAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); await UpdateSecurityStampInternal(user, cancellationToken); @@ -372,7 +319,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task UpdateAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); if (user == null) @@ -394,7 +342,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task DeleteAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task DeleteAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); if (user == null) @@ -411,7 +360,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual Task FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken)) + public virtual Task FindByIdAsync(string userId, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); return Store.FindByIdAsync(userId, cancellationToken); @@ -423,7 +373,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual Task FindByNameAsync(string userName, CancellationToken cancellationToken = default(CancellationToken)) + public virtual Task FindByNameAsync(string userName, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); if (userName == null) @@ -451,7 +402,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task CreateAsync(TUser user, string password, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task CreateAsync(TUser user, string password, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var passwordStore = GetPasswordStore(); @@ -477,7 +429,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task GetUserNameAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task GetUserNameAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); if (user == null) @@ -494,7 +447,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task SetUserNameAsync(TUser user, string userName, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task SetUserNameAsync(TUser user, string userName, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); if (user == null) @@ -511,7 +465,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task GetUserIdAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task GetUserIdAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); return await Store.GetUserIdAsync(user, cancellationToken); @@ -524,7 +479,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task FindByUserNamePasswordAsync(string userName, string password, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task FindByUserNamePasswordAsync(string userName, string password, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var user = await FindByNameAsync(userName, cancellationToken); @@ -542,7 +498,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task CheckPasswordAsync(TUser user, string password, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task CheckPasswordAsync(TUser user, string password, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var passwordStore = GetPasswordStore(); @@ -559,7 +516,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task HasPasswordAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task HasPasswordAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var passwordStore = GetPasswordStore(); @@ -577,7 +535,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task AddPasswordAsync(TUser user, string password, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task AddPasswordAsync(TUser user, string password, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var passwordStore = GetPasswordStore(); @@ -633,7 +592,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task RemovePasswordAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task RemovePasswordAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var passwordStore = GetPasswordStore(); @@ -664,7 +624,7 @@ namespace Microsoft.AspNet.Identity } /// - /// By default, retrieves the hashed password from the user store and calls PasswordHasher.VerifyHashPassword + /// By default, retrieves the hashed password from the user store and calls PasswordHasher.VerifyHashPassword /// /// /// @@ -695,7 +655,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task GetSecurityStampAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task GetSecurityStampAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var securityStore = GetSecurityStore(); @@ -712,7 +673,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task UpdateSecurityStampAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task UpdateSecurityStampAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); GetSecurityStore(); @@ -730,7 +692,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task GeneratePasswordResetTokenAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task GeneratePasswordResetTokenAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); return await GenerateUserTokenAsync("ResetPassword", user, cancellationToken); @@ -744,7 +707,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task ResetPasswordAsync(TUser user, string token, string newPassword, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task ResetPasswordAsync(TUser user, string token, string newPassword, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); if (user == null) @@ -794,7 +758,8 @@ namespace Microsoft.AspNet.Identity /// Returns the user associated with this login /// /// - public virtual Task FindByLoginAsync(UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken)) + public virtual Task FindByLoginAsync(UserLoginInfo login, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); return GetLoginStore().FindByLoginAsync(login, cancellationToken); @@ -807,7 +772,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task RemoveLoginAsync(TUser user, UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task RemoveLoginAsync(TUser user, UserLoginInfo login, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var loginStore = GetLoginStore(); @@ -831,7 +797,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task AddLoginAsync(TUser user, UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task AddLoginAsync(TUser user, UserLoginInfo login, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var loginStore = GetLoginStore(); @@ -858,7 +825,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task> GetLoginsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task> GetLoginsAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var loginStore = GetLoginStore(); @@ -887,7 +855,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task AddClaimAsync(TUser user, Claim claim, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task AddClaimAsync(TUser user, Claim claim, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var claimStore = GetClaimStore(); @@ -910,7 +879,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task RemoveClaimAsync(TUser user, Claim claim, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task RemoveClaimAsync(TUser user, Claim claim, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var claimStore = GetClaimStore(); @@ -928,7 +898,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task> GetClaimsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task> GetClaimsAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var claimStore = GetClaimStore(); @@ -956,7 +927,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task AddToRoleAsync(TUser user, string role, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task AddToRoleAsync(TUser user, string role, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var userRoleStore = GetUserRoleStore(); @@ -980,7 +952,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task AddToRolesAsync(TUser user, IEnumerable roles, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task AddToRolesAsync(TUser user, IEnumerable roles, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var userRoleStore = GetUserRoleStore(); @@ -1011,7 +984,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task RemoveFromRoleAsync(TUser user, string role, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task RemoveFromRoleAsync(TUser user, string role, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var userRoleStore = GetUserRoleStore(); @@ -1034,7 +1008,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task RemoveFromRolesAsync(TUser user, IEnumerable roles, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task RemoveFromRolesAsync(TUser user, IEnumerable roles, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var userRoleStore = GetUserRoleStore(); @@ -1063,7 +1038,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task> GetRolesAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task> GetRolesAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var userRoleStore = GetUserRoleStore(); @@ -1081,7 +1057,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task IsInRoleAsync(TUser user, string role, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task IsInRoleAsync(TUser user, string role, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var userRoleStore = GetUserRoleStore(); @@ -1109,7 +1086,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task GetEmailAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task GetEmailAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var store = GetEmailStore(); @@ -1127,7 +1105,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task SetEmailAsync(TUser user, string email, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task SetEmailAsync(TUser user, string email, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var store = GetEmailStore(); @@ -1147,7 +1126,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual Task FindByEmailAsync(string email, CancellationToken cancellationToken = default(CancellationToken)) + public virtual Task FindByEmailAsync(string email, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var store = GetEmailStore(); @@ -1164,7 +1144,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual Task GenerateEmailConfirmationTokenAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual Task GenerateEmailConfirmationTokenAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); return GenerateUserTokenAsync("Confirmation", user, cancellationToken); @@ -1177,7 +1158,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task ConfirmEmailAsync(TUser user, string token, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task ConfirmEmailAsync(TUser user, string token, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var store = GetEmailStore(); @@ -1199,7 +1181,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task IsEmailConfirmedAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task IsEmailConfirmedAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var store = GetEmailStore(); @@ -1227,7 +1210,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task GetPhoneNumberAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task GetPhoneNumberAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var store = GetPhoneNumberStore(); @@ -1245,7 +1229,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task SetPhoneNumberAsync(TUser user, string phoneNumber, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task SetPhoneNumberAsync(TUser user, string phoneNumber, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var store = GetPhoneNumberStore(); @@ -1267,7 +1252,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task ChangePhoneNumberAsync(TUser user, string phoneNumber, string token, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task ChangePhoneNumberAsync(TUser user, string phoneNumber, string token, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var store = GetPhoneNumberStore(); @@ -1291,7 +1277,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task IsPhoneNumberConfirmedAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task IsPhoneNumberConfirmedAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var store = GetPhoneNumberStore(); @@ -1355,7 +1342,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task VerifyUserTokenAsync(TUser user, string purpose, string token, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task VerifyUserTokenAsync(TUser user, string purpose, string token, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); if (UserTokenProvider == null) @@ -1377,7 +1365,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task GenerateUserTokenAsync(string purpose, TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task GenerateUserTokenAsync(string purpose, TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); if (UserTokenProvider == null) @@ -1416,7 +1405,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task> GetValidTwoFactorProvidersAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task> GetValidTwoFactorProvidersAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); if (user == null) @@ -1442,7 +1432,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task VerifyTwoFactorTokenAsync(TUser user, string twoFactorProvider, string token, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task VerifyTwoFactorTokenAsync(TUser user, string twoFactorProvider, string token, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); if (user == null) @@ -1451,8 +1442,8 @@ namespace Microsoft.AspNet.Identity } if (!_factors.ContainsKey(twoFactorProvider)) { - throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture, Resources.NoTwoFactorProvider, - twoFactorProvider)); + throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture, + Resources.NoTwoFactorProvider, twoFactorProvider)); } // Make sure the token is valid var provider = _factors[twoFactorProvider]; @@ -1466,7 +1457,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task GenerateTwoFactorTokenAsync(TUser user, string twoFactorProvider, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task GenerateTwoFactorTokenAsync(TUser user, string twoFactorProvider, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); if (user == null) @@ -1475,8 +1467,8 @@ namespace Microsoft.AspNet.Identity } if (!_factors.ContainsKey(twoFactorProvider)) { - throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture, Resources.NoTwoFactorProvider, - twoFactorProvider)); + throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture, + Resources.NoTwoFactorProvider, twoFactorProvider)); } return await _factors[twoFactorProvider].GenerateAsync(twoFactorProvider, this, user, cancellationToken); } @@ -1499,8 +1491,8 @@ namespace Microsoft.AspNet.Identity } if (!_factors.ContainsKey(twoFactorProvider)) { - throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture, Resources.NoTwoFactorProvider, - twoFactorProvider)); + throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture, + Resources.NoTwoFactorProvider, twoFactorProvider)); } await _factors[twoFactorProvider].NotifyAsync(token, this, user, cancellationToken); return IdentityResult.Success; @@ -1523,7 +1515,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task GetTwoFactorEnabledAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task GetTwoFactorEnabledAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var store = GetUserTwoFactorStore(); @@ -1541,7 +1534,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task SetTwoFactorEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task SetTwoFactorEnabledAsync(TUser user, bool enabled, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var store = GetUserTwoFactorStore(); @@ -1564,7 +1558,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task SendEmailAsync(TUser user, string subject, string body, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task SendEmailAsync(TUser user, string subject, string body, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); if (user == null) @@ -1590,7 +1585,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task SendSmsAsync(TUser user, string message, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task SendSmsAsync(TUser user, string message, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); if (user == null) @@ -1625,7 +1621,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task IsLockedOutAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task IsLockedOutAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var store = GetUserLockoutStore(); @@ -1648,7 +1645,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task SetLockoutEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task SetLockoutEnabledAsync(TUser user, bool enabled, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var store = GetUserLockoutStore(); @@ -1666,7 +1664,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task GetLockoutEnabledAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task GetLockoutEnabledAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var store = GetUserLockoutStore(); @@ -1683,7 +1682,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task GetLockoutEndDateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task GetLockoutEndDateAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var store = GetUserLockoutStore(); @@ -1701,7 +1701,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task SetLockoutEndDateAsync(TUser user, DateTimeOffset lockoutEnd, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task SetLockoutEndDateAsync(TUser user, DateTimeOffset lockoutEnd, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var store = GetUserLockoutStore(); @@ -1718,14 +1719,15 @@ namespace Microsoft.AspNet.Identity } /// - /// Increments the access failed count for the user and if the failed access account is greater than or equal - /// to the MaxFailedAccessAttempsBeforeLockout, the user will be locked out for the next DefaultAccountLockoutTimeSpan - /// and the AccessFailedCount will be reset to 0. + /// Increments the access failed count for the user and if the failed access account is greater than or equal + /// to the MaxFailedAccessAttempsBeforeLockout, the user will be locked out for the next + /// DefaultAccountLockoutTimeSpan and the AccessFailedCount will be reset to 0. /// /// /// /// - public virtual async Task AccessFailedAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task AccessFailedAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var store = GetUserLockoutStore(); @@ -1739,8 +1741,8 @@ namespace Microsoft.AspNet.Identity { return await UpdateAsync(user, cancellationToken); } - await - store.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.Add(Options.Lockout.DefaultLockoutTimeSpan), cancellationToken); + await store.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.Add(Options.Lockout.DefaultLockoutTimeSpan), + cancellationToken); await store.ResetAccessFailedCountAsync(user, cancellationToken); return await UpdateAsync(user, cancellationToken); } @@ -1751,7 +1753,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task ResetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task ResetAccessFailedCountAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var store = GetUserLockoutStore(); @@ -1769,7 +1772,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task GetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task GetAccessFailedCountAsync(TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { ThrowIfDisposed(); var store = GetUserLockoutStore(); diff --git a/src/Microsoft.AspNet.Identity/UserOptions.cs b/src/Microsoft.AspNet.Identity/UserOptions.cs index f24ceb3bf6..7c5b90977d 100644 --- a/src/Microsoft.AspNet.Identity/UserOptions.cs +++ b/src/Microsoft.AspNet.Identity/UserOptions.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.Framework.ConfigurationModel; - namespace Microsoft.AspNet.Identity { public class UserOptions diff --git a/src/Microsoft.AspNet.Identity/UserValidator.cs b/src/Microsoft.AspNet.Identity/UserValidator.cs index 067afdcbfc..9d10b25be2 100644 --- a/src/Microsoft.AspNet.Identity/UserValidator.cs +++ b/src/Microsoft.AspNet.Identity/UserValidator.cs @@ -1,14 +1,14 @@ // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System.Threading; -#if NET45 -using System.Net.Mail; -#endif using System; using System.Collections.Generic; using System.Globalization; using System.Linq; +#if NET45 +using System.Net.Mail; +#endif +using System.Threading; using System.Threading.Tasks; namespace Microsoft.AspNet.Identity @@ -26,7 +26,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - public virtual async Task ValidateAsync(UserManager manager, TUser user, CancellationToken cancellationToken = default(CancellationToken)) + public virtual async Task ValidateAsync(UserManager manager, TUser user, + CancellationToken cancellationToken = default(CancellationToken)) { if (manager == null) { @@ -102,7 +103,8 @@ namespace Microsoft.AspNet.Identity else { var owner = await manager.FindByNameAsync(userName); - if (owner != null && !string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user))) + if (owner != null && + !string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user))) { errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.DuplicateName, userName)); } @@ -130,7 +132,8 @@ namespace Microsoft.AspNet.Identity } #endif var owner = await manager.FindByEmailAsync(email); - if (owner != null && !string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user))) + if (owner != null && + !string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user))) { errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.DuplicateEmail, email)); } diff --git a/src/Microsoft.AspNet.Identity/project.json b/src/Microsoft.AspNet.Identity/project.json index 80c96a511c..ba73fdea11 100644 --- a/src/Microsoft.AspNet.Identity/project.json +++ b/src/Microsoft.AspNet.Identity/project.json @@ -1,10 +1,10 @@ { - "version": "3.0.0-*", + "version": "0.1-alpha-*", "dependencies": { - "Microsoft.Framework.ConfigurationModel": "1.0.0-*", - "Microsoft.Framework.DependencyInjection" : "1.0.0-*", - "Microsoft.Framework.OptionsModel": "1.0.0-*", - "System.Security.Claims" : "1.0.0-*" + "Microsoft.Framework.ConfigurationModel": "0.1-alpha-*", + "Microsoft.Framework.DependencyInjection" : "0.1-alpha-*", + "Microsoft.Framework.OptionsModel": "0.1-alpha-*", + "System.Security.Claims" : "0.1-alpha-*" }, "configurations": { "net45": {}, diff --git a/test/Microsoft.AspNet.Identity.Authentication.Test/HttpSignInTest.cs b/test/Microsoft.AspNet.Identity.Authentication.Test/HttpSignInTest.cs index 308e4f6388..43e365fcd8 100644 --- a/test/Microsoft.AspNet.Identity.Authentication.Test/HttpSignInTest.cs +++ b/test/Microsoft.AspNet.Identity.Authentication.Test/HttpSignInTest.cs @@ -16,7 +16,7 @@ using System.Threading.Tasks; using Xunit; using Microsoft.AspNet.Builder; -namespace Microsoft.AspNet.Identity.Security.Test +namespace Microsoft.AspNet.Identity.Authentication.Test { public class ApplicationUser : IdentityUser { } @@ -118,9 +118,11 @@ namespace Microsoft.AspNet.Identity.Security.Test [Fact] public void ConstructorNullChecks() { - Assert.Throws("userManager", () => new SignInManager(null, null)); + Assert.Throws("userManager", () => new SignInManager(null, null, null)); var userManager = MockHelpers.MockUserManager().Object; - Assert.Throws("authenticationManager", () => new SignInManager(userManager, null)); + Assert.Throws("authenticationManager", () => new SignInManager(userManager, null, null)); + var authManager = new Mock().Object; + Assert.Throws("claimsFactory", () => new SignInManager(userManager, authManager, null)); } //TODO: Mock fails in K (this works fine in net45) @@ -165,7 +167,9 @@ namespace Microsoft.AspNet.Identity.Security.Test var context = new Mock(); var contextAccessor = new Mock>(); contextAccessor.Setup(a => a.Value).Returns(context.Object); - var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object)); + var roleManager = MockHelpers.MockRoleManager(); + var claimsFactory = new Mock>(manager.Object, roleManager.Object); + var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), claimsFactory.Object); // Act var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false); @@ -187,14 +191,16 @@ namespace Microsoft.AspNet.Identity.Security.Test manager.Setup(m => m.IsLockedOutAsync(user, CancellationToken.None)).ReturnsAsync(false).Verifiable(); manager.Setup(m => m.FindByNameAsync(user.UserName, CancellationToken.None)).ReturnsAsync(user).Verifiable(); manager.Setup(m => m.CheckPasswordAsync(user, "password", CancellationToken.None)).ReturnsAsync(true).Verifiable(); - manager.Setup(m => m.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie, CancellationToken.None)).ReturnsAsync(new ClaimsIdentity("Microsoft.AspNet.Identity")).Verifiable(); var context = new Mock(); var response = new Mock(); context.Setup(c => c.Response).Returns(response.Object).Verifiable(); response.Setup(r => r.SignIn(It.IsAny(), It.Is(v => v.IsPersistent == isPersistent))).Verifiable(); var contextAccessor = new Mock>(); contextAccessor.Setup(a => a.Value).Returns(context.Object); - var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object)); + var roleManager = MockHelpers.MockRoleManager(); + var claimsFactory = new Mock>(manager.Object, roleManager.Object); + claimsFactory.Setup(m => m.CreateAsync(user, DefaultAuthenticationTypes.ApplicationCookie, CancellationToken.None)).ReturnsAsync(new ClaimsIdentity("Microsoft.AspNet.Identity")).Verifiable(); + var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), claimsFactory.Object); // Act var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false); @@ -205,6 +211,7 @@ namespace Microsoft.AspNet.Identity.Security.Test context.VerifyAll(); response.VerifyAll(); contextAccessor.VerifyAll(); + claimsFactory.VerifyAll(); } [Fact] @@ -223,7 +230,9 @@ namespace Microsoft.AspNet.Identity.Security.Test context.Setup(c => c.Response).Returns(response.Object).Verifiable(); var contextAccessor = new Mock>(); contextAccessor.Setup(a => a.Value).Returns(context.Object); - var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object)); + var roleManager = MockHelpers.MockRoleManager(); + var claimsFactory = new Mock>(manager.Object, roleManager.Object); + var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), claimsFactory.Object); // Act var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false); @@ -255,7 +264,8 @@ namespace Microsoft.AspNet.Identity.Security.Test context.Setup(c => c.Response).Returns(response.Object).Verifiable(); var contextAccessor = new Mock>(); contextAccessor.Setup(a => a.Value).Returns(context.Object); - var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object)); + var roleManager = MockHelpers.MockRoleManager(); + var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), new ClaimsIdentityFactory(manager.Object, roleManager.Object)); // Act var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false); @@ -281,6 +291,8 @@ namespace Microsoft.AspNet.Identity.Security.Test manager.Setup(m => m.IsLockedOutAsync(user, CancellationToken.None)).ReturnsAsync(false).Verifiable(); manager.Setup(m => m.FindByIdAsync(user.Id, CancellationToken.None)).ReturnsAsync(user).Verifiable(); manager.Setup(m => m.VerifyTwoFactorTokenAsync(user, provider, code, CancellationToken.None)).ReturnsAsync(true).Verifiable(); + manager.Setup(m => m.GetUserIdAsync(user, CancellationToken.None)).ReturnsAsync(user.Id).Verifiable(); + manager.Setup(m => m.GetUserNameAsync(user, CancellationToken.None)).ReturnsAsync(user.UserName).Verifiable(); var context = new Mock(); var response = new Mock(); response.Setup(r => r.SignIn(It.IsAny(), It.Is(v => v.IsPersistent == isPersistent))).Verifiable(); @@ -291,7 +303,9 @@ namespace Microsoft.AspNet.Identity.Security.Test context.Setup(c => c.AuthenticateAsync(HttpAuthenticationManager.TwoFactorUserIdAuthenticationType)).ReturnsAsync(authResult).Verifiable(); var contextAccessor = new Mock>(); contextAccessor.Setup(a => a.Value).Returns(context.Object); - var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object)); + var roleManager = MockHelpers.MockRoleManager(); + var claimsFactory = new ClaimsIdentityFactory(manager.Object, roleManager.Object); + var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), claimsFactory); // Act var result = await helper.TwoFactorSignInAsync(provider, code, isPersistent); @@ -344,7 +358,6 @@ namespace Microsoft.AspNet.Identity.Security.Test manager.Setup(m => m.FindByNameAsync(user.UserName, CancellationToken.None)).ReturnsAsync(user).Verifiable(); manager.Setup(m => m.GetUserIdAsync(user, CancellationToken.None)).ReturnsAsync(user.Id).Verifiable(); manager.Setup(m => m.CheckPasswordAsync(user, "password", CancellationToken.None)).ReturnsAsync(true).Verifiable(); - manager.Setup(m => m.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie, CancellationToken.None)).ReturnsAsync(new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie)).Verifiable(); var context = new Mock(); var response = new Mock(); context.Setup(c => c.Response).Returns(response.Object).Verifiable(); @@ -356,7 +369,10 @@ namespace Microsoft.AspNet.Identity.Security.Test var contextAccessor = new Mock>(); contextAccessor.Setup(a => a.Value).Returns(context.Object); var signInService = new HttpAuthenticationManager(contextAccessor.Object); - var helper = new SignInManager(manager.Object, signInService); + var roleManager = MockHelpers.MockRoleManager(); + var claimsFactory = new Mock>(manager.Object, roleManager.Object); + claimsFactory.Setup(m => m.CreateAsync(user, DefaultAuthenticationTypes.ApplicationCookie, CancellationToken.None)).ReturnsAsync(new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie)).Verifiable(); + var helper = new SignInManager(manager.Object, signInService, claimsFactory.Object); // Act var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false); @@ -367,10 +383,11 @@ namespace Microsoft.AspNet.Identity.Security.Test context.VerifyAll(); response.VerifyAll(); contextAccessor.VerifyAll(); + claimsFactory.VerifyAll(); } [Theory] - [InlineData("Microsoft.AspNet.Identity.Security.Application")] + [InlineData("Microsoft.AspNet.Identity.Authentication.Application")] [InlineData("Foo")] public void SignOutCallsContextResponseSignOut(string authenticationType) { @@ -382,7 +399,9 @@ namespace Microsoft.AspNet.Identity.Security.Test response.Setup(r => r.SignOut(authenticationType)).Verifiable(); var contextAccessor = new Mock>(); contextAccessor.Setup(a => a.Value).Returns(context.Object); - var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object)) + var roleManager = MockHelpers.MockRoleManager(); + var claimsFactory = new Mock>(manager.Object, roleManager.Object); + var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), claimsFactory.Object) { AuthenticationType = authenticationType }; @@ -408,7 +427,9 @@ namespace Microsoft.AspNet.Identity.Security.Test var context = new Mock(); var contextAccessor = new Mock>(); contextAccessor.Setup(a => a.Value).Returns(context.Object); - var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object)); + var roleManager = MockHelpers.MockRoleManager(); + var claimsFactory = new Mock>(manager.Object, roleManager.Object); + var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), claimsFactory.Object); // Act var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false); @@ -426,7 +447,9 @@ namespace Microsoft.AspNet.Identity.Security.Test var context = new Mock(); var contextAccessor = new Mock>(); contextAccessor.Setup(a => a.Value).Returns(context.Object); - var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object)); + var roleManager = MockHelpers.MockRoleManager(); + var claimsFactory = new Mock>(manager.Object, roleManager.Object); + var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), claimsFactory.Object); // Act var result = await helper.PasswordSignInAsync("bogus", "bogus", false, false); @@ -455,7 +478,9 @@ namespace Microsoft.AspNet.Identity.Security.Test var context = new Mock(); var contextAccessor = new Mock>(); contextAccessor.Setup(a => a.Value).Returns(context.Object); - var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object)); + var roleManager = MockHelpers.MockRoleManager(); + var claimsFactory = new Mock>(manager.Object, roleManager.Object); + var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object), claimsFactory.Object); // Act var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, true); diff --git a/test/Microsoft.AspNet.Identity.Authentication.Test/IdentityExtensionsTest.cs b/test/Microsoft.AspNet.Identity.Authentication.Test/IdentityExtensionsTest.cs index 1ca702576c..0b42b7f813 100644 --- a/test/Microsoft.AspNet.Identity.Authentication.Test/IdentityExtensionsTest.cs +++ b/test/Microsoft.AspNet.Identity.Authentication.Test/IdentityExtensionsTest.cs @@ -6,7 +6,7 @@ using System.Security.Claims; using System.Security.Principal; using Xunit; -namespace Microsoft.AspNet.Identity.Security.Test +namespace Microsoft.AspNet.Identity.Authentication.Test { public class IdentityExtensionsTest { diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/EntityIdentityBuilderExtensions.cs b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/EntityIdentityBuilderExtensions.cs new file mode 100644 index 0000000000..c86bc2b306 --- /dev/null +++ b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/EntityIdentityBuilderExtensions.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Identity.EntityFramework; +using Microsoft.AspNet.Identity.EntityFramework.InMemory.Test; +using Microsoft.Data.Entity; +using Microsoft.Framework.DependencyInjection; + +namespace Microsoft.AspNet.Identity +{ + public static class EntityIdentityBuilderExtensions + { + public static IdentityBuilder AddEntityFrameworkInMemory(this IdentityBuilder builder) + where TUser : InMemoryUser + where TRole : IdentityRole + where TDbContext : DbContext + { + builder.Services.AddScoped(); + builder.Services.AddScoped, InMemoryUserStore>(); + builder.Services.AddScoped, RoleStore>(); + return builder; + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryContext.cs b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryContext.cs new file mode 100644 index 0000000000..61665e8503 --- /dev/null +++ b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryContext.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.Data.Entity; +using Microsoft.Data.Entity.Metadata; + +namespace Microsoft.AspNet.Identity.EntityFramework.InMemory.Test +{ + public class InMemoryContext : + InMemoryContext + { + public InMemoryContext() { } + public InMemoryContext(IServiceProvider serviceProvider) : base(serviceProvider) { } + } + + public class InMemoryContext : + InMemoryContext + where TUser : InMemoryUser + { + public InMemoryContext() { } + public InMemoryContext(IServiceProvider serviceProvider) : base(serviceProvider) { } + } + + public class InMemoryContext : DbContext + where TUser : InMemoryUser + where TRole : IdentityRole + where TUserLogin : IdentityUserLogin + where TUserRole : IdentityUserRole + where TUserClaim : IdentityUserClaim + where TKey : IEquatable + { + + public DbSet Users { get; set; } + public DbSet Roles { get; set; } + public DbSet RoleClaims { get; set; } + + public InMemoryContext(IServiceProvider serviceProvider) + : base(serviceProvider) { } + + public InMemoryContext() { } + + protected override void OnConfiguring(DbContextOptions builder) + { + builder.UseInMemoryStore(); + } + + protected override void OnModelCreating(ModelBuilder builder) + { + builder.Entity() + .Key(u => u.Id) + .Properties(ps => ps.Property(u => u.UserName)) + .ToTable("AspNetUsers"); + + builder.Entity() + .Key(r => r.Id) + .ToTable("AspNetRoles"); + + builder.Entity() + .Key(r => new { r.UserId, r.RoleId }) + .ForeignKeys(fk => fk.ForeignKey(f => f.UserId)) + .ForeignKeys(fk => fk.ForeignKey(f => f.RoleId)) + .ToTable("AspNetUserRoles"); + + builder.Entity() + .Key(l => new { l.LoginProvider, l.ProviderKey, l.UserId }) + .ForeignKeys(fk => fk.ForeignKey(f => f.UserId)) + .ToTable("AspNetUserLogins"); + + builder.Entity() + .Key(c => c.Id) + .ForeignKeys(fk => fk.ForeignKey(f => f.UserId)) + .ToTable("AspNetUserClaims"); + + builder.Entity() + .Key(c => c.Id) + .ForeignKeys(fk => fk.ForeignKey(f => f.RoleId)) + .ToTable("AspNetRoleClaims"); + + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryUser.cs b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryUser.cs new file mode 100644 index 0000000000..59ad8d4ea7 --- /dev/null +++ b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryUser.cs @@ -0,0 +1,106 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.AspNet.Identity.EntityFramework.InMemory.Test +{ + public class InMemoryUser : InMemoryUser + { + public InMemoryUser() + { + Id = Guid.NewGuid().ToString(); + } + + public InMemoryUser(string userName) + : this() + { + UserName = userName; + } + } + + public class InMemoryUser + where TLogin : IdentityUserLogin + where TRole : IdentityUserRole + where TClaim : IdentityUserClaim + where TKey : IEquatable + { + public InMemoryUser() + { + Claims = new List(); + Roles = new List(); + Logins = new List(); + } + + public virtual TKey Id { get; set; } + public virtual string UserName { get; set; } + + /// + /// Email + /// + public virtual string Email { get; set; } + + /// + /// True if the email is confirmed, default is false + /// + public virtual bool EmailConfirmed { get; set; } + + /// + /// The salted/hashed form of the user password + /// + public virtual string PasswordHash { get; set; } + + /// + /// A random value that should change whenever a users credentials have changed (password changed, login removed) + /// + public virtual string SecurityStamp { get; set; } + + /// + /// PhoneNumber for the user + /// + public virtual string PhoneNumber { get; set; } + + /// + /// True if the phone number is confirmed, default is false + /// + public virtual bool PhoneNumberConfirmed { get; set; } + + /// + /// Is two factor enabled for the user + /// + public virtual bool TwoFactorEnabled { get; set; } + + /// + /// DateTime in UTC when lockout ends, any time in the past is considered not locked out. + /// + public virtual DateTime? LockoutEnd { get; set; } + + /// + /// Is lockout enabled for this user + /// + public virtual bool LockoutEnabled { get; set; } + + /// + /// Used to record failures for the purposes of lockout + /// + public virtual int AccessFailedCount { get; set; } + + /// + /// Navigation property for user roles + /// + public virtual ICollection Roles { get; private set; } + + /// + /// Navigation property for user claims + /// + public virtual ICollection Claims { get; private set; } + + /// + /// Navigation property for user logins + /// + public virtual ICollection Logins { get; private set; } + + } +} + diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryUserStore.cs b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryUserStore.cs new file mode 100644 index 0000000000..259b13dd15 --- /dev/null +++ b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryUserStore.cs @@ -0,0 +1,883 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Linq.Expressions; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Data.Entity; + +namespace Microsoft.AspNet.Identity.EntityFramework.InMemory.Test +{ + public class InMemoryUserStore : InMemoryUserStore + { + public InMemoryUserStore(InMemoryContext context) : base(context) { } + } + + public class InMemoryUserStore : InMemoryUserStore + where TUser : InMemoryUser + { + public InMemoryUserStore(InMemoryContext context) : base(context) { } + } + + public class InMemoryUserStore : InMemoryUserStore + where TUser:InMemoryUser + where TContext : DbContext + { + public InMemoryUserStore(TContext context) : base(context) { } + } + + public class InMemoryUserStore : + IUserLoginStore, + IUserClaimStore, + IUserRoleStore, + IUserPasswordStore, + IUserSecurityStampStore, + IQueryableUserStore, + IUserEmailStore, + IUserPhoneNumberStore, + IUserTwoFactorStore, + IUserLockoutStore + where TKey : IEquatable + where TUser : InMemoryUser + where TRole : IdentityRole + where TUserLogin : IdentityUserLogin, new() + where TUserRole : IdentityUserRole, new() + where TUserClaim : IdentityUserClaim, new() + where TContext : DbContext + { + private bool _disposed; + + public InMemoryUserStore(TContext context) + { + if (context == null) + { + throw new ArgumentNullException("context"); + } + Context = context; + AutoSaveChanges = true; + } + + public TContext Context { get; private set; } + + /// + /// If true will call SaveChanges after CreateAsync/UpdateAsync/DeleteAsync + /// + public bool AutoSaveChanges { get; set; } + + private Task SaveChanges(CancellationToken cancellationToken) + { + return AutoSaveChanges ? Context.SaveChangesAsync(cancellationToken) : Task.FromResult(0); + } + + protected virtual Task GetUserAggregate(Expression> filter, CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult(Users.SingleOrDefault(filter)); + // TODO: return Users.SingleOrDefaultAsync(filter, cancellationToken); + //Include(u => u.Roles) + //.Include(u => u.Claims) + //.Include(u => u.Logins) + } + + public Task GetUserIdAsync(TUser user, CancellationToken cancellationToken = new CancellationToken()) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(Convert.ToString(user.Id, CultureInfo.InvariantCulture)); + } + + public Task GetUserNameAsync(TUser user, CancellationToken cancellationToken = new CancellationToken()) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(user.UserName); + } + + public Task SetUserNameAsync(TUser user, string userName, CancellationToken cancellationToken = new CancellationToken()) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.UserName = userName; + return Task.FromResult(0); + } + + public async virtual Task CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + await Context.AddAsync(user, cancellationToken); + await SaveChanges(cancellationToken); + } + + public async virtual Task UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + await Context.UpdateAsync(user, cancellationToken); + await SaveChanges(cancellationToken); + } + + public async virtual Task DeleteAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + Context.Delete(user); + await SaveChanges(cancellationToken); + } + + public virtual TKey ConvertUserId(string userId) + { + return (TKey)Convert.ChangeType(userId, typeof(TKey)); + } + + /// + /// Find a user by id + /// + /// + /// + /// + public virtual Task FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + var id = ConvertUserId(userId); + return GetUserAggregate(u => u.Id.Equals(id), cancellationToken); + } + + /// + /// Find a user by name + /// + /// + /// + /// + public virtual Task FindByNameAsync(string userName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + return GetUserAggregate(u => u.UserName.ToUpper() == userName.ToUpper(), cancellationToken); + } + + public IQueryable Users + { + get { return Context.Set(); } + } + + public async virtual Task AddLoginAsync(TUser user, UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + if (login == null) + { + throw new ArgumentNullException("login"); + } + var l = new TUserLogin + { + UserId = user.Id, + ProviderKey = login.ProviderKey, + LoginProvider = login.LoginProvider + }; + await Context.Set().AddAsync(l, cancellationToken); + user.Logins.Add(l); + } + + public virtual Task RemoveLoginAsync(TUser user, UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + if (login == null) + { + throw new ArgumentNullException("login"); + } + var provider = login.LoginProvider; + var key = login.ProviderKey; + var entry = user.Logins.SingleOrDefault(l => l.LoginProvider == provider && l.ProviderKey == key); + if (entry != null) + { + user.Logins.Remove(entry); + Context.Set().Remove(entry); + } + return Task.FromResult(0); + } + + public virtual Task> GetLoginsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + IList result = + user.Logins.Select(l => new UserLoginInfo(l.LoginProvider, l.ProviderKey)).ToList(); + return Task.FromResult(result); + } + + public async virtual Task FindByLoginAsync(UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (login == null) + { + throw new ArgumentNullException("login"); + } + var provider = login.LoginProvider; + var key = login.ProviderKey; + // TODO: use FirstOrDefaultAsync + var userLogin = + Context.Set().FirstOrDefault(l => l.LoginProvider == provider && l.ProviderKey == key); + if (userLogin != null) + { + return await GetUserAggregate(u => u.Id.Equals(userLogin.UserId), cancellationToken); + } + return null; + } + + /// + /// Set the password hash for a user + /// + /// + /// + /// + /// + public virtual Task SetPasswordHashAsync(TUser user, string passwordHash, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.PasswordHash = passwordHash; + return Task.FromResult(0); + } + + /// + /// Get the password hash for a user + /// + /// + /// + /// + public virtual Task GetPasswordHashAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(user.PasswordHash); + } + + /// + /// Returns true if the user has a password set + /// + /// + /// + /// + public virtual Task HasPasswordAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + return Task.FromResult(user.PasswordHash != null); + } + + /// + /// Return the claims for a user + /// + /// + /// + /// + public virtual Task> GetClaimsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + IList result = user.Claims.Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToList(); + return Task.FromResult(result); + } + + /// + /// Add a claim to a user + /// + /// + /// + /// + /// + public virtual Task AddClaimAsync(TUser user, Claim claim, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + if (claim == null) + { + throw new ArgumentNullException("claim"); + } + user.Claims.Add(new TUserClaim { UserId = user.Id, ClaimType = claim.Type, ClaimValue = claim.Value }); + return Task.FromResult(0); + } + + /// + /// Remove a claim from a user + /// + /// + /// + /// + /// + public virtual Task RemoveClaimAsync(TUser user, Claim claim, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + if (claim == null) + { + throw new ArgumentNullException("claim"); + } + var claims = + user.Claims.Where(uc => uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToList(); + foreach (var c in claims) + { + user.Claims.Remove(c); + } + // TODO:these claims might not exist in the dbset + //var query = + // _userClaims.Where( + // uc => uc.UserId.Equals(user.Id) && uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type); + //foreach (var c in query) + //{ + // _userClaims.Remove(c); + //} + return Task.FromResult(0); + } + + /// + /// Returns whether the user email is confirmed + /// + /// + /// + /// + public virtual Task GetEmailConfirmedAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(user.EmailConfirmed); + } + + /// + /// Set IsConfirmed on the user + /// + /// + /// + /// + /// + public virtual Task SetEmailConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.EmailConfirmed = confirmed; + return Task.FromResult(0); + } + + /// + /// Set the user email + /// + /// + /// + /// + /// + public virtual Task SetEmailAsync(TUser user, string email, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.Email = email; + return Task.FromResult(0); + } + + /// + /// Get the user's email + /// + /// + /// + /// + public virtual Task GetEmailAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(user.Email); + } + + /// + /// FindByLoginAsync a user by email + /// + /// + /// + /// + public virtual Task FindByEmailAsync(string email, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + return Task.FromResult(Users.SingleOrDefault(u => u.Email.ToUpper() == email.ToUpper())); + //return GetUserAggregate(u => u.Email.ToUpper() == email.ToUpper(), cancellationToken); + } + + /// + /// Returns the DateTimeOffset that represents the end of a user's lockout, any time in the past should be considered + /// not locked out. + /// + /// + /// + /// + public virtual Task GetLockoutEndDateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return + Task.FromResult(user.LockoutEnd.HasValue + ? new DateTimeOffset(DateTime.SpecifyKind(user.LockoutEnd.Value, DateTimeKind.Utc)) + : new DateTimeOffset()); + } + + /// + /// Locks a user out until the specified end date (set to a past date, to unlock a user) + /// + /// + /// + /// + /// + public virtual Task SetLockoutEndDateAsync(TUser user, DateTimeOffset lockoutEnd, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.LockoutEnd = lockoutEnd == DateTimeOffset.MinValue ? (DateTime?)null : lockoutEnd.UtcDateTime; + return Task.FromResult(0); + } + + /// + /// Used to record when an attempt to access the user has failed + /// + /// + /// + /// + public virtual Task IncrementAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.AccessFailedCount++; + return Task.FromResult(user.AccessFailedCount); + } + + /// + /// Used to reset the account access count, typically after the account is successfully accessed + /// + /// + /// + /// + public virtual Task ResetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.AccessFailedCount = 0; + return Task.FromResult(0); + } + + /// + /// Returns the current number of failed access attempts. This number usually will be reset whenever the password is + /// verified or the account is locked out. + /// + /// + /// + /// + public virtual Task GetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(user.AccessFailedCount); + } + + /// + /// Returns whether the user can be locked out. + /// + /// + /// + /// + public virtual Task GetLockoutEnabledAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(user.LockoutEnabled); + } + + /// + /// Sets whether the user can be locked out. + /// + /// + /// + /// + /// + public virtual Task SetLockoutEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.LockoutEnabled = enabled; + return Task.FromResult(0); + } + + /// + /// Set the user's phone number + /// + /// + /// + /// + /// + public virtual Task SetPhoneNumberAsync(TUser user, string phoneNumber, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.PhoneNumber = phoneNumber; + return Task.FromResult(0); + } + + /// + /// Get a user's phone number + /// + /// + /// + /// + public virtual Task GetPhoneNumberAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(user.PhoneNumber); + } + + /// + /// Returns whether the user phoneNumber is confirmed + /// + /// + /// + /// + public virtual Task GetPhoneNumberConfirmedAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(user.PhoneNumberConfirmed); + } + + /// + /// Set PhoneNumberConfirmed on the user + /// + /// + /// + /// + /// + public virtual Task SetPhoneNumberConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.PhoneNumberConfirmed = confirmed; + return Task.FromResult(0); + } + + /// + /// Add a user to a role + /// + /// + /// + /// + /// + public virtual Task AddToRoleAsync(TUser user, string roleName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + // TODO: + //if (String.IsNullOrWhiteSpace(roleName)) + //{ + // throw new ArgumentException(IdentityResources.ValueCannotBeNullOrEmpty, "roleName"); + //} + var roleEntity = Context.Set().SingleOrDefault(r => r.Name.ToUpper() == roleName.ToUpper()); + if (roleEntity == null) + { + throw new InvalidOperationException("Role Not Found"); + //TODO: String.Format(CultureInfo.CurrentCulture, IdentityResources.RoleNotFound, roleName)); + } + var ur = new TUserRole { UserId = user.Id, RoleId = roleEntity.Id }; + user.Roles.Add(ur); + roleEntity.Users.Add(ur); + return Task.FromResult(0); + } + + /// + /// Remove a user from a role + /// + /// + /// + /// + /// + public virtual Task RemoveFromRoleAsync(TUser user, string roleName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + //if (String.IsNullOrWhiteSpace(roleName)) + //{ + // throw new ArgumentException(IdentityResources.ValueCannotBeNullOrEmpty, "roleName"); + //} + var roleEntity = Context.Set().SingleOrDefault(r => r.Name.ToUpper() == roleName.ToUpper()); + if (roleEntity != null) + { + var userRole = user.Roles.FirstOrDefault(r => roleEntity.Id.Equals(r.RoleId)); + if (userRole != null) + { + user.Roles.Remove(userRole); + roleEntity.Users.Remove(userRole); + } + } + return Task.FromResult(0); + } + + /// + /// Get the names of the roles a user is a member of + /// + /// + /// + /// + public virtual Task> GetRolesAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + var query = from userRoles in user.Roles + join roles in Context.Set() + on userRoles.RoleId equals roles.Id + select roles.Name; + return Task.FromResult>(query.ToList()); + } + + /// + /// Returns true if the user is in the named role + /// + /// + /// + /// + /// + public virtual Task IsInRoleAsync(TUser user, string roleName, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + //if (String.IsNullOrWhiteSpace(roleName)) + //{ + // throw new ArgumentException(IdentityResources.ValueCannotBeNullOrEmpty, "roleName"); + //} + var any = + Context.Set().Where(r => r.Name.ToUpper() == roleName.ToUpper()) + .Where(r => r.Users.Any(ur => ur.UserId.Equals(user.Id))) + .Count() > 0; + return Task.FromResult(any); + } + + /// + /// Set the security stamp for the user + /// + /// + /// + /// + /// + public virtual Task SetSecurityStampAsync(TUser user, string stamp, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.SecurityStamp = stamp; + return Task.FromResult(0); + } + + /// + /// Get the security stamp for a user + /// + /// + /// + /// + public virtual Task GetSecurityStampAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(user.SecurityStamp); + } + + /// + /// Set whether two factor authentication is enabled for the user + /// + /// + /// + /// + /// + public virtual Task SetTwoFactorEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + user.TwoFactorEnabled = enabled; + return Task.FromResult(0); + } + + /// + /// Gets whether two factor authentication is enabled for the user + /// + /// + /// + /// + public virtual Task GetTwoFactorEnabledAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + cancellationToken.ThrowIfCancellationRequested(); + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + return Task.FromResult(user.TwoFactorEnabled); + } + + private void ThrowIfDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(GetType().Name); + } + } + + /// + /// Dispose the store + /// + public void Dispose() + { + _disposed = true; + } + } +} diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryUserStoreTest.cs b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryUserStoreTest.cs new file mode 100644 index 0000000000..13c68de6f8 --- /dev/null +++ b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/InMemoryUserStoreTest.cs @@ -0,0 +1,1661 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Identity.Test; +using Microsoft.AspNet.Testing; +using Microsoft.Data.Entity; +using Microsoft.Data.Entity.Metadata; +using System; +using System.Linq; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.DependencyInjection.Fallback; +using Microsoft.Framework.OptionsModel; +using Xunit; + +namespace Microsoft.AspNet.Identity.EntityFramework.InMemory.Test +{ + public class InMemoryUserStoreTest + { + + [Fact] + public async Task CanUseAddedManagerInstance() + { + var services = new ServiceCollection(); + services.AddEntityFramework().AddInMemoryStore(); + services.AddIdentity(); + services.AddSingleton, OptionsAccessor>(); + services.AddInstance(new InMemoryContext()); + services.AddTransient, InMemoryUserStore>(); + var provider = services.BuildServiceProvider(); + var manager = provider.GetService>(); + Assert.NotNull(manager); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(new InMemoryUser("hello"))); + } + + [Fact] + public async Task CanCreateUsingAddUserManager() + { + var services = new ServiceCollection(); + services.AddEntityFramework().AddInMemoryStore(); + + // TODO: this needs to construct a new instance of InMemoryStore + var store = new InMemoryUserStore(new InMemoryContext()); + services.Add(OptionsServices.GetDefaultServices()); + services.AddIdentity(s => + { + s.AddUserStore(() => store); + }); + + var provider = services.BuildServiceProvider(); + var manager = provider.GetService>(); + Assert.NotNull(manager); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(new InMemoryUser("hello2"))); + } + + [Fact] + public async Task EntityUserStoreMethodsThrowWhenDisposedTest() + { + var store = new InMemoryUserStore(new InMemoryContext()); + store.Dispose(); + await Assert.ThrowsAsync(async () => await store.AddClaimAsync(null, null)); + await Assert.ThrowsAsync(async () => await store.AddLoginAsync(null, null)); + await Assert.ThrowsAsync(async () => await store.AddToRoleAsync(null, null)); + await Assert.ThrowsAsync(async () => await store.GetClaimsAsync(null)); + await Assert.ThrowsAsync(async () => await store.GetLoginsAsync(null)); + await Assert.ThrowsAsync(async () => await store.GetRolesAsync(null)); + await Assert.ThrowsAsync(async () => await store.IsInRoleAsync(null, null)); + await Assert.ThrowsAsync(async () => await store.RemoveClaimAsync(null, null)); + await Assert.ThrowsAsync(async () => await store.RemoveLoginAsync(null, null)); + await Assert.ThrowsAsync( + async () => await store.RemoveFromRoleAsync(null, null)); + await Assert.ThrowsAsync(async () => await store.RemoveClaimAsync(null, null)); + await Assert.ThrowsAsync(async () => await store.FindByLoginAsync(null)); + await Assert.ThrowsAsync(async () => await store.FindByIdAsync(null)); + await Assert.ThrowsAsync(async () => await store.FindByNameAsync(null)); + await Assert.ThrowsAsync(async () => await store.CreateAsync(null)); + await Assert.ThrowsAsync(async () => await store.UpdateAsync(null)); + await Assert.ThrowsAsync(async () => await store.DeleteAsync(null)); + await Assert.ThrowsAsync( + async () => await store.SetEmailConfirmedAsync(null, true)); + await Assert.ThrowsAsync(async () => await store.GetEmailConfirmedAsync(null)); + await Assert.ThrowsAsync( + async () => await store.SetPhoneNumberConfirmedAsync(null, true)); + await Assert.ThrowsAsync( + async () => await store.GetPhoneNumberConfirmedAsync(null)); + } + + [Fact] + public async Task EntityUserStorePublicNullCheckTest() + { + Assert.Throws("context", () => new InMemoryUserStore(null)); + var store = new InMemoryUserStore(new InMemoryContext()); + await Assert.ThrowsAsync("user", async () => await store.GetUserIdAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.GetUserNameAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.SetUserNameAsync(null, null)); + await Assert.ThrowsAsync("user", async () => await store.CreateAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.UpdateAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.DeleteAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.AddClaimAsync(null, null)); + await Assert.ThrowsAsync("user", async () => await store.RemoveClaimAsync(null, null)); + await Assert.ThrowsAsync("user", async () => await store.GetClaimsAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.GetLoginsAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.GetRolesAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.AddLoginAsync(null, null)); + await + Assert.ThrowsAsync("user", async () => await store.RemoveLoginAsync(null, null)); + await Assert.ThrowsAsync("user", async () => await store.AddToRoleAsync(null, null)); + await + Assert.ThrowsAsync("user", + async () => await store.RemoveFromRoleAsync(null, null)); + await Assert.ThrowsAsync("user", async () => await store.IsInRoleAsync(null, null)); + await Assert.ThrowsAsync("user", async () => await store.GetPasswordHashAsync(null)); + await + Assert.ThrowsAsync("user", + async () => await store.SetPasswordHashAsync(null, null)); + await Assert.ThrowsAsync("user", async () => await store.GetSecurityStampAsync(null)); + await Assert.ThrowsAsync("user", + async () => await store.SetSecurityStampAsync(null, null)); + await Assert.ThrowsAsync("claim", + async () => await store.AddClaimAsync(new InMemoryUser("fake"), null)); + await Assert.ThrowsAsync("claim", + async () => await store.RemoveClaimAsync(new InMemoryUser("fake"), null)); + await Assert.ThrowsAsync("login", + async () => await store.AddLoginAsync(new InMemoryUser("fake"), null)); + await Assert.ThrowsAsync("login", + async () => await store.RemoveLoginAsync(new InMemoryUser("fake"), null)); + await Assert.ThrowsAsync("login", async () => await store.FindByLoginAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.GetEmailConfirmedAsync(null)); + await Assert.ThrowsAsync("user", + async () => await store.SetEmailConfirmedAsync(null, true)); + await Assert.ThrowsAsync("user", async () => await store.GetEmailAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.SetEmailAsync(null, null)); + await Assert.ThrowsAsync("user", async () => await store.GetPhoneNumberAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.SetPhoneNumberAsync(null, null)); + await Assert.ThrowsAsync("user", + async () => await store.GetPhoneNumberConfirmedAsync(null)); + await Assert.ThrowsAsync("user", + async () => await store.SetPhoneNumberConfirmedAsync(null, true)); + await Assert.ThrowsAsync("user", async () => await store.GetTwoFactorEnabledAsync(null)); + await Assert.ThrowsAsync("user", + async () => await store.SetTwoFactorEnabledAsync(null, true)); + await Assert.ThrowsAsync("user", async () => await store.GetAccessFailedCountAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.GetLockoutEnabledAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.SetLockoutEnabledAsync(null, false)); + await Assert.ThrowsAsync("user", async () => await store.GetLockoutEndDateAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.SetLockoutEndDateAsync(null, new DateTimeOffset())); + await Assert.ThrowsAsync("user", async () => await store.ResetAccessFailedCountAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.IncrementAccessFailedCountAsync(null)); + // TODO: + //ExceptionAssert.ThrowsArgumentNullOrEmpty( + // async () => store.AddToRoleAsync(new InMemoryUser("fake"), null)), "roleName"); + //ExceptionAssert.ThrowsArgumentNullOrEmpty( + // async () => store.RemoveFromRoleAsync(new InMemoryUser("fake"), null)), "roleName"); + //ExceptionAssert.ThrowsArgumentNullOrEmpty( + // async () => store.IsInRoleAsync(new InMemoryUser("fake"), null)), "roleName"); + } + + [Fact] + public async Task Can_share_instance_between_contexts_with_sugar_experience2() + { + // TODO: Should be possible to do this without creating the provider externally, but + // that is currently not working. Will be investigated. + var services = new ServiceCollection(); + services.AddEntityFramework().AddInMemoryStore(); + var provider = services.BuildServiceProvider(); + + using (var db = new InMemoryContext(provider)) + { + db.Users.Add(new InMemoryUser { UserName = "John Doe" }); + await db.SaveChangesAsync(); + } + + using (var db = new InMemoryContext(provider)) + { + var data = db.Users.ToList(); + Assert.Equal(1, data.Count); + Assert.Equal("John Doe", data[0].UserName); + } + } + + [Fact] + public async Task Can_create_two_artists() + { + using (var db = new SimpleContext()) + { + db.Artists.Add(new SimpleContext.Artist { Name = "John Doe", ArtistId = Guid.NewGuid().ToString() }); + await db.SaveChangesAsync(); + db.Artists.Add(new SimpleContext.Artist { Name = "Second guy", ArtistId = Guid.NewGuid().ToString() }); + await db.SaveChangesAsync(); + } + } + + private class SimpleContext : DbContext + { + public DbSet Artists { get; set; } + + protected override void OnConfiguring(DbContextOptions builder) + { + builder.UseInMemoryStore(); + } + + protected override void OnModelCreating(ModelBuilder builder) + { + builder.Entity().Key(a => a.ArtistId); + } + + public class Artist// : ArtistBase + { + public string ArtistId { get; set; } + public string Name { get; set; } + } + + public class ArtistBase + { + public TKey ArtistId { get; set; } + public string Name { get; set; } + } + } + + [Fact] + public async Task CanDeleteUser() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("DeleteAsync"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); + Assert.Null(await manager.FindByIdAsync(user.Id)); + } + + [Fact] + public async Task CanUpdateUserName() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("UpdateAsync"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.Null(await manager.FindByNameAsync("New")); + user.UserName = "New"; + IdentityResultAssert.IsSuccess(await manager.UpdateAsync(user)); + Assert.NotNull(await manager.FindByNameAsync("New")); + Assert.Null(await manager.FindByNameAsync("UpdateAsync")); + } + + [Fact] + public async Task CanSetUserName() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("UpdateAsync"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.Null(await manager.FindByNameAsync("New")); + IdentityResultAssert.IsSuccess(await manager.SetUserNameAsync(user, "New")); + Assert.NotNull(await manager.FindByNameAsync("New")); + Assert.Null(await manager.FindByNameAsync("UpdateAsync")); + } + + [Fact] + public async Task UserValidatorCanBlockCreate() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("CreateBlocked"); + manager.UserValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.CreateAsync(user), AlwaysBadValidator.ErrorMessage); + } + + [Fact] + public async Task UserValidatorCanBlockUpdate() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("UpdateBlocked"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + manager.UserValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.UpdateAsync(user), AlwaysBadValidator.ErrorMessage); + } + + [Theory] + [InlineData("")] + [InlineData(null)] + public async Task UserValidatorBlocksShortEmailsWhenRequiresUniqueEmail(string email) + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("UpdateBlocked") { Email = email }; + manager.Options.User.RequireUniqueEmail = true; + IdentityResultAssert.IsFailure(await manager.CreateAsync(user), "Email cannot be null or empty."); + } + +#if NET45 + [Theory] + [InlineData("@@afd")] + [InlineData("bogus")] + public async Task UserValidatorBlocksInvalidEmailsWhenRequiresUniqueEmail(string email) + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("UpdateBlocked") { Email = email }; + manager.Options.User.RequireUniqueEmail = true; + IdentityResultAssert.IsFailure(await manager.CreateAsync(user), "Email '" + email + "' is invalid."); + } +#endif + + [Fact] + public async Task PasswordValidatorCanBlockAddPassword() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("AddPasswordBlocked"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + manager.PasswordValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.AddPasswordAsync(user, "password"), + AlwaysBadValidator.ErrorMessage); + } + + [Fact] + public async Task PasswordValidatorCanBlockChangePassword() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("ChangePasswordBlocked"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); + manager.PasswordValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.ChangePasswordAsync(user, "password", "new"), + AlwaysBadValidator.ErrorMessage); + } + + [Fact] + public async Task CanCreateUserNoPassword() + { + var manager = TestIdentityFactory.CreateManager(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(new InMemoryUser("CreateUserTest"))); + var user = await manager.FindByNameAsync("CreateUserTest"); + Assert.NotNull(user); + Assert.Null(user.PasswordHash); + var logins = await manager.GetLoginsAsync(user); + Assert.NotNull(logins); + Assert.Equal(0, logins.Count()); + } + + //[Fact] Disabled--see issue #107 + public async Task CanCreateUserAddLogin() + { + var manager = TestIdentityFactory.CreateManager(); + const string userName = "CreateExternalUserTest"; + const string provider = "ZzAuth"; + const string providerKey = "HaoKey"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(new InMemoryUser(userName))); + var user = await manager.FindByNameAsync(userName); + Assert.NotNull(user); + var login = new UserLoginInfo(provider, providerKey); + IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login)); + var logins = await manager.GetLoginsAsync(user); + Assert.NotNull(logins); + Assert.Equal(1, logins.Count()); + Assert.Equal(provider, logins.First().LoginProvider); + Assert.Equal(providerKey, logins.First().ProviderKey); + } + + //[Fact] Disabled--see issue #107 + public async Task CanCreateUserLoginAndAddPassword() + { + var manager = TestIdentityFactory.CreateManager(); + var login = new UserLoginInfo("Provider", "key"); + var user = new InMemoryUser("CreateUserLoginAddPasswordTest"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login)); + Assert.False(await manager.HasPasswordAsync(user)); + IdentityResultAssert.IsSuccess(await manager.AddPasswordAsync(user, "password")); + Assert.True(await manager.HasPasswordAsync(user)); + var logins = await manager.GetLoginsAsync(user); + Assert.NotNull(logins); + Assert.Equal(1, logins.Count()); + Assert.Equal(user, await manager.FindByLoginAsync(login)); + Assert.Equal(user, await manager.FindByUserNamePasswordAsync(user.UserName, "password")); + } + + [Fact] + public async Task AddPasswordFailsIfAlreadyHave() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("CannotAddAnotherPassword"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "Password")); + Assert.True(await manager.HasPasswordAsync(user)); + IdentityResultAssert.IsFailure(await manager.AddPasswordAsync(user, "password"), + "User already has a password set."); + } + + //[Fact] Disabled--see issue #107 + public async Task CanCreateUserAddRemoveLogin() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("CreateUserAddRemoveLoginTest"); + var login = new UserLoginInfo("Provider", "key"); + var result = await manager.CreateAsync(user); + Assert.NotNull(user); + IdentityResultAssert.IsSuccess(result); + IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login)); + Assert.Equal(user, await manager.FindByLoginAsync(login)); + var logins = await manager.GetLoginsAsync(user); + Assert.NotNull(logins); + Assert.Equal(1, logins.Count()); + Assert.Equal(login.LoginProvider, logins.Last().LoginProvider); + Assert.Equal(login.ProviderKey, logins.Last().ProviderKey); + var stamp = user.SecurityStamp; + IdentityResultAssert.IsSuccess(await manager.RemoveLoginAsync(user, login)); + Assert.Null(await manager.FindByLoginAsync(login)); + logins = await manager.GetLoginsAsync(user); + Assert.NotNull(logins); + Assert.Equal(0, logins.Count()); + Assert.NotEqual(stamp, user.SecurityStamp); + } + + [Fact] + public async Task CanRemovePassword() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("RemovePasswordTest"); + const string password = "password"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = user.SecurityStamp; + IdentityResultAssert.IsSuccess(await manager.RemovePasswordAsync(user)); + var u = await manager.FindByNameAsync(user.UserName); + Assert.NotNull(u); + Assert.Null(u.PasswordHash); + Assert.NotEqual(stamp, user.SecurityStamp); + } + + [Fact] + public async Task CanChangePassword() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("ChangePasswordTest"); + const string password = "password"; + const string newPassword = "newpassword"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + Assert.Equal(manager.Users.Count(), 1); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + IdentityResultAssert.IsSuccess(await manager.ChangePasswordAsync(user, password, newPassword)); + Assert.Null(await manager.FindByUserNamePasswordAsync(user.UserName, password)); + Assert.Equal(user, await manager.FindByUserNamePasswordAsync(user.UserName, newPassword)); + Assert.NotEqual(stamp, user.SecurityStamp); + } + + [Fact] + public async Task CanAddRemoveUserClaim() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("ClaimsAddRemove"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Claim[] claims = { new Claim("c", "v"), new Claim("c2", "v2"), new Claim("c2", "v3") }; + foreach (var c in claims) + { + IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c)); + } + var userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(3, userClaims.Count); + IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[0])); + userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(2, userClaims.Count); + IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[1])); + userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(1, userClaims.Count); + IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[2])); + userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(0, userClaims.Count); + } + + [Fact] + public async Task ChangePasswordFallsIfPasswordWrong() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("user"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); + var result = await manager.ChangePasswordAsync(user, "bogus", "newpassword"); + IdentityResultAssert.IsFailure(result, "Incorrect password."); + } + + [Fact] + public async Task AddDupeUserNameFails() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("dupe"); + var user2 = new InMemoryUser("dupe"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsFailure(await manager.CreateAsync(user2), "Name dupe is already taken."); + } + + [Fact] + public async Task AddDupeEmailAllowedByDefault() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("dupe") { Email = "yup@yup.com" }; + var user2 = new InMemoryUser("dupeEmail") { Email = "yup@yup.com" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2)); + } + + [Fact] + public async Task AddDupeEmailFallsWhenUniqueEmailRequired() + { + var manager = TestIdentityFactory.CreateManager(); + manager.Options.User.RequireUniqueEmail = true; + var user = new InMemoryUser("dupe") { Email = "yup@yup.com" }; + var user2 = new InMemoryUser("dupeEmail") { Email = "yup@yup.com" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsFailure(await manager.CreateAsync(user2), "Email 'yup@yup.com' is already taken."); + } + + [Fact] + public async Task UpdateSecurityStampActuallyChanges() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("stampMe"); + Assert.Null(user.SecurityStamp); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user)); + Assert.NotEqual(stamp, user.SecurityStamp); + } + + //[Fact] Disabled--see issue #107 + public async Task AddDupeLoginFails() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("DupeLogin"); + var login = new UserLoginInfo("provder", "key"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login)); + var result = await manager.AddLoginAsync(user, login); + IdentityResultAssert.IsFailure(result, "A user with that external login already exists."); + } + + // Email tests + [Fact] + public async Task CanFindByEmail() + { + var manager = TestIdentityFactory.CreateManager(); + const string userName = "EmailTest"; + const string email = "email@test.com"; + var user = new InMemoryUser(userName) { Email = email }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var fetch = await manager.FindByEmailAsync(email); + Assert.Equal(user, fetch); + } + + [Fact] + public async Task CanFindUsersViaUserQuerable() + { + var mgr = TestIdentityFactory.CreateManager(); + var users = new[] + { + new InMemoryUser("user1"), + new InMemoryUser("user2"), + new InMemoryUser("user3") + }; + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(u)); + } + var usersQ = mgr.Users; + Assert.Equal(3, usersQ.Count()); + Assert.NotNull(usersQ.FirstOrDefault(u => u.UserName == "user1")); + Assert.NotNull(usersQ.FirstOrDefault(u => u.UserName == "user2")); + Assert.NotNull(usersQ.FirstOrDefault(u => u.UserName == "user3")); + Assert.Null(usersQ.FirstOrDefault(u => u.UserName == "bogus")); + } + + [Fact] + public async Task ClaimsIdentityCreatesExpectedClaims() + { + var context = TestIdentityFactory.CreateContext(); + var manager = TestIdentityFactory.CreateManager(context); + var role = TestIdentityFactory.CreateRoleManager(context); + var user = new InMemoryUser("Hao"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await role.CreateAsync(new IdentityRole("Admin"))); + IdentityResultAssert.IsSuccess(await role.CreateAsync(new IdentityRole("Local"))); + IdentityResultAssert.IsSuccess(await manager.AddToRoleAsync(user, "Admin")); + IdentityResultAssert.IsSuccess(await manager.AddToRoleAsync(user, "Local")); + Claim[] userClaims = + { + new Claim("Whatever", "Value"), + new Claim("Whatever2", "Value2") + }; + foreach (var c in userClaims) + { + IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c)); + } + + var claimsFactory = new ClaimsIdentityFactory(manager, role); + var identity = await claimsFactory.CreateAsync(user, "test"); + var claims = identity.Claims.ToList(); + Assert.NotNull(claims); + Assert.True( + claims.Any(c => c.Type == manager.Options.ClaimType.UserName && c.Value == user.UserName)); + Assert.True(claims.Any(c => c.Type == manager.Options.ClaimType.UserId && c.Value == user.Id.ToString())); + Assert.True(claims.Any(c => c.Type == manager.Options.ClaimType.Role && c.Value == "Admin")); + Assert.True(claims.Any(c => c.Type == manager.Options.ClaimType.Role && c.Value == "Local")); + foreach (var cl in userClaims) + { + Assert.True(claims.Any(c => c.Type == cl.Type && c.Value == cl.Value)); + } + } + + [Fact] + public async Task ConfirmEmailFalseByDefaultTest() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("test"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + } + + // TODO: No token provider implementations yet + private class StaticTokenProvider : IUserTokenProvider + { + public Task GenerateAsync(string purpose, UserManager manager, + InMemoryUser user, CancellationToken token) + { + return Task.FromResult(MakeToken(purpose, user)); + } + + public Task ValidateAsync(string purpose, string token, UserManager manager, + InMemoryUser user, CancellationToken cancellationToken) + { + return Task.FromResult(token == MakeToken(purpose, user)); + } + + public Task NotifyAsync(string token, UserManager manager, InMemoryUser user, CancellationToken cancellationToken) + { + return Task.FromResult(0); + } + + public Task IsValidProviderForUserAsync(UserManager manager, InMemoryUser user, CancellationToken token) + { + return Task.FromResult(true); + } + + private static string MakeToken(string purpose, InMemoryUser user) + { + return string.Join(":", user.Id, purpose, "ImmaToken"); + } + } + + [Fact] + public async Task CanResetPasswordWithStaticTokenProvider() + { + var manager = TestIdentityFactory.CreateManager(); + manager.UserTokenProvider = new StaticTokenProvider(); + var user = new InMemoryUser("ResetPasswordTest"); + const string password = "password"; + const string newPassword = "newpassword"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + var token = await manager.GeneratePasswordResetTokenAsync(user); + Assert.NotNull(token); + IdentityResultAssert.IsSuccess(await manager.ResetPasswordAsync(user, token, newPassword)); + Assert.Null(await manager.FindByUserNamePasswordAsync(user.UserName, password)); + Assert.Equal(user, await manager.FindByUserNamePasswordAsync(user.UserName, newPassword)); + Assert.NotEqual(stamp, user.SecurityStamp); + } + + [Fact] + public async Task PasswordValidatorCanBlockResetPasswordWithStaticTokenProvider() + { + var manager = TestIdentityFactory.CreateManager(); + manager.UserTokenProvider = new StaticTokenProvider(); + var user = new InMemoryUser("ResetPasswordTest"); + const string password = "password"; + const string newPassword = "newpassword"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + var token = await manager.GeneratePasswordResetTokenAsync(user); + Assert.NotNull(token); + manager.PasswordValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.ResetPasswordAsync(user, token, newPassword), + AlwaysBadValidator.ErrorMessage); + Assert.NotNull(await manager.FindByUserNamePasswordAsync(user.UserName, password)); + Assert.Equal(user, await manager.FindByUserNamePasswordAsync(user.UserName, password)); + Assert.Equal(stamp, user.SecurityStamp); + } + + [Fact] + public async Task ResetPasswordWithStaticTokenProviderFailsWithWrongToken() + { + var manager = TestIdentityFactory.CreateManager(); + manager.UserTokenProvider = new StaticTokenProvider(); + var user = new InMemoryUser("ResetPasswordTest"); + const string password = "password"; + const string newPassword = "newpassword"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + IdentityResultAssert.IsFailure(await manager.ResetPasswordAsync(user, "bogus", newPassword), "Invalid token."); + Assert.NotNull(await manager.FindByUserNamePasswordAsync(user.UserName, password)); + Assert.Equal(user, await manager.FindByUserNamePasswordAsync(user.UserName, password)); + Assert.Equal(stamp, user.SecurityStamp); + } + + [Fact] + public async Task CanGenerateAndVerifyUserTokenWithStaticTokenProvider() + { + var manager = TestIdentityFactory.CreateManager(); + manager.UserTokenProvider = new StaticTokenProvider(); + var user = new InMemoryUser("UserTokenTest"); + var user2 = new InMemoryUser("UserTokenTest2"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2)); + var token = await manager.GenerateUserTokenAsync("test", user); + Assert.True(await manager.VerifyUserTokenAsync(user, "test", token)); + Assert.False(await manager.VerifyUserTokenAsync(user, "test2", token)); + Assert.False(await manager.VerifyUserTokenAsync(user, "test", token + "a")); + Assert.False(await manager.VerifyUserTokenAsync(user2, "test", token)); + } + + [Fact] + public async Task CanConfirmEmailWithStaticToken() + { + var manager = TestIdentityFactory.CreateManager(); + manager.UserTokenProvider = new StaticTokenProvider(); + var user = new InMemoryUser("test"); + Assert.False(user.EmailConfirmed); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var token = await manager.GenerateEmailConfirmationTokenAsync(user); + Assert.NotNull(token); + IdentityResultAssert.IsSuccess(await manager.ConfirmEmailAsync(user, token)); + Assert.True(await manager.IsEmailConfirmedAsync(user)); + IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, null)); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + } + + [Fact] + public async Task ConfirmEmailWithStaticTokenFailsWithWrongToken() + { + var manager = TestIdentityFactory.CreateManager(); + manager.UserTokenProvider = new StaticTokenProvider(); + var user = new InMemoryUser("test"); + Assert.False(user.EmailConfirmed); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsFailure(await manager.ConfirmEmailAsync(user, "bogus"), "Invalid token."); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + } + + // TODO: Can't reenable til we have a SecurityStamp linked token provider + //[Fact] + //public async Task ConfirmTokenFailsAfterPasswordChange() + //{ + // var manager = TestIdentityFactory.CreateManager(); + // var user = new InMemoryUser("test"); + // Assert.False(user.EmailConfirmed); + // IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); + // var token = await manager.GenerateEmailConfirmationTokenAsync(user); + // Assert.NotNull(token); + // IdentityResultAssert.IsSuccess(await manager.ChangePasswordAsync(user, "password", "newpassword")); + // IdentityResultAssert.IsFailure(await manager.ConfirmEmailAsync(user, token), "Invalid token."); + // Assert.False(await manager.IsEmailConfirmedAsync(user)); + //} + + // Lockout tests + + [Fact] + public async Task SingleFailureLockout() + { + var mgr = TestIdentityFactory.CreateManager(); + mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); + mgr.Options.Lockout.EnabledByDefault = true; + mgr.Options.Lockout.MaxFailedAccessAttempts = 0; + var user = new InMemoryUser("fastLockout"); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + Assert.False(await mgr.IsLockedOutAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.True(await mgr.IsLockedOutAsync(user)); + Assert.True(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user)); + } + + [Fact] + public async Task TwoFailureLockout() + { + var mgr = TestIdentityFactory.CreateManager(); + mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); + mgr.Options.Lockout.EnabledByDefault = true; + mgr.Options.Lockout.MaxFailedAccessAttempts = 2; + var user = new InMemoryUser("twoFailureLockout"); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + Assert.False(await mgr.IsLockedOutAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.True(await mgr.IsLockedOutAsync(user)); + Assert.True(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user)); + } + + [Fact] + public async Task ResetAccessCountPreventsLockout() + { + var mgr = TestIdentityFactory.CreateManager(); + mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); + mgr.Options.Lockout.EnabledByDefault = true; + mgr.Options.Lockout.MaxFailedAccessAttempts = 2; + var user = new InMemoryUser("resetLockout"); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + Assert.False(await mgr.IsLockedOutAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.ResetAccessFailedCountAsync(user)); + Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user)); + } + + [Fact] + public async Task CanEnableLockoutManuallyAndLockout() + { + var mgr = TestIdentityFactory.CreateManager(); + mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); + mgr.Options.Lockout.MaxFailedAccessAttempts = 2; + var user = new InMemoryUser("manualLockout"); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.False(await mgr.GetLockoutEnabledAsync(user)); + Assert.False(user.LockoutEnabled); + IdentityResultAssert.IsSuccess(await mgr.SetLockoutEnabledAsync(user, true)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + Assert.False(await mgr.IsLockedOutAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.True(await mgr.IsLockedOutAsync(user)); + Assert.True(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user)); + } + + [Fact] + public async Task UserNotLockedOutWithNullDateTimeAndIsSetToNullDate() + { + var mgr = TestIdentityFactory.CreateManager(); + mgr.Options.Lockout.EnabledByDefault = true; + var user = new InMemoryUser("LockoutTest"); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + IdentityResultAssert.IsSuccess(await mgr.SetLockoutEndDateAsync(user, new DateTimeOffset())); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.Equal(new DateTimeOffset(), await mgr.GetLockoutEndDateAsync(user)); + Assert.Null(user.LockoutEnd); + } + + [Fact] + public async Task LockoutFailsIfNotEnabled() + { + var mgr = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("LockoutNotEnabledTest"); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.False(await mgr.GetLockoutEnabledAsync(user)); + Assert.False(user.LockoutEnabled); + IdentityResultAssert.IsFailure(await mgr.SetLockoutEndDateAsync(user, new DateTimeOffset()), + "Lockout is not enabled for this user."); + Assert.False(await mgr.IsLockedOutAsync(user)); + } + + [Fact] + public async Task LockoutEndToUtcNowMinus1SecInUserShouldNotBeLockedOut() + { + var mgr = TestIdentityFactory.CreateManager(); + mgr.Options.Lockout.EnabledByDefault = true; + var user = new InMemoryUser("LockoutUtcNowTest") { LockoutEnd = DateTime.UtcNow.AddSeconds(-1) }; + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + Assert.False(await mgr.IsLockedOutAsync(user)); + } + + [Fact] + public async Task LockoutEndToUtcNowSubOneSecondWithManagerShouldNotBeLockedOut() + { + var mgr = TestIdentityFactory.CreateManager(); + mgr.Options.Lockout.EnabledByDefault = true; + var user = new InMemoryUser("LockoutUtcNowTest"); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + IdentityResultAssert.IsSuccess(await mgr.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.AddSeconds(-1))); + Assert.False(await mgr.IsLockedOutAsync(user)); + } + + [Fact] + public async Task LockoutEndToUtcNowPlus5ShouldBeLockedOut() + { + var mgr = TestIdentityFactory.CreateManager(); + mgr.Options.Lockout.EnabledByDefault = true; + var user = new InMemoryUser("LockoutUtcNowTest") { LockoutEnd = DateTime.UtcNow.AddMinutes(5) }; + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + Assert.True(await mgr.IsLockedOutAsync(user)); + } + + [Fact] + public async Task UserLockedOutWithDateTimeLocalKindNowPlus30() + { + var mgr = TestIdentityFactory.CreateManager(); + mgr.Options.Lockout.EnabledByDefault = true; + var user = new InMemoryUser("LockoutTest"); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + var lockoutEnd = new DateTimeOffset(DateTime.Now.AddMinutes(30).ToLocalTime()); + IdentityResultAssert.IsSuccess(await mgr.SetLockoutEndDateAsync(user, lockoutEnd)); + Assert.True(await mgr.IsLockedOutAsync(user)); + var end = await mgr.GetLockoutEndDateAsync(user); + Assert.Equal(lockoutEnd, end); + } + + // Role Tests + [Fact] + public async Task CanCreateRoleTest() + { + var manager = TestIdentityFactory.CreateRoleManager(); + var role = new IdentityRole("create"); + Assert.False(await manager.RoleExistsAsync(role.Name)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Assert.True(await manager.RoleExistsAsync(role.Name)); + } + + private class AlwaysBadValidator : IUserValidator, IRoleValidator, + IPasswordValidator + { + public const string ErrorMessage = "I'm Bad."; + + public Task ValidateAsync(string password, UserManager manager,CancellationToken token) + { + return Task.FromResult(IdentityResult.Failed(ErrorMessage)); + } + + public Task ValidateAsync(RoleManager manager, IdentityRole role, CancellationToken token) + { + return Task.FromResult(IdentityResult.Failed(ErrorMessage)); + } + + public Task ValidateAsync(UserManager manager, InMemoryUser user, CancellationToken token) + { + return Task.FromResult(IdentityResult.Failed(ErrorMessage)); + } + } + + [Fact] + public async Task BadValidatorBlocksCreateRole() + { + var manager = TestIdentityFactory.CreateRoleManager(); + manager.RoleValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.CreateAsync(new IdentityRole("blocked")), + AlwaysBadValidator.ErrorMessage); + } + + [Fact] + public async Task BadValidatorBlocksRoleUpdate() + { + var manager = TestIdentityFactory.CreateRoleManager(); + var role = new IdentityRole("poorguy"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + var error = AlwaysBadValidator.ErrorMessage; + manager.RoleValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.UpdateAsync(role), error); + } + + [Fact] + public async Task CanDeleteRoleTest() + { + var manager = TestIdentityFactory.CreateRoleManager(); + var role = new IdentityRole("delete"); + Assert.False(await manager.RoleExistsAsync(role.Name)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + IdentityResultAssert.IsSuccess(await manager.DeleteAsync(role)); + Assert.False(await manager.RoleExistsAsync(role.Name)); + } + + [Fact] + public async Task CanRoleFindByIdTest() + { + var manager = TestIdentityFactory.CreateRoleManager(); + var role = new IdentityRole("FindByIdAsync"); + Assert.Null(await manager.FindByIdAsync(role.Id)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Assert.Equal(role, await manager.FindByIdAsync(role.Id)); + } + + [Fact] + public async Task CanRoleFindByName() + { + var manager = TestIdentityFactory.CreateRoleManager(); + var role = new IdentityRole("FindByNameAsync"); + Assert.Null(await manager.FindByNameAsync(role.Name)); + Assert.False(await manager.RoleExistsAsync(role.Name)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Assert.Equal(role, await manager.FindByNameAsync(role.Name)); + } + + [Fact] + public async Task CanUpdateRoleNameTest() + { + var manager = TestIdentityFactory.CreateRoleManager(); + var role = new IdentityRole("update"); + Assert.False(await manager.RoleExistsAsync(role.Name)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Assert.True(await manager.RoleExistsAsync(role.Name)); + role.Name = "Changed"; + IdentityResultAssert.IsSuccess(await manager.UpdateAsync(role)); + Assert.False(await manager.RoleExistsAsync("update")); + Assert.Equal(role, await manager.FindByNameAsync(role.Name)); + } + + [Fact] + public async Task CanQuerableRolesTest() + { + var manager = TestIdentityFactory.CreateRoleManager(); + IdentityRole[] roles = + { + new IdentityRole("r1"), new IdentityRole("r2"), new IdentityRole("r3"), + new IdentityRole("r4") + }; + foreach (var r in roles) + { + IdentityResultAssert.IsSuccess(await manager.CreateAsync(r)); + } + Assert.Equal(roles.Length, manager.Roles.Count()); + var r1 = manager.Roles.FirstOrDefault(r => r.Name == "r1"); + Assert.Equal(roles[0], r1); + } + + //[Fact] + //public async Task DeleteRoleNonEmptySucceedsTest() + //{ + // // Need fail if not empty? + // var userMgr = TestIdentityFactory.CreateManager(); + // var roleMgr = TestIdentityFactory.CreateRoleManager(); + // var role = new IdentityRole("deleteNonEmpty"); + // Assert.False(await roleMgr.RoleExistsAsync(role.Name)); + // IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + // var user = new InMemoryUser("t"); + // IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user)); + // IdentityResultAssert.IsSuccess(await userMgr.AddToRoleAsync(user, role.Name)); + // IdentityResultAssert.IsSuccess(await roleMgr.DeleteAsync(role)); + // Assert.Null(await roleMgr.FindByNameAsync(role.Name)); + // Assert.False(await roleMgr.RoleExistsAsync(role.Name)); + // // REVIEW: We should throw if deleteing a non empty role? + // var roles = await userMgr.GetRolesAsync(user); + + // // In memory this doesn't work since there's no concept of cascading deletes + // //Assert.Equal(0, roles.Count()); + //} + + ////[Fact] + ////public async Task DeleteUserRemovesFromRoleTest() + ////{ + //// // Need fail if not empty? + //// var userMgr = TestIdentityFactory.CreateManager(); + //// var roleMgr = TestIdentityFactory.CreateRoleManager(); + //// var role = new IdentityRole("deleteNonEmpty"); + //// Assert.False(await roleMgr.RoleExistsAsync(role.Name)); + //// IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + //// var user = new InMemoryUser("t"); + //// IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user)); + //// IdentityResultAssert.IsSuccess(await userMgr.AddToRoleAsync(user, role.Name)); + //// IdentityResultAssert.IsSuccess(await userMgr.DeleteAsync(user)); + //// role = roleMgr.FindByIdAsync(role.Id); + ////} + + [Fact] + public async Task CreateRoleFailsIfExists() + { + var manager = TestIdentityFactory.CreateRoleManager(); + var role = new IdentityRole("dupeRole"); + Assert.False(await manager.RoleExistsAsync(role.Name)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Assert.True(await manager.RoleExistsAsync(role.Name)); + var role2 = new IdentityRole("dupeRole"); + IdentityResultAssert.IsFailure(await manager.CreateAsync(role2)); + } + + [Fact] + public async Task CanAddUsersToRole() + { + var context = TestIdentityFactory.CreateContext(); + var manager = TestIdentityFactory.CreateManager(context); + var roleManager = TestIdentityFactory.CreateRoleManager(context); + var role = new IdentityRole("addUserTest"); + IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(role)); + InMemoryUser[] users = + { + new InMemoryUser("1"), new InMemoryUser("2"), new InMemoryUser("3"), + new InMemoryUser("4") + }; + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await manager.CreateAsync(u)); + IdentityResultAssert.IsSuccess(await manager.AddToRoleAsync(u, role.Name)); + Assert.True(await manager.IsInRoleAsync(u, role.Name)); + } + } + + [Fact] + public async Task CanGetRolesForUser() + { + var context = TestIdentityFactory.CreateContext(); + var userManager = TestIdentityFactory.CreateManager(context); + var roleManager = TestIdentityFactory.CreateRoleManager(context); + InMemoryUser[] users = + { + new InMemoryUser("u1"), new InMemoryUser("u2"), new InMemoryUser("u3"), + new InMemoryUser("u4") + }; + IdentityRole[] roles = + { + new IdentityRole("r1"), new IdentityRole("r2"), new IdentityRole("r3"), + new IdentityRole("r4") + }; + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await userManager.CreateAsync(u)); + } + foreach (var r in roles) + { + IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(r)); + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await userManager.AddToRoleAsync(u, r.Name)); + Assert.True(await userManager.IsInRoleAsync(u, r.Name)); + } + } + + foreach (var u in users) + { + var rs = await userManager.GetRolesAsync(u); + Assert.Equal(roles.Length, rs.Count); + foreach (var r in roles) + { + Assert.True(rs.Any(role => role == r.Name)); + } + } + } + + + [Fact] + public async Task RemoveUserFromRoleWithMultipleRoles() + { + var context = TestIdentityFactory.CreateContext(); + var userManager = TestIdentityFactory.CreateManager(context); + var roleManager = TestIdentityFactory.CreateRoleManager(context); + var user = new InMemoryUser("MultiRoleUser"); + IdentityResultAssert.IsSuccess(await userManager.CreateAsync(user)); + IdentityRole[] roles = + { + new IdentityRole("r1"), new IdentityRole("r2"), new IdentityRole("r3"), + new IdentityRole("r4") + }; + foreach (var r in roles) + { + IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(r)); + IdentityResultAssert.IsSuccess(await userManager.AddToRoleAsync(user, r.Name)); + Assert.True(await userManager.IsInRoleAsync(user, r.Name)); + } + IdentityResultAssert.IsSuccess(await userManager.RemoveFromRoleAsync(user, roles[2].Name)); + Assert.False(await userManager.IsInRoleAsync(user, roles[2].Name)); + } + + [Fact] + public async Task CanRemoveUsersFromRole() + { + var context = TestIdentityFactory.CreateContext(); + var userManager = TestIdentityFactory.CreateManager(context); + var roleManager = TestIdentityFactory.CreateRoleManager(context); + InMemoryUser[] users = + { + new InMemoryUser("1"), new InMemoryUser("2"), new InMemoryUser("3"), + new InMemoryUser("4") + }; + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await userManager.CreateAsync(u)); + } + var r = new IdentityRole("r1"); + IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(r)); + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await userManager.AddToRoleAsync(u, r.Name)); + Assert.True(await userManager.IsInRoleAsync(u, r.Name)); + } + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await userManager.RemoveFromRoleAsync(u, r.Name)); + Assert.False(await userManager.IsInRoleAsync(u, r.Name)); + } + } + + [Fact] + public async Task RemoveUserNotInRoleFails() + { + var context = TestIdentityFactory.CreateContext(); + var userMgr = TestIdentityFactory.CreateManager(context); + var roleMgr = TestIdentityFactory.CreateRoleManager(context); + var role = new IdentityRole("addUserDupeTest"); + var user = new InMemoryUser("user1"); + IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + var result = await userMgr.RemoveFromRoleAsync(user, role.Name); + IdentityResultAssert.IsFailure(result, "User is not in role."); + } + + [Fact] + public async Task AddUserToUnknownRoleFails() + { + var manager = TestIdentityFactory.CreateManager(); + var u = new InMemoryUser("u1"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(u)); + await Assert.ThrowsAsync( + async () => await manager.AddToRoleAsync(u, "bogus")); + } + + [Fact] + public async Task AddUserToRoleFailsIfAlreadyInRole() + { + var context = TestIdentityFactory.CreateContext(); + var userMgr = TestIdentityFactory.CreateManager(context); + var roleMgr = TestIdentityFactory.CreateRoleManager(context); + var role = new IdentityRole("addUserDupeTest"); + var user = new InMemoryUser("user1"); + IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + IdentityResultAssert.IsSuccess(await userMgr.AddToRoleAsync(user, role.Name)); + Assert.True(await userMgr.IsInRoleAsync(user, role.Name)); + IdentityResultAssert.IsFailure(await userMgr.AddToRoleAsync(user, role.Name), "User already in role."); + } + + [Fact] + public async Task CanFindRoleByNameWithManager() + { + var roleMgr = TestIdentityFactory.CreateRoleManager(); + var role = new IdentityRole("findRoleByNameTest"); + IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + Assert.Equal(role.Id, (await roleMgr.FindByNameAsync(role.Name)).Id); + } + + [Fact] + public async Task CanFindRoleWithManager() + { + var roleMgr = TestIdentityFactory.CreateRoleManager(); + var role = new IdentityRole("findRoleTest"); + IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + Assert.Equal(role, await roleMgr.FindByIdAsync(role.Id)); + } + + [Fact] + public async Task SetPhoneNumberTest() + { + var manager = TestIdentityFactory.CreateManager(); + const string userName = "PhoneTest"; + var user = new InMemoryUser(userName) { PhoneNumber = "123-456-7890" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + Assert.Equal(await manager.GetPhoneNumberAsync(user), "123-456-7890"); + IdentityResultAssert.IsSuccess(await manager.SetPhoneNumberAsync(user, "111-111-1111")); + Assert.Equal(await manager.GetPhoneNumberAsync(user), "111-111-1111"); + Assert.NotEqual(stamp, user.SecurityStamp); + } + +#if NET45 + [Fact] + public async Task CanChangePhoneNumber() + { + var manager = TestIdentityFactory.CreateManager(); + const string userName = "PhoneTest"; + var user = new InMemoryUser(userName) { PhoneNumber = "123-456-7890" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.IsPhoneNumberConfirmedAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + var token1 = await manager.GenerateChangePhoneNumberTokenAsync(user, "111-111-1111"); + IdentityResultAssert.IsSuccess(await manager.ChangePhoneNumberAsync(user, "111-111-1111", token1)); + Assert.True(await manager.IsPhoneNumberConfirmedAsync(user)); + Assert.Equal(await manager.GetPhoneNumberAsync(user), "111-111-1111"); + Assert.NotEqual(stamp, user.SecurityStamp); + } + + [Fact] + public async Task ChangePhoneNumberFailsWithWrongToken() + { + var manager = TestIdentityFactory.CreateManager(); + const string userName = "PhoneTest"; + var user = new InMemoryUser(userName) { PhoneNumber = "123-456-7890" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.IsPhoneNumberConfirmedAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + IdentityResultAssert.IsFailure(await manager.ChangePhoneNumberAsync(user, "111-111-1111", "bogus"), + "Invalid token."); + Assert.False(await manager.IsPhoneNumberConfirmedAsync(user)); + Assert.Equal(await manager.GetPhoneNumberAsync(user), "123-456-7890"); + Assert.Equal(stamp, user.SecurityStamp); + } + + [Fact] + public async Task CanVerifyPhoneNumber() + { + var manager = TestIdentityFactory.CreateManager(); + const string userName = "VerifyPhoneTest"; + var user = new InMemoryUser(userName); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + const string num1 = "111-123-4567"; + const string num2 = "111-111-1111"; + var token1 = await manager.GenerateChangePhoneNumberTokenAsync(user, num1); + var token2 = await manager.GenerateChangePhoneNumberTokenAsync(user, num2); + Assert.NotEqual(token1, token2); + Assert.True(await manager.VerifyChangePhoneNumberTokenAsync(user, token1, num1)); + Assert.True(await manager.VerifyChangePhoneNumberTokenAsync(user, token2, num2)); + Assert.False(await manager.VerifyChangePhoneNumberTokenAsync(user, token2, num1)); + Assert.False(await manager.VerifyChangePhoneNumberTokenAsync(user, token1, num2)); + } +#endif + + private class EmailTokenProvider : IUserTokenProvider + { + public Task GenerateAsync(string purpose, UserManager manager, InMemoryUser user, CancellationToken token) + { + return Task.FromResult(MakeToken(purpose)); + } + + public Task ValidateAsync(string purpose, string token, UserManager manager, + InMemoryUser user, CancellationToken cancellationToken) + { + return Task.FromResult(token == MakeToken(purpose)); + } + + public Task NotifyAsync(string token, UserManager manager, InMemoryUser user, CancellationToken cancellationToken) + { + return manager.SendEmailAsync(user, token, token); + } + + public async Task IsValidProviderForUserAsync(UserManager manager, InMemoryUser user, CancellationToken token) + { + return !string.IsNullOrEmpty(await manager.GetEmailAsync(user)); + } + + private static string MakeToken(string purpose) + { + return "email:" + purpose; + } + } + + private class SmsTokenProvider : IUserTokenProvider + { + public Task GenerateAsync(string purpose, UserManager manager, InMemoryUser user, CancellationToken token) + { + return Task.FromResult(MakeToken(purpose)); + } + + public Task ValidateAsync(string purpose, string token, UserManager manager, + InMemoryUser user, CancellationToken cancellationToken) + { + return Task.FromResult(token == MakeToken(purpose)); + } + + public Task NotifyAsync(string token, UserManager manager, InMemoryUser user, CancellationToken cancellationToken) + { + return manager.SendSmsAsync(user, token); + } + + public async Task IsValidProviderForUserAsync(UserManager manager, InMemoryUser user, CancellationToken token) + { + return !string.IsNullOrEmpty(await manager.GetPhoneNumberAsync(user)); + } + + private static string MakeToken(string purpose) + { + return "sms:" + purpose; + } + } + + [Fact] + public async Task CanEmailTwoFactorToken() + { + var manager = TestIdentityFactory.CreateManager(); + var messageService = new TestMessageService(); + manager.EmailService = messageService; + const string factorId = "EmailCode"; + manager.RegisterTwoFactorProvider(factorId, new EmailTokenProvider()); + var user = new InMemoryUser("EmailCodeTest") { Email = "foo@foo.com" }; + const string password = "password"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + var token = await manager.GenerateTwoFactorTokenAsync(user, factorId); + Assert.NotNull(token); + Assert.Null(messageService.Message); + IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token)); + Assert.NotNull(messageService.Message); + Assert.Equal(token, messageService.Message.Subject); + Assert.Equal(token, messageService.Message.Body); + Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token)); + } + + [Fact] + public async Task NotifyWithUnknownProviderFails() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("NotifyFail"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + await + ExceptionAssert.ThrowsAsync( + async () => await manager.NotifyTwoFactorTokenAsync(user, "Bogus", "token"), + "No IUserTwoFactorProvider for 'Bogus' is registered."); + } + + + //[Fact] + //public async Task EmailTokenFactorWithFormatTest() + //{ + // var manager = TestIdentityFactory.CreateManager(); + // var messageService = new TestMessageService(); + // manager.EmailService = messageService; + // const string factorId = "EmailCode"; + // manager.RegisterTwoFactorProvider(factorId, new EmailTokenProvider + // { + // Subject = "Security Code", + // BodyFormat = "Your code is: {0}" + // }); + // var user = new InMemoryUser("EmailCodeTest") { Email = "foo@foo.com" }; + // const string password = "password"; + // IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + // var stamp = user.SecurityStamp; + // Assert.NotNull(stamp); + // var token = await manager.GenerateTwoFactorTokenAsync(user, factorId); + // Assert.NotNull(token); + // Assert.Null(messageService.Message); + // IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token)); + // Assert.NotNull(messageService.Message); + // Assert.Equal("Security Code", messageService.Message.Subject); + // Assert.Equal("Your code is: " + token, messageService.Message.Body); + // Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token)); + //} + + //[Fact] + //public async Task EmailFactorFailsAfterSecurityStampChangeTest() + //{ + // var manager = TestIdentityFactory.CreateManager(); + // const string factorId = "EmailCode"; + // manager.RegisterTwoFactorProvider(factorId, new EmailTokenProvider()); + // var user = new InMemoryUser("EmailCodeTest") { Email = "foo@foo.com" }; + // IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + // var stamp = user.SecurityStamp; + // Assert.NotNull(stamp); + // var token = await manager.GenerateTwoFactorTokenAsync(user, factorId); + // Assert.NotNull(token); + // IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user)); + // Assert.False(await manager.VerifyTwoFactorTokenAsync(user, factorId, token)); + //} + + [Fact] + public async Task EnableTwoFactorChangesSecurityStamp() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("TwoFactorEnabledTest"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + IdentityResultAssert.IsSuccess(await manager.SetTwoFactorEnabledAsync(user, true)); + Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); + Assert.True(await manager.GetTwoFactorEnabledAsync(user)); + } + + [Fact] + public async Task CanSendSms() + { + var manager = TestIdentityFactory.CreateManager(); + var messageService = new TestMessageService(); + manager.SmsService = messageService; + var user = new InMemoryUser("SmsTest") { PhoneNumber = "4251234567" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + await manager.SendSmsAsync(user, "Hi"); + Assert.NotNull(messageService.Message); + Assert.Equal("Hi", messageService.Message.Body); + } + + [Fact] + public async Task CanSendEmail() + { + var manager = TestIdentityFactory.CreateManager(); + var messageService = new TestMessageService(); + manager.EmailService = messageService; + var user = new InMemoryUser("EmailTest") { Email = "foo@foo.com" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + await manager.SendEmailAsync(user, "Hi", "Body"); + Assert.NotNull(messageService.Message); + Assert.Equal("Hi", messageService.Message.Subject); + Assert.Equal("Body", messageService.Message.Body); + } + + [Fact] + public async Task CanSmsTwoFactorToken() + { + var manager = TestIdentityFactory.CreateManager(); + var messageService = new TestMessageService(); + manager.SmsService = messageService; + const string factorId = "PhoneCode"; + manager.RegisterTwoFactorProvider(factorId, new SmsTokenProvider()); + var user = new InMemoryUser("PhoneCodeTest") { PhoneNumber = "4251234567" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + var token = await manager.GenerateTwoFactorTokenAsync(user, factorId); + Assert.NotNull(token); + Assert.Null(messageService.Message); + IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token)); + Assert.NotNull(messageService.Message); + Assert.Equal(token, messageService.Message.Body); + Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token)); + } + + //[Fact] + //public async Task PhoneTokenFactorFormatTest() + //{ + // var manager = TestIdentityFactory.CreateManager(); + // var messageService = new TestMessageService(); + // manager.SmsService = messageService; + // const string factorId = "PhoneCode"; + // manager.RegisterTwoFactorProvider(factorId, new PhoneNumberTokenProvider + // { + // MessageFormat = "Your code is: {0}" + // }); + // var user = new InMemoryUser("PhoneCodeTest") { PhoneNumber = "4251234567" }; + // IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + // var stamp = user.SecurityStamp; + // Assert.NotNull(stamp); + // var token = await manager.GenerateTwoFactorTokenAsync(user, factorId); + // Assert.NotNull(token); + // Assert.Null(messageService.Message); + // IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token)); + // Assert.NotNull(messageService.Message); + // Assert.Equal("Your code is: " + token, messageService.Message.Body); + // Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token)); + //} + + [Fact] + public async Task GenerateTwoFactorWithUnknownFactorProviderWillThrow() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("PhoneCodeTest"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + const string error = "No IUserTwoFactorProvider for 'bogus' is registered."; + await + ExceptionAssert.ThrowsAsync( + () => manager.GenerateTwoFactorTokenAsync(user, "bogus"), error); + await ExceptionAssert.ThrowsAsync( + () => manager.VerifyTwoFactorTokenAsync(user, "bogus", "bogus"), error); + } + + [Fact] + public async Task GetValidTwoFactorTestEmptyWithNoProviders() + { + var manager = TestIdentityFactory.CreateManager(); + var user = new InMemoryUser("test"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var factors = await manager.GetValidTwoFactorProvidersAsync(user); + Assert.NotNull(factors); + Assert.True(!factors.Any()); + } + + [Fact] + public async Task GetValidTwoFactorTest() + { + var manager = TestIdentityFactory.CreateManager(); + manager.RegisterTwoFactorProvider("phone", new SmsTokenProvider()); + manager.RegisterTwoFactorProvider("email", new EmailTokenProvider()); + var user = new InMemoryUser("test"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var factors = await manager.GetValidTwoFactorProvidersAsync(user); + Assert.NotNull(factors); + Assert.True(!factors.Any()); + IdentityResultAssert.IsSuccess(await manager.SetPhoneNumberAsync(user, "111-111-1111")); + factors = await manager.GetValidTwoFactorProvidersAsync(user); + Assert.NotNull(factors); + Assert.True(factors.Count() == 1); + Assert.Equal("phone", factors[0]); + IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, "test@test.com")); + factors = await manager.GetValidTwoFactorProvidersAsync(user); + Assert.NotNull(factors); + Assert.True(factors.Count() == 2); + IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, null)); + factors = await manager.GetValidTwoFactorProvidersAsync(user); + Assert.NotNull(factors); + Assert.True(factors.Count() == 1); + Assert.Equal("phone", factors[0]); + } + + //[Fact] + //public async Task PhoneFactorFailsAfterSecurityStampChangeTest() + //{ + // var manager = TestIdentityFactory.CreateManager(); + // var factorId = "PhoneCode"; + // manager.RegisterTwoFactorProvider(factorId, new PhoneNumberTokenProvider()); + // var user = new InMemoryUser("PhoneCodeTest"); + // user.PhoneNumber = "4251234567"; + // IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + // var stamp = user.SecurityStamp; + // Assert.NotNull(stamp); + // var token = await manager.GenerateTwoFactorTokenAsync(user, factorId); + // Assert.NotNull(token); + // IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user)); + // Assert.False(await manager.VerifyTwoFactorTokenAsync(user, factorId, token)); + //} + + [Fact] + public async Task VerifyTokenFromWrongTokenProviderFails() + { + var manager = TestIdentityFactory.CreateManager(); + manager.RegisterTwoFactorProvider("PhoneCode", new SmsTokenProvider()); + manager.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider()); + var user = new InMemoryUser("WrongTokenProviderTest") { PhoneNumber = "4251234567" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var token = await manager.GenerateTwoFactorTokenAsync(user, "PhoneCode"); + Assert.NotNull(token); + Assert.False(await manager.VerifyTwoFactorTokenAsync(user, "EmailCode", token)); + } + + [Fact] + public async Task VerifyWithWrongSmsTokenFails() + { + var manager = TestIdentityFactory.CreateManager(); + const string factorId = "PhoneCode"; + manager.RegisterTwoFactorProvider(factorId, new SmsTokenProvider()); + var user = new InMemoryUser("PhoneCodeTest") { PhoneNumber = "4251234567" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.VerifyTwoFactorTokenAsync(user, factorId, "bogus")); + } + + public class TestMessageService : IIdentityMessageService + { + public IdentityMessage Message { get; set; } + + public Task SendAsync(IdentityMessage message, CancellationToken token) + { + Message = message; + return Task.FromResult(0); + } + } + } +} diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test.kproj b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test.kproj new file mode 100644 index 0000000000..399135e972 --- /dev/null +++ b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test.kproj @@ -0,0 +1,33 @@ + + + + 12.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 813b36fe-bba5-4449-b157-6ebba5ed02ca + Library + net45 + + + + + + + 2.0 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/RoleStoreTest.cs b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/RoleStoreTest.cs new file mode 100644 index 0000000000..5c216147c4 --- /dev/null +++ b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/RoleStoreTest.cs @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; +using Microsoft.AspNet.Identity.Test; +using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.DependencyInjection.Fallback; +using Xunit; + +namespace Microsoft.AspNet.Identity.EntityFramework.InMemory.Test +{ + public class RoleStoreTest + { + [Fact] + public async Task CanCreateUsingAddRoleManager() + { + var services = new ServiceCollection(); + services.AddEntityFramework().AddInMemoryStore(); + var store = new RoleStore(new InMemoryContext()); + services.AddIdentity(s => + { + s.AddRoleStore(() => store); + }); + var provider = services.BuildServiceProvider(); + var manager = provider.GetService>(); + Assert.NotNull(manager); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(new IdentityRole("arole"))); + } + [Fact] + public async Task CanCreateRoleWithSingletonManager() + { + var services = new ServiceCollection(); + services.AddEntityFramework().AddInMemoryStore(); + services.AddTransient(); + services.AddTransient, RoleStore>(); + services.AddTransient, RoleValidator>(); + services.AddSingleton>(); + var provider = services.BuildServiceProvider(); + var manager = provider.GetService>(); + Assert.NotNull(manager); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(new IdentityRole("someRole"))); + } + + [Fact] + public async Task RoleStoreMethodsThrowWhenDisposedTest() + { + var store = new RoleStore(new InMemoryContext()); + store.Dispose(); + await Assert.ThrowsAsync(async () => await store.FindByIdAsync(null)); + await Assert.ThrowsAsync(async () => await store.FindByNameAsync(null)); + await Assert.ThrowsAsync(async () => await store.GetRoleIdAsync(null)); + await Assert.ThrowsAsync(async () => await store.GetRoleNameAsync(null)); + await Assert.ThrowsAsync(async () => await store.SetRoleNameAsync(null, null)); + await Assert.ThrowsAsync(async () => await store.CreateAsync(null)); + await Assert.ThrowsAsync(async () => await store.UpdateAsync(null)); + await Assert.ThrowsAsync(async () => await store.DeleteAsync(null)); + } + + [Fact] + public async Task RoleStorePublicNullCheckTest() + { + Assert.Throws("context", () => new RoleStore(null)); + var store = new RoleStore(new InMemoryContext()); + await Assert.ThrowsAsync("role", async () => await store.GetRoleIdAsync(null)); + await Assert.ThrowsAsync("role", async () => await store.GetRoleNameAsync(null)); + await Assert.ThrowsAsync("role", async () => await store.SetRoleNameAsync(null, null)); + await Assert.ThrowsAsync("role", async () => await store.CreateAsync(null)); + await Assert.ThrowsAsync("role", async () => await store.UpdateAsync(null)); + await Assert.ThrowsAsync("role", async () => await store.DeleteAsync(null)); + } + + [Fact] + public async Task CanUpdateRoleName() + { + var manager = TestIdentityFactory.CreateRoleManager(); + var role = new IdentityRole("UpdateRoleName"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Assert.Null(await manager.FindByNameAsync("New")); + role.Name = "New"; + IdentityResultAssert.IsSuccess(await manager.UpdateAsync(role)); + Assert.NotNull(await manager.FindByNameAsync("New")); + Assert.Null(await manager.FindByNameAsync("UpdateAsync")); + } + + [Fact] + public async Task CanSetUserName() + { + var manager = TestIdentityFactory.CreateRoleManager(); + var role = new IdentityRole("UpdateRoleName"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Assert.Null(await manager.FindByNameAsync("New")); + IdentityResultAssert.IsSuccess(await manager.SetRoleNameAsync(role, "New")); + Assert.NotNull(await manager.FindByNameAsync("New")); + Assert.Null(await manager.FindByNameAsync("UpdateAsync")); + } + + } +} diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/TestIdentityFactory.cs b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/TestIdentityFactory.cs new file mode 100644 index 0000000000..83b94b677e --- /dev/null +++ b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/TestIdentityFactory.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Identity.Test; +using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.DependencyInjection.Fallback; + +namespace Microsoft.AspNet.Identity.EntityFramework.InMemory.Test +{ + public static class TestIdentityFactory + { + public static InMemoryContext CreateContext() + { + var services = new ServiceCollection(); + services.AddEntityFramework().AddInMemoryStore(); + var serviceProvider = services.BuildServiceProvider(); + + var db = new InMemoryContext(serviceProvider); + db.Database.EnsureCreated(); + + return db; + } + + public static UserManager CreateManager(InMemoryContext context) + { + return MockHelpers.CreateManager(() => new InMemoryUserStore(context)); + } + + public static UserManager CreateManager() + { + return CreateManager(CreateContext()); + } + + public static RoleManager CreateRoleManager(InMemoryContext context) + { + var services = new ServiceCollection(); + services.AddIdentity(b => b.AddRoleStore(() => new RoleStore(context))); + return services.BuildServiceProvider().GetService>(); + } + + public static RoleManager CreateRoleManager() + { + return CreateRoleManager(CreateContext()); + } + } +} diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/project.json b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/project.json new file mode 100644 index 0000000000..3050096619 --- /dev/null +++ b/test/Microsoft.AspNet.Identity.EntityFramework.InMemory.Test/project.json @@ -0,0 +1,30 @@ +{ + "version": "0.1-alpha-*", + "dependencies": { + "Microsoft.AspNet.Http": "1.0.0-*", + "Microsoft.AspNet.Identity": "", + "Microsoft.AspNet.Identity.EntityFramework": "", + "Microsoft.AspNet.PipelineCore": "1.0.0-*", + "Microsoft.AspNet.RequestContainer": "1.0.0-*", + "Microsoft.AspNet.Security.DataProtection": "1.0.0-*", + "Microsoft.AspNet.Testing": "1.0.0-*", + "EntityFramework.InMemory": "7.0.0-*", + "Microsoft.Framework.OptionsModel" : "1.0.0-*", + "System.Security.Claims": "1.0.0-*", + "Xunit.KRunner": "1.0.0-*" + }, + "code": "**\\*.cs;..\\Shared\\*.cs", + "configurations": { + "net451": { + "dependencies": { + "Moq" : "4.2.1312.1622", + "System.Collections": "", + "System.Runtime": "" + } + } + }, + "code": "**\\*.cs;..\\Shared\\*.cs", + "commands": { + "test": "Xunit.KRunner" + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.Test/Microsoft.AspNet.Identity.EntityFramework.Test.kproj b/test/Microsoft.AspNet.Identity.EntityFramework.Test/Microsoft.AspNet.Identity.EntityFramework.Test.kproj index 42617178b1..b7c20385e7 100644 --- a/test/Microsoft.AspNet.Identity.EntityFramework.Test/Microsoft.AspNet.Identity.EntityFramework.Test.kproj +++ b/test/Microsoft.AspNet.Identity.EntityFramework.Test/Microsoft.AspNet.Identity.EntityFramework.Test.kproj @@ -21,11 +21,7 @@ - - - - - + - + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.Test/SqlUserStoreTest.cs b/test/Microsoft.AspNet.Identity.EntityFramework.Test/SqlUserStoreTest.cs deleted file mode 100644 index 375280d51f..0000000000 --- a/test/Microsoft.AspNet.Identity.EntityFramework.Test/SqlUserStoreTest.cs +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.AspNet.Builder; -using Microsoft.AspNet.Identity.Test; -using Microsoft.Data.Entity; -using Microsoft.Framework.DependencyInjection; -using Microsoft.Framework.DependencyInjection.Fallback; -using Microsoft.Framework.OptionsModel; -using System; -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; -using Xunit; - -namespace Microsoft.AspNet.Identity.Entity.Test -{ - public class SqlUserStoreTest - { - private const string ConnectionString = @"Server=(localdb)\v11.0;Database=SqlUserStoreTest;Trusted_Connection=True;"; - - public class ApplicationUser : User { } - - public class ApplicationDbContext : IdentitySqlContext - { - public ApplicationDbContext(IServiceProvider services, IOptionsAccessor options) : base(services, options.Options) { } - } - - [TestPriority(0)] - [Fact] - public void RecreateDatabase() - { - CreateContext(true); - } - - [Fact] - public async Task EnsureStartupUsageWorks() - { - EnsureDatabase(); - IBuilder builder = new Builder.Builder(new ServiceCollection().BuildServiceProvider()); - - builder.UseServices(services => - { - services.AddEntityFramework().AddSqlServer(); - services.AddIdentity().AddEntityFramework(); - services.SetupOptions(options => - options.UseSqlServer(ConnectionString)); - }); - - var userStore = builder.ApplicationServices.GetService>(); - var userManager = builder.ApplicationServices.GetService>(); - - Assert.NotNull(userStore); - Assert.NotNull(userManager); - - const string userName = "admin"; - const string password = "1qaz@WSX"; - var user = new ApplicationUser { UserName = userName }; - IdentityResultAssert.IsSuccess(await userManager.CreateAsync(user, password)); - IdentityResultAssert.IsSuccess(await userManager.DeleteAsync(user)); - } - - [Fact] - public void CanCreateUserUsingEF() - { - using (var db = CreateContext()) - { - var guid = Guid.NewGuid().ToString(); - db.Users.Add(new User {Id = guid, UserName = guid}); - db.SaveChanges(); - Assert.True(db.Users.Any(u => u.UserName == guid)); - Assert.NotNull(db.Users.FirstOrDefault(u => u.UserName == guid)); - } - } - - public static IdentitySqlContext CreateContext(bool delete = false) - { - var services = new ServiceCollection(); - services.AddEntityFramework().AddSqlServer(); - var serviceProvider = services.BuildServiceProvider(); - - var db = new IdentitySqlContext(serviceProvider, ConnectionString); - if (delete) - { - db.Database.EnsureDeleted(); - } - db.Database.EnsureCreated(); - return db; - } - - public static void EnsureDatabase() - { - CreateContext(); - } - - public static ApplicationDbContext CreateAppContext() - { - CreateContext(); - var services = new ServiceCollection(); - services.AddEntityFramework().AddSqlServer(); - services.Add(OptionsServices.GetDefaultServices()); - var serviceProvider = services.BuildServiceProvider(); - - var db = new ApplicationDbContext(serviceProvider, serviceProvider.GetService>()); - db.Database.EnsureCreated(); - return db; - } - - public static UserManager CreateManager(DbContext context) - { - return MockHelpers.CreateManager(() => new UserStore(context)); - } - - public static UserManager CreateManager() - { - return CreateManager(CreateContext()); - } - - [Fact] - public async Task CanCreateUsingManager() - { - var manager = CreateManager(); - var guid = Guid.NewGuid().ToString(); - var user = new User { UserName = "New"+guid }; - IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); - IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); - } - - [Fact] - public async Task CanDeleteUser() - { - var manager = CreateManager(); - var user = new User("DeleteAsync"); - IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); - IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); - Assert.Null(await manager.FindByIdAsync(user.Id)); - } - - [Fact] - public async Task CanUpdateUserName() - { - var manager = CreateManager(); - var oldName = Guid.NewGuid().ToString(); - var user = new User(oldName); - IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); - var newName = Guid.NewGuid().ToString(); - user.UserName = newName; - IdentityResultAssert.IsSuccess(await manager.UpdateAsync(user)); - Assert.NotNull(await manager.FindByNameAsync(newName)); - Assert.Null(await manager.FindByNameAsync(oldName)); - IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); - } - - [Fact] - public async Task CanSetUserName() - { - var manager = CreateManager(); - var oldName = Guid.NewGuid().ToString(); - var user = new User(oldName); - IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); - var newName = Guid.NewGuid().ToString(); - IdentityResultAssert.IsSuccess(await manager.SetUserNameAsync(user, newName)); - Assert.NotNull(await manager.FindByNameAsync(newName)); - Assert.Null(await manager.FindByNameAsync(oldName)); - IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); - } - - [Fact] - public async Task CanChangePassword() - { - var manager = CreateManager(); - var user = new User("ChangePasswordTest"); - const string password = "password"; - const string newPassword = "newpassword"; - IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); - //Assert.Equal(manager.Users.Count(), 1); - //var stamp = user.SecurityStamp; - //Assert.NotNull(stamp); - IdentityResultAssert.IsSuccess(await manager.ChangePasswordAsync(user, password, newPassword)); - Assert.Null(await manager.FindByUserNamePasswordAsync(user.UserName, password)); - Assert.Equal(user, await manager.FindByUserNamePasswordAsync(user.UserName, newPassword)); - //Assert.NotEqual(stamp, user.SecurityStamp); - IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); - } - - [Fact] - public async Task ClaimsIdentityCreatesExpectedClaims() - { - var manager = CreateManager(); - //var role = TestIdentityFactory.CreateRoleManager(context); - var user = new User("Hao"); - IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); - //IdentityResultAssert.IsSuccess(await role.CreateAsync(new EntityRole("Admin"))); - //IdentityResultAssert.IsSuccess(await role.CreateAsync(new EntityRole("Local"))); - //IdentityResultAssert.IsSuccess(await manager.AddToRoleAsync(user, "Admin")); - //IdentityResultAssert.IsSuccess(await manager.AddToRoleAsync(user, "Local")); - Claim[] userClaims = - { - new Claim("Whatever", "Value"), - new Claim("Whatever2", "Value2") - }; - foreach (var c in userClaims) - { - IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c)); - } - - var identity = await manager.CreateIdentityAsync(user, "test"); - var claimsFactory = (ClaimsIdentityFactory)manager.ClaimsIdentityFactory; - Assert.NotNull(claimsFactory); - var claims = identity.Claims.ToList(); - Assert.NotNull(claims); - Assert.True( - claims.Any(c => c.Type == manager.Options.ClaimType.UserName && c.Value == user.UserName)); - Assert.True(claims.Any(c => c.Type == manager.Options.ClaimType.UserId && c.Value == user.Id.ToString())); - //Assert.True(claims.Any(c => c.Type == manager.Options.ClaimType.Role && c.Value == "Admin")); - //Assert.True(claims.Any(c => c.Type == manager.Options.ClaimType.Role && c.Value == "Local")); - foreach (var cl in userClaims) - { - Assert.True(claims.Any(c => c.Type == cl.Type && c.Value == cl.Value)); - } - IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); - } - } -} diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.Test/StartupTest.cs b/test/Microsoft.AspNet.Identity.EntityFramework.Test/StartupTest.cs deleted file mode 100644 index 3d423b212f..0000000000 --- a/test/Microsoft.AspNet.Identity.EntityFramework.Test/StartupTest.cs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using Microsoft.AspNet.Builder; -using Microsoft.AspNet.Identity.Test; -using Microsoft.Framework.DependencyInjection; -using Microsoft.Framework.DependencyInjection.Fallback; -using Microsoft.Framework.OptionsModel; -using System; -using System.Threading.Tasks; -using Xunit; - -namespace Microsoft.AspNet.Identity.Entity.Test -{ - public class StartupTest - { - public class ApplicationUser : EntityUser { } - - public class ApplicationDbContext : IdentityContext - { - public ApplicationDbContext(IServiceProvider services) : base(services) { } - } - - [Fact] - public async Task EnsureStartupUsageWorks() - { - EnsureDatabase(); - - IBuilder builder = new Builder.Builder(new ServiceCollection().BuildServiceProvider()); - - builder.UseServices(services => - { - services.AddEntityFramework().AddInMemoryStore(); - services.AddIdentity(s => - { - s.AddEntityFrameworkInMemory(); - }); - }); - - var userStore = builder.ApplicationServices.GetService>(); - var roleStore = builder.ApplicationServices.GetService>(); - var userManager = builder.ApplicationServices.GetService>(); - var roleManager = builder.ApplicationServices.GetService>(); - - Assert.NotNull(userStore); - Assert.NotNull(userManager); - Assert.NotNull(roleStore); - Assert.NotNull(roleManager); - - await CreateAdminUser(builder.ApplicationServices); - } - - private static async Task CreateAdminUser(IServiceProvider serviceProvider) - { - const string userName = "admin"; - const string roleName = "Admins"; - const string password = "1qaz@WSX"; - var userManager = serviceProvider.GetService>(); - var roleManager = serviceProvider.GetService>(); - - var user = new ApplicationUser { UserName = userName }; - IdentityResultAssert.IsSuccess(await userManager.CreateAsync(user, password)); - IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(new EntityRole { Name = roleName })); - IdentityResultAssert.IsSuccess(await userManager.AddToRoleAsync(user, roleName)); - } - - public static void EnsureDatabase() - { - var services = new ServiceCollection(); - services.AddEntityFramework().AddInMemoryStore(); - var serviceProvider = services.BuildServiceProvider(); - - var db = new ApplicationDbContext(serviceProvider); - db.Database.EnsureCreated(); - } - } -} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.EntityFramework.Test/UserStoreTest.cs b/test/Microsoft.AspNet.Identity.EntityFramework.Test/UserStoreTest.cs new file mode 100644 index 0000000000..ec9878273c --- /dev/null +++ b/test/Microsoft.AspNet.Identity.EntityFramework.Test/UserStoreTest.cs @@ -0,0 +1,1748 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Builder; +using Microsoft.AspNet.Identity.Test; +using Microsoft.AspNet.Testing; +using Microsoft.Data.Entity; +using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.DependencyInjection.Fallback; +using Microsoft.Framework.OptionsModel; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNet.Identity.EntityFramework.Test +{ + [TestCaseOrderer("Microsoft.AspNet.Identity.Test.PriorityOrderer", "Microsoft.AspNet.Identity.EntityFramework.Test")] + public class UserStoreTest + { + private const string ConnectionString = @"Server=(localdb)\v11.0;Database=SqlUserStoreTest;Trusted_Connection=True;"; + + public class ApplicationUser : User { } + + public class ApplicationDbContext : IdentityDbContext + { + public ApplicationDbContext(IServiceProvider services, IOptionsAccessor options) : base(services, options.Options) { } + } + + [TestPriority(-1)] + [Fact] + public void RecreateDatabase() + { + CreateContext(true); + } + + [Fact] + public async Task EnsureStartupUsageWorks() + { + EnsureDatabase(); + IBuilder builder = new Builder.Builder(new ServiceCollection().BuildServiceProvider()); + + builder.UseServices(services => + { + services.AddEntityFramework().AddSqlServer(); + services.AddIdentity().AddEntityFramework(); + services.SetupOptions(options => + options.UseSqlServer(ConnectionString)); + }); + + var userStore = builder.ApplicationServices.GetService>(); + var userManager = builder.ApplicationServices.GetService>(); + + Assert.NotNull(userStore); + Assert.NotNull(userManager); + + const string userName = "admin"; + const string password = "1qaz@WSX"; + var user = new ApplicationUser { UserName = userName }; + IdentityResultAssert.IsSuccess(await userManager.CreateAsync(user, password)); + IdentityResultAssert.IsSuccess(await userManager.DeleteAsync(user)); + } + + [Fact] + public void CanCreateUserUsingEF() + { + using (var db = CreateContext()) + { + var guid = Guid.NewGuid().ToString(); + db.Users.Add(new User {Id = guid, UserName = guid}); + db.SaveChanges(); + Assert.True(db.Users.Any(u => u.UserName == guid)); + Assert.NotNull(db.Users.FirstOrDefault(u => u.UserName == guid)); + } + } + + public static IdentityDbContext CreateContext(bool delete = false) + { + var services = new ServiceCollection(); + services.AddEntityFramework().AddSqlServer(); + var serviceProvider = services.BuildServiceProvider(); + + var db = new IdentityDbContext(serviceProvider, ConnectionString); + if (delete) + { + db.Database.EnsureDeleted(); + } + db.Database.EnsureCreated(); + return db; + } + + public static void EnsureDatabase() + { + CreateContext(); + } + + public static ApplicationDbContext CreateAppContext() + { + CreateContext(); + var services = new ServiceCollection(); + services.AddEntityFramework().AddSqlServer(); + services.Add(OptionsServices.GetDefaultServices()); + var serviceProvider = services.BuildServiceProvider(); + + var db = new ApplicationDbContext(serviceProvider, serviceProvider.GetService>()); + db.Database.EnsureCreated(); + return db; + } + + public static UserManager CreateManager(DbContext context) + { + return MockHelpers.CreateManager(() => new UserStore(context)); + } + + public static UserManager CreateManager() + { + return CreateManager(CreateContext()); + } + + public static RoleManager CreateRoleManager(IdentityDbContext context) + { + var services = new ServiceCollection(); + services.AddIdentity(b => b.AddRoleStore(() => new RoleStore(context))); + return services.BuildServiceProvider().GetService>(); + } + + public static RoleManager CreateRoleManager() + { + return CreateRoleManager(CreateContext()); + } + + [Fact] + public async Task SqlUserStoreMethodsThrowWhenDisposedTest() + { + var store = new UserStore(new IdentityDbContext()); + store.Dispose(); + await Assert.ThrowsAsync(async () => await store.AddClaimAsync(null, null)); + await Assert.ThrowsAsync(async () => await store.AddLoginAsync(null, null)); + await Assert.ThrowsAsync(async () => await store.AddToRoleAsync(null, null)); + await Assert.ThrowsAsync(async () => await store.GetClaimsAsync(null)); + await Assert.ThrowsAsync(async () => await store.GetLoginsAsync(null)); + await Assert.ThrowsAsync(async () => await store.GetRolesAsync(null)); + await Assert.ThrowsAsync(async () => await store.IsInRoleAsync(null, null)); + await Assert.ThrowsAsync(async () => await store.RemoveClaimAsync(null, null)); + await Assert.ThrowsAsync(async () => await store.RemoveLoginAsync(null, null)); + await Assert.ThrowsAsync( + async () => await store.RemoveFromRoleAsync(null, null)); + await Assert.ThrowsAsync(async () => await store.RemoveClaimAsync(null, null)); + await Assert.ThrowsAsync(async () => await store.FindByLoginAsync(null)); + await Assert.ThrowsAsync(async () => await store.FindByIdAsync(null)); + await Assert.ThrowsAsync(async () => await store.FindByNameAsync(null)); + await Assert.ThrowsAsync(async () => await store.CreateAsync(null)); + await Assert.ThrowsAsync(async () => await store.UpdateAsync(null)); + await Assert.ThrowsAsync(async () => await store.DeleteAsync(null)); + await Assert.ThrowsAsync( + async () => await store.SetEmailConfirmedAsync(null, true)); + await Assert.ThrowsAsync(async () => await store.GetEmailConfirmedAsync(null)); + await Assert.ThrowsAsync( + async () => await store.SetPhoneNumberConfirmedAsync(null, true)); + await Assert.ThrowsAsync( + async () => await store.GetPhoneNumberConfirmedAsync(null)); + } + + [Fact] + public async Task UserStorePublicNullCheckTest() + { + Assert.Throws("context", () => new UserStore(null)); + var store = new UserStore(new IdentityDbContext()); + await Assert.ThrowsAsync("user", async () => await store.GetUserIdAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.GetUserNameAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.SetUserNameAsync(null, null)); + await Assert.ThrowsAsync("user", async () => await store.CreateAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.UpdateAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.DeleteAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.AddClaimAsync(null, null)); + await Assert.ThrowsAsync("user", async () => await store.RemoveClaimAsync(null, null)); + await Assert.ThrowsAsync("user", async () => await store.GetClaimsAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.GetLoginsAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.GetRolesAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.AddLoginAsync(null, null)); + await + Assert.ThrowsAsync("user", async () => await store.RemoveLoginAsync(null, null)); + await Assert.ThrowsAsync("user", async () => await store.AddToRoleAsync(null, null)); + await + Assert.ThrowsAsync("user", + async () => await store.RemoveFromRoleAsync(null, null)); + await Assert.ThrowsAsync("user", async () => await store.IsInRoleAsync(null, null)); + await Assert.ThrowsAsync("user", async () => await store.GetPasswordHashAsync(null)); + await + Assert.ThrowsAsync("user", + async () => await store.SetPasswordHashAsync(null, null)); + await Assert.ThrowsAsync("user", async () => await store.GetSecurityStampAsync(null)); + await Assert.ThrowsAsync("user", + async () => await store.SetSecurityStampAsync(null, null)); + await Assert.ThrowsAsync("claim", + async () => await store.AddClaimAsync(new User("fake"), null)); + await Assert.ThrowsAsync("claim", + async () => await store.RemoveClaimAsync(new User("fake"), null)); + await Assert.ThrowsAsync("login", + async () => await store.AddLoginAsync(new User("fake"), null)); + await Assert.ThrowsAsync("login", + async () => await store.RemoveLoginAsync(new User("fake"), null)); + await Assert.ThrowsAsync("login", async () => await store.FindByLoginAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.GetEmailConfirmedAsync(null)); + await Assert.ThrowsAsync("user", + async () => await store.SetEmailConfirmedAsync(null, true)); + await Assert.ThrowsAsync("user", async () => await store.GetEmailAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.SetEmailAsync(null, null)); + await Assert.ThrowsAsync("user", async () => await store.GetPhoneNumberAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.SetPhoneNumberAsync(null, null)); + await Assert.ThrowsAsync("user", + async () => await store.GetPhoneNumberConfirmedAsync(null)); + await Assert.ThrowsAsync("user", + async () => await store.SetPhoneNumberConfirmedAsync(null, true)); + await Assert.ThrowsAsync("user", async () => await store.GetTwoFactorEnabledAsync(null)); + await Assert.ThrowsAsync("user", + async () => await store.SetTwoFactorEnabledAsync(null, true)); + await Assert.ThrowsAsync("user", async () => await store.GetAccessFailedCountAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.GetLockoutEnabledAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.SetLockoutEnabledAsync(null, false)); + await Assert.ThrowsAsync("user", async () => await store.GetLockoutEndDateAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.SetLockoutEndDateAsync(null, new DateTimeOffset())); + await Assert.ThrowsAsync("user", async () => await store.ResetAccessFailedCountAsync(null)); + await Assert.ThrowsAsync("user", async () => await store.IncrementAccessFailedCountAsync(null)); + await Assert.ThrowsAsync("roleName", async () => await store.AddToRoleAsync(new User("fake"), null)); + await Assert.ThrowsAsync("roleName", async () => await store.RemoveFromRoleAsync(new User("fake"), null)); + await Assert.ThrowsAsync("roleName", async () => await store.IsInRoleAsync(new User("fake"), null)); + await Assert.ThrowsAsync("roleName", async () => await store.AddToRoleAsync(new User("fake"), "")); + await Assert.ThrowsAsync("roleName", async () => await store.RemoveFromRoleAsync(new User("fake"), "")); + await Assert.ThrowsAsync("roleName", async () => await store.IsInRoleAsync(new User("fake"), "")); + } + + [Fact] + public async Task CanCreateUsingManager() + { + var manager = CreateManager(); + var guid = Guid.NewGuid().ToString(); + var user = new User { UserName = "New"+guid }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); + } + + [Fact] + public async Task CanDeleteUser() + { + var manager = CreateManager(); + var user = new User("CanDeleteUser"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); + Assert.Null(await manager.FindByIdAsync(user.Id)); + } + + [Fact] + public async Task CanUpdateUserName() + { + var manager = CreateManager(); + var oldName = Guid.NewGuid().ToString(); + var user = new User(oldName); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var newName = Guid.NewGuid().ToString(); + user.UserName = newName; + IdentityResultAssert.IsSuccess(await manager.UpdateAsync(user)); + Assert.NotNull(await manager.FindByNameAsync(newName)); + Assert.Null(await manager.FindByNameAsync(oldName)); + IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); + } + + [Fact] + public async Task CanUpdatePasswordUsingHasher() + { + var manager = CreateManager(); + var user = new User("CanUpdatePasswordUsingHasher"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); + Assert.True(await manager.CheckPasswordAsync(user, "password")); + user.PasswordHash = manager.PasswordHasher.HashPassword("New"); + IdentityResultAssert.IsSuccess(await manager.UpdateAsync(user)); + Assert.False(await manager.CheckPasswordAsync(user, "password")); + Assert.True(await manager.CheckPasswordAsync(user, "New")); + IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); + } + + [Fact] + public async Task CanSetUserName() + { + var manager = CreateManager(); + var oldName = Guid.NewGuid().ToString(); + var user = new User(oldName); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var newName = Guid.NewGuid().ToString(); + IdentityResultAssert.IsSuccess(await manager.SetUserNameAsync(user, newName)); + Assert.NotNull(await manager.FindByNameAsync(newName)); + Assert.Null(await manager.FindByNameAsync(oldName)); + IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); + } + + [Fact] + public async Task RemoveClaimOnlyForUser() + { + var manager = CreateManager(); + var user = new User("RemoveClaimForMe"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user)); + } + + [Fact] + public async Task UserValidatorCanBlockCreate() + { + var manager = CreateManager(); + var user = new User("UserValidatorCanBlockCreate"); + manager.UserValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.CreateAsync(user), AlwaysBadValidator.ErrorMessage); + } + + [Fact] + public async Task UserValidatorCanBlockUpdate() + { + var manager = CreateManager(); + var user = new User("UserValidatorCanBlockUpdate"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + manager.UserValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.UpdateAsync(user), AlwaysBadValidator.ErrorMessage); + } + + [Theory] + [InlineData("")] + [InlineData(null)] + public async Task UserValidatorBlocksShortEmailsWhenRequiresUniqueEmail(string email) + { + var manager = CreateManager(); + var user = new User("UserValidatorBlocksShortEmailsWhenRequiresUniqueEmail") { Email = email }; + manager.Options.User.RequireUniqueEmail = true; + IdentityResultAssert.IsFailure(await manager.CreateAsync(user), "Email cannot be null or empty."); + } + +#if NET45 + [Theory] + [InlineData("@@afd")] + [InlineData("bogus")] + public async Task UserValidatorBlocksInvalidEmailsWhenRequiresUniqueEmail(string email) + { + var manager = CreateManager(); + var user = new User("UserValidatorBlocksInvalidEmailsWhenRequiresUniqueEmail") { Email = email }; + manager.Options.User.RequireUniqueEmail = true; + IdentityResultAssert.IsFailure(await manager.CreateAsync(user), "Email '" + email + "' is invalid."); + } +#endif + + [Fact] + public async Task PasswordValidatorCanBlockAddPassword() + { + var manager = CreateManager(); + var user = new User("AddPasswordBlocked"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + manager.PasswordValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.AddPasswordAsync(user, "password"), + AlwaysBadValidator.ErrorMessage); + } + + [Fact] + public async Task PasswordValidatorCanBlockChangePassword() + { + var manager = CreateManager(); + var user = new User("ChangePasswordBlocked"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); + manager.PasswordValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.ChangePasswordAsync(user, "password", "new"), + AlwaysBadValidator.ErrorMessage); + } + + [Fact] + public async Task CanCreateUserNoPassword() + { + var manager = CreateManager(); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(new User("CanCreateUserNoPassword"))); + var user = await manager.FindByNameAsync("CanCreateUserNoPassword"); + Assert.NotNull(user); + Assert.Null(user.PasswordHash); + var logins = await manager.GetLoginsAsync(user); + Assert.NotNull(logins); + Assert.Equal(0, logins.Count()); + } + + [Fact] + public async Task CanCreateUserAddLogin() + { + var manager = CreateManager(); + const string userName = "CanCreateUserAddLogin"; + const string provider = "ZzAuth"; + const string providerKey = "HaoKey"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(new User(userName))); + var user = await manager.FindByNameAsync(userName); + Assert.NotNull(user); + var login = new UserLoginInfo(provider, providerKey); + IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login)); + var logins = await manager.GetLoginsAsync(user); + Assert.NotNull(logins); + Assert.Equal(1, logins.Count()); + Assert.Equal(provider, logins.First().LoginProvider); + Assert.Equal(providerKey, logins.First().ProviderKey); + } + + [Fact] + public async Task CanCreateUserLoginAndAddPassword() + { + var manager = CreateManager(); + var login = new UserLoginInfo("Provider", "key"); + var user = new User("CreateUserLoginAddPasswordTest"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login)); + Assert.False(await manager.HasPasswordAsync(user)); + IdentityResultAssert.IsSuccess(await manager.AddPasswordAsync(user, "password")); + Assert.True(await manager.HasPasswordAsync(user)); + var logins = await manager.GetLoginsAsync(user); + Assert.NotNull(logins); + Assert.Equal(1, logins.Count()); + Assert.Equal(user, await manager.FindByLoginAsync(login)); + Assert.Equal(user, await manager.FindByUserNamePasswordAsync(user.UserName, "password")); + } + + [Fact] + public async Task AddPasswordFailsIfAlreadyHave() + { + var manager = CreateManager(); + var user = new User("CannotAddAnotherPassword"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "Password")); + Assert.True(await manager.HasPasswordAsync(user)); + IdentityResultAssert.IsFailure(await manager.AddPasswordAsync(user, "password"), + "User already has a password set."); + } + + [Fact] + public async Task CanCreateUserAddRemoveLogin() + { + var manager = CreateManager(); + var user = new User("CreateUserAddRemoveLoginTest"); + var login = new UserLoginInfo("Provider", "key"); + var result = await manager.CreateAsync(user); + Assert.NotNull(user); + IdentityResultAssert.IsSuccess(result); + IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login)); + Assert.Equal(user, await manager.FindByLoginAsync(login)); + var logins = await manager.GetLoginsAsync(user); + Assert.NotNull(logins); + Assert.Equal(1, logins.Count()); + Assert.Equal(login.LoginProvider, logins.Last().LoginProvider); + Assert.Equal(login.ProviderKey, logins.Last().ProviderKey); + var stamp = user.SecurityStamp; + IdentityResultAssert.IsSuccess(await manager.RemoveLoginAsync(user, login)); + Assert.Null(await manager.FindByLoginAsync(login)); + logins = await manager.GetLoginsAsync(user); + Assert.NotNull(logins); + Assert.Equal(0, logins.Count()); + Assert.NotEqual(stamp, user.SecurityStamp); + } + + [Fact] + public async Task CanRemovePassword() + { + var manager = CreateManager(); + var user = new User("RemovePasswordTest"); + const string password = "password"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = user.SecurityStamp; + IdentityResultAssert.IsSuccess(await manager.RemovePasswordAsync(user)); + var u = await manager.FindByNameAsync(user.UserName); + Assert.NotNull(u); + Assert.Null(u.PasswordHash); + Assert.NotEqual(stamp, user.SecurityStamp); + } + + [Fact] + public async Task CanChangePassword() + { + var manager = CreateManager(); + var user = new User("ChangePasswordTest"); + const string password = "password"; + const string newPassword = "newpassword"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + IdentityResultAssert.IsSuccess(await manager.ChangePasswordAsync(user, password, newPassword)); + Assert.Null(await manager.FindByUserNamePasswordAsync(user.UserName, password)); + Assert.Equal(user, await manager.FindByUserNamePasswordAsync(user.UserName, newPassword)); + Assert.NotEqual(stamp, user.SecurityStamp); + } + + [Fact] + public async Task CanAddRemoveUserClaim() + { + var manager = CreateManager(); + var user = new User("ClaimsAddRemove"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Claim[] claims = { new Claim("c", "v"), new Claim("c2", "v2"), new Claim("c2", "v3") }; + foreach (var c in claims) + { + IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c)); + Assert.NotNull(user.Claims.Single(cl => cl.ClaimType == c.Type && cl.ClaimValue == c.Value)); + } + var userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(3, userClaims.Count); + IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[0])); + userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(2, userClaims.Count); + IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[1])); + userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(1, userClaims.Count); + IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[2])); + userClaims = await manager.GetClaimsAsync(user); + Assert.Equal(0, userClaims.Count); + } + + [Fact] + public async Task ChangePasswordFallsIfPasswordWrong() + { + var manager = CreateManager(); + var user = new User("user"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); + var result = await manager.ChangePasswordAsync(user, "bogus", "newpassword"); + IdentityResultAssert.IsFailure(result, "Incorrect password."); + } + + [Fact] + public async Task AddDupeUserNameFails() + { + var manager = CreateManager(); + var user = new User("AddDupeUserNameFails"); + var user2 = new User("AddDupeUserNameFails"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsFailure(await manager.CreateAsync(user2), "Name AddDupeUserNameFails is already taken."); + } + + [Fact] + public async Task AddDupeEmailAllowedByDefault() + { + var manager = CreateManager(); + var user = new User("AddDupeEmailAllowedByDefault") { Email = "yup@yup.com" }; + var user2 = new User("AddDupeEmailAllowedByDefault2") { Email = "yup@yup.com" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2)); + } + + [Fact] + public async Task AddDupeEmailFallsWhenUniqueEmailRequired() + { + var manager = CreateManager(); + manager.Options.User.RequireUniqueEmail = true; + var user = new User("AddDupeEmailFallsWhenUniqueEmailRequired") { Email = "dupeEmailTrue@yup.com" }; + var user2 = new User("AddDupeEmailFallsWhenUniqueEmailRequiredDupe") { Email = "dupeEmailTrue@yup.com" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsFailure(await manager.CreateAsync(user2), "Email 'dupeEmailTrue@yup.com' is already taken."); + } + + [Fact] + public async Task UpdateSecurityStampActuallyChanges() + { + var manager = CreateManager(); + var user = new User("UpdateSecurityStampActuallyChanges"); + Assert.Null(user.SecurityStamp); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user)); + Assert.NotEqual(stamp, user.SecurityStamp); + } + + [Fact] + public async Task AddDupeLoginFails() + { + var manager = CreateManager(); + var user = new User("DupeLogin"); + var login = new UserLoginInfo("provder", "key"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login)); + var result = await manager.AddLoginAsync(user, login); + IdentityResultAssert.IsFailure(result, "A user with that external login already exists."); + } + + // Email tests + [Fact] + public async Task CanFindByEmail() + { + var manager = CreateManager(); + const string userName = "EmailTest"; + const string email = "email@test.com"; + var user = new User(userName) { Email = email }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var fetch = await manager.FindByEmailAsync(email); + Assert.Equal(user, fetch); + } + + [Fact] + public async Task CanFindUsersViaUserQuerable() + { + var mgr = CreateManager(); + var users = GenerateUsers("CanFindUsersViaUserQuerable", 3); + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(u)); + } + var usersQ = mgr.Users.Where(u => u.UserName.StartsWith("CanFindUsersViaUserQuerable")); + Assert.Null(mgr.Users.FirstOrDefault(u => u.UserName == "bogus")); + } + + [Fact] + public async Task EnsureRoleClaimNavigationProperty() + { + var context = CreateContext(); + var roleManager = CreateRoleManager(context); + var r = new IdentityRole("EnsureRoleClaimNavigationProperty"); + IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(r)); + var c = new Claim("a", "b"); + IdentityResultAssert.IsSuccess(await roleManager.AddClaimAsync(r, c)); + Assert.NotNull(r.Claims.Single(cl => cl.ClaimValue == c.Value && cl.ClaimType == c.Type)); + } + + [Fact] + public async Task ClaimsIdentityCreatesExpectedClaims() + { + var context = CreateContext(); + var manager = CreateManager(context); + var role = CreateRoleManager(context); + var user = new User("ClaimsIdentityCreatesExpectedClaims"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var admin = new IdentityRole("Admin"); + var local = new IdentityRole("Local"); + IdentityResultAssert.IsSuccess(await role.CreateAsync(admin)); + IdentityResultAssert.IsSuccess(await role.CreateAsync(local)); + IdentityResultAssert.IsSuccess(await manager.AddToRoleAsync(user, "Admin")); + IdentityResultAssert.IsSuccess(await manager.AddToRoleAsync(user, "Local")); + Claim[] userClaims = + { + new Claim("Whatever", "Value"), + new Claim("Whatever2", "Value2") + }; + foreach (var c in userClaims) + { + IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c)); + } + Claim[] adminClaims = + { + new Claim("Admin", "Value"), + }; + foreach (var c in adminClaims) + { + IdentityResultAssert.IsSuccess(await role.AddClaimAsync(admin, c)); + } + Claim[] localClaims = + { + new Claim("Local", "Value"), + new Claim("Local2", "Value2") + }; + foreach (var c in localClaims) + { + IdentityResultAssert.IsSuccess(await role.AddClaimAsync(local, c)); + } + + var claimsFactory = new ClaimsIdentityFactory(manager, role); + var identity = await claimsFactory.CreateAsync(user, "test"); + var claims = identity.Claims.ToList(); + Assert.NotNull(claims); + Assert.True( + claims.Any(c => c.Type == manager.Options.ClaimType.UserName && c.Value == user.UserName)); + Assert.True(claims.Any(c => c.Type == manager.Options.ClaimType.UserId && c.Value == user.Id.ToString())); + Assert.True(claims.Any(c => c.Type == manager.Options.ClaimType.Role && c.Value == "Admin")); + Assert.True(claims.Any(c => c.Type == manager.Options.ClaimType.Role && c.Value == "Local")); + foreach (var cl in userClaims) + { + Assert.True(claims.Any(c => c.Type == cl.Type && c.Value == cl.Value)); + } + foreach (var cl in adminClaims) + { + Assert.True(claims.Any(c => c.Type == cl.Type && c.Value == cl.Value)); + } + foreach (var cl in localClaims) + { + Assert.True(claims.Any(c => c.Type == cl.Type && c.Value == cl.Value)); + } + + // Remove a role claim and make sure its not there + IdentityResultAssert.IsSuccess(await role.RemoveClaimAsync(local, localClaims[0])); + identity = await claimsFactory.CreateAsync(user, "test"); + claims = identity.Claims.ToList(); + Assert.False(claims.Any(c => c.Type == localClaims[0].Type && c.Value == localClaims[0].Value)); + Assert.True(claims.Any(c => c.Type == localClaims[1].Type && c.Value == localClaims[1].Value)); + } + + [Fact] + public async Task ConfirmEmailFalseByDefaultTest() + { + var manager = CreateManager(); + var user = new User("ConfirmEmailFalseByDefaultTest"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + } + + // TODO: No token provider implementations yet + private class StaticTokenProvider : IUserTokenProvider + { + public Task GenerateAsync(string purpose, UserManager manager, + User user, CancellationToken token) + { + return Task.FromResult(MakeToken(purpose, user)); + } + + public Task ValidateAsync(string purpose, string token, UserManager manager, + User user, CancellationToken cancellationToken) + { + return Task.FromResult(token == MakeToken(purpose, user)); + } + + public Task NotifyAsync(string token, UserManager manager, User user, CancellationToken cancellationToken) + { + return Task.FromResult(0); + } + + public Task IsValidProviderForUserAsync(UserManager manager, User user, CancellationToken token) + { + return Task.FromResult(true); + } + + private static string MakeToken(string purpose, User user) + { + return string.Join(":", user.Id, purpose, "ImmaToken"); + } + } + + [Fact] + public async Task CanResetPasswordWithStaticTokenProvider() + { + var manager = CreateManager(); + manager.UserTokenProvider = new StaticTokenProvider(); + var user = new User("CanResetPasswordWithStaticTokenProvider"); + const string password = "password"; + const string newPassword = "newpassword"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + var token = await manager.GeneratePasswordResetTokenAsync(user); + Assert.NotNull(token); + IdentityResultAssert.IsSuccess(await manager.ResetPasswordAsync(user, token, newPassword)); + Assert.Null(await manager.FindByUserNamePasswordAsync(user.UserName, password)); + Assert.Equal(user, await manager.FindByUserNamePasswordAsync(user.UserName, newPassword)); + Assert.NotEqual(stamp, user.SecurityStamp); + } + + [Fact] + public async Task PasswordValidatorCanBlockResetPasswordWithStaticTokenProvider() + { + var manager = CreateManager(); + manager.UserTokenProvider = new StaticTokenProvider(); + var user = new User("PasswordValidatorCanBlockResetPasswordWithStaticTokenProvider"); + const string password = "password"; + const string newPassword = "newpassword"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + var token = await manager.GeneratePasswordResetTokenAsync(user); + Assert.NotNull(token); + manager.PasswordValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.ResetPasswordAsync(user, token, newPassword), + AlwaysBadValidator.ErrorMessage); + Assert.NotNull(await manager.FindByUserNamePasswordAsync(user.UserName, password)); + Assert.Equal(user, await manager.FindByUserNamePasswordAsync(user.UserName, password)); + Assert.Equal(stamp, user.SecurityStamp); + } + + [Fact] + public async Task ResetPasswordWithStaticTokenProviderFailsWithWrongToken() + { + var manager = CreateManager(); + manager.UserTokenProvider = new StaticTokenProvider(); + var user = new User("ResetPasswordWithStaticTokenProviderFailsWithWrongToken"); + const string password = "password"; + const string newPassword = "newpassword"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + IdentityResultAssert.IsFailure(await manager.ResetPasswordAsync(user, "bogus", newPassword), "Invalid token."); + Assert.NotNull(await manager.FindByUserNamePasswordAsync(user.UserName, password)); + Assert.Equal(user, await manager.FindByUserNamePasswordAsync(user.UserName, password)); + Assert.Equal(stamp, user.SecurityStamp); + } + + [Fact] + public async Task CanGenerateAndVerifyUserTokenWithStaticTokenProvider() + { + var manager = CreateManager(); + manager.UserTokenProvider = new StaticTokenProvider(); + var user = new User("CanGenerateAndVerifyUserTokenWithStaticTokenProvider"); + var user2 = new User("CanGenerateAndVerifyUserTokenWithStaticTokenProvider2"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2)); + var token = await manager.GenerateUserTokenAsync("test", user); + Assert.True(await manager.VerifyUserTokenAsync(user, "test", token)); + Assert.False(await manager.VerifyUserTokenAsync(user, "test2", token)); + Assert.False(await manager.VerifyUserTokenAsync(user, "test", token + "a")); + Assert.False(await manager.VerifyUserTokenAsync(user2, "test", token)); + } + + [Fact] + public async Task CanConfirmEmailWithStaticToken() + { + var manager = CreateManager(); + manager.UserTokenProvider = new StaticTokenProvider(); + var user = new User("CanConfirmEmailWithStaticToken"); + Assert.False(user.EmailConfirmed); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var token = await manager.GenerateEmailConfirmationTokenAsync(user); + Assert.NotNull(token); + IdentityResultAssert.IsSuccess(await manager.ConfirmEmailAsync(user, token)); + Assert.True(await manager.IsEmailConfirmedAsync(user)); + IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, null)); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + } + + [Fact] + public async Task ConfirmEmailWithStaticTokenFailsWithWrongToken() + { + var manager = CreateManager(); + manager.UserTokenProvider = new StaticTokenProvider(); + var user = new User("ConfirmEmailWithStaticTokenFailsWithWrongToken"); + Assert.False(user.EmailConfirmed); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + IdentityResultAssert.IsFailure(await manager.ConfirmEmailAsync(user, "bogus"), "Invalid token."); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + } + + // TODO: Can't reenable til we have a SecurityStamp linked token provider + //[Fact] + //public async Task ConfirmTokenFailsAfterPasswordChange() + //{ + // var manager = CreateManager(); + // var user = new User("test"); + // Assert.False(user.EmailConfirmed); + // IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); + // var token = await manager.GenerateEmailConfirmationTokenAsync(user); + // Assert.NotNull(token); + // IdentityResultAssert.IsSuccess(await manager.ChangePasswordAsync(user, "password", "newpassword")); + // IdentityResultAssert.IsFailure(await manager.ConfirmEmailAsync(user, token), "Invalid token."); + // Assert.False(await manager.IsEmailConfirmedAsync(user)); + //} + + // Lockout tests + + [Fact] + public async Task SingleFailureLockout() + { + var mgr = CreateManager(); + mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); + mgr.Options.Lockout.EnabledByDefault = true; + mgr.Options.Lockout.MaxFailedAccessAttempts = 0; + var user = new User("SingleFailureLockout"); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + Assert.False(await mgr.IsLockedOutAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.True(await mgr.IsLockedOutAsync(user)); + Assert.True(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user)); + } + + [Fact] + public async Task TwoFailureLockout() + { + var mgr = CreateManager(); + mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); + mgr.Options.Lockout.EnabledByDefault = true; + mgr.Options.Lockout.MaxFailedAccessAttempts = 2; + var user = new User("twoFailureLockout"); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + Assert.False(await mgr.IsLockedOutAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.True(await mgr.IsLockedOutAsync(user)); + Assert.True(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user)); + } + + [Fact] + public async Task ResetAccessCountPreventsLockout() + { + var mgr = CreateManager(); + mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); + mgr.Options.Lockout.EnabledByDefault = true; + mgr.Options.Lockout.MaxFailedAccessAttempts = 2; + var user = new User("ResetAccessCountPreventsLockout"); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + Assert.False(await mgr.IsLockedOutAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.ResetAccessFailedCountAsync(user)); + Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user)); + } + + [Fact] + public async Task CanEnableLockoutManuallyAndLockout() + { + var mgr = CreateManager(); + mgr.Options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(1); + mgr.Options.Lockout.MaxFailedAccessAttempts = 2; + var user = new User("manualLockout"); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.False(await mgr.GetLockoutEnabledAsync(user)); + Assert.False(user.LockoutEnabled); + IdentityResultAssert.IsSuccess(await mgr.SetLockoutEnabledAsync(user, true)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + Assert.False(await mgr.IsLockedOutAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user)); + IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user)); + Assert.True(await mgr.IsLockedOutAsync(user)); + Assert.True(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55)); + Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user)); + } + + [Fact] + public async Task UserNotLockedOutWithNullDateTimeAndIsSetToNullDate() + { + var mgr = CreateManager(); + mgr.Options.Lockout.EnabledByDefault = true; + var user = new User("UserNotLockedOutWithNullDateTimeAndIsSetToNullDate"); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + IdentityResultAssert.IsSuccess(await mgr.SetLockoutEndDateAsync(user, new DateTimeOffset())); + Assert.False(await mgr.IsLockedOutAsync(user)); + Assert.Equal(new DateTimeOffset(), await mgr.GetLockoutEndDateAsync(user)); + Assert.Null(user.LockoutEnd); + } + + [Fact] + public async Task LockoutFailsIfNotEnabled() + { + var mgr = CreateManager(); + var user = new User("LockoutFailsIfNotEnabled"); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.False(await mgr.GetLockoutEnabledAsync(user)); + Assert.False(user.LockoutEnabled); + IdentityResultAssert.IsFailure(await mgr.SetLockoutEndDateAsync(user, new DateTimeOffset()), + "Lockout is not enabled for this user."); + Assert.False(await mgr.IsLockedOutAsync(user)); + } + + [Fact] + public async Task LockoutEndToUtcNowMinus1SecInUserShouldNotBeLockedOut() + { + var mgr = CreateManager(); + mgr.Options.Lockout.EnabledByDefault = true; + var user = new User("LockoutEndToUtcNowMinus1SecInUserShouldNotBeLockedOut") { LockoutEnd = DateTime.UtcNow.AddSeconds(-1) }; + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + Assert.False(await mgr.IsLockedOutAsync(user)); + } + + [Fact] + public async Task LockoutEndToUtcNowSubOneSecondWithManagerShouldNotBeLockedOut() + { + var mgr = CreateManager(); + mgr.Options.Lockout.EnabledByDefault = true; + var user = new User("LockoutEndToUtcNowSubOneSecondWithManagerShouldNotBeLockedOut"); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + IdentityResultAssert.IsSuccess(await mgr.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.AddSeconds(-1))); + Assert.False(await mgr.IsLockedOutAsync(user)); + } + + [Fact] + public async Task LockoutEndToUtcNowPlus5ShouldBeLockedOut() + { + var mgr = CreateManager(); + mgr.Options.Lockout.EnabledByDefault = true; + var user = new User("LockoutEndToUtcNowPlus5ShouldBeLockedOut") { LockoutEnd = DateTime.UtcNow.AddMinutes(5) }; + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + Assert.True(await mgr.IsLockedOutAsync(user)); + } + + [Fact] + public async Task UserLockedOutWithDateTimeLocalKindNowPlus30() + { + var mgr = CreateManager(); + mgr.Options.Lockout.EnabledByDefault = true; + var user = new User("UserLockedOutWithDateTimeLocalKindNowPlus30"); + IdentityResultAssert.IsSuccess(await mgr.CreateAsync(user)); + Assert.True(await mgr.GetLockoutEnabledAsync(user)); + Assert.True(user.LockoutEnabled); + var lockoutEnd = new DateTimeOffset(DateTime.Now.AddMinutes(30).ToLocalTime()); + IdentityResultAssert.IsSuccess(await mgr.SetLockoutEndDateAsync(user, lockoutEnd)); + Assert.True(await mgr.IsLockedOutAsync(user)); + var end = await mgr.GetLockoutEndDateAsync(user); + Assert.Equal(lockoutEnd, end); + } + + // Role Tests + [Fact] + public async Task CanCreateRoleTest() + { + var manager = CreateRoleManager(); + var role = new IdentityRole("CanCreateRoleTest"); + Assert.False(await manager.RoleExistsAsync(role.Name)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Assert.True(await manager.RoleExistsAsync(role.Name)); + } + + private class AlwaysBadValidator : IUserValidator, IRoleValidator, + IPasswordValidator + { + public const string ErrorMessage = "I'm Bad."; + + public Task ValidateAsync(string password, UserManager manager, CancellationToken token) + { + return Task.FromResult(IdentityResult.Failed(ErrorMessage)); + } + + public Task ValidateAsync(RoleManager manager, IdentityRole role, CancellationToken token) + { + return Task.FromResult(IdentityResult.Failed(ErrorMessage)); + } + + public Task ValidateAsync(UserManager manager, User user, CancellationToken token) + { + return Task.FromResult(IdentityResult.Failed(ErrorMessage)); + } + } + + [Fact] + public async Task BadValidatorBlocksCreateRole() + { + var manager = CreateRoleManager(); + manager.RoleValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.CreateAsync(new IdentityRole("blocked")), + AlwaysBadValidator.ErrorMessage); + } + + [Fact] + public async Task BadValidatorBlocksRoleUpdate() + { + var manager = CreateRoleManager(); + var role = new IdentityRole("BadValidatorBlocksRoleUpdate"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + var error = AlwaysBadValidator.ErrorMessage; + manager.RoleValidator = new AlwaysBadValidator(); + IdentityResultAssert.IsFailure(await manager.UpdateAsync(role), error); + } + + [Fact] + public async Task CanDeleteRole() + { + var manager = CreateRoleManager(); + var role = new IdentityRole("CanDeleteRole"); + Assert.False(await manager.RoleExistsAsync(role.Name)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + IdentityResultAssert.IsSuccess(await manager.DeleteAsync(role)); + Assert.False(await manager.RoleExistsAsync(role.Name)); + } + + [Fact] + public async Task CanRoleFindById() + { + var manager = CreateRoleManager(); + var role = new IdentityRole("CanRoleFindById"); + Assert.Null(await manager.FindByIdAsync(role.Id)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Assert.Equal(role, await manager.FindByIdAsync(role.Id)); + } + + [Fact] + public async Task CanRoleFindByName() + { + var manager = CreateRoleManager(); + var role = new IdentityRole("CanRoleFindByName"); + Assert.Null(await manager.FindByNameAsync(role.Name)); + Assert.False(await manager.RoleExistsAsync(role.Name)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Assert.Equal(role, await manager.FindByNameAsync(role.Name)); + } + + [Fact] + public async Task CanUpdateRoleName() + { + var manager = CreateRoleManager(); + var role = new IdentityRole("CanUpdateRoleName"); + Assert.False(await manager.RoleExistsAsync(role.Name)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Assert.True(await manager.RoleExistsAsync(role.Name)); + role.Name = "CanUpdateRoleNameChanged"; + IdentityResultAssert.IsSuccess(await manager.UpdateAsync(role)); + Assert.False(await manager.RoleExistsAsync("CanUpdateRoleName")); + Assert.Equal(role, await manager.FindByNameAsync(role.Name)); + } + + [Fact] + public async Task CanQuerableRolesTest() + { + var manager = CreateRoleManager(); + var roles = GenerateRoles("CanQuerableRolesTest", 4); + foreach (var r in roles) + { + IdentityResultAssert.IsSuccess(await manager.CreateAsync(r)); + } + Assert.Equal(roles.Count, manager.Roles.Count(r => r.Name.StartsWith("CanQuerableRolesTest"))); + var r1 = manager.Roles.FirstOrDefault(r => r.Name == "CanQuerableRolesTest1"); + Assert.Equal(roles[1], r1); + } + + [Fact] + public async Task DeleteRoleNonEmptySucceedsTest() + { + // Need fail if not empty? + var userMgr = CreateManager(); + var roleMgr = CreateRoleManager(); + var role = new IdentityRole("deleteNonEmpty"); + Assert.False(await roleMgr.RoleExistsAsync(role.Name)); + IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + var user = new User("t"); + IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await userMgr.AddToRoleAsync(user, role.Name)); + var roles = await userMgr.GetRolesAsync(user); + Assert.Equal(1, roles.Count()); + IdentityResultAssert.IsSuccess(await roleMgr.DeleteAsync(role)); + Assert.Null(await roleMgr.FindByNameAsync(role.Name)); + Assert.False(await roleMgr.RoleExistsAsync(role.Name)); + // REVIEW: We should throw if deleteing a non empty role? + roles = await userMgr.GetRolesAsync(user); + + Assert.Equal(0, roles.Count()); + } + + // TODO: cascading deletes? navigation properties not working + //[Fact] + //public async Task DeleteUserRemovesFromRoleTest() + //{ + // // Need fail if not empty? + // var userMgr = CreateManager(); + // var roleMgr = CreateRoleManager(); + // var role = new IdentityRole("deleteNonEmpty"); + // Assert.False(await roleMgr.RoleExistsAsync(role.Name)); + // IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + // var user = new User("t"); + // IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user)); + // IdentityResultAssert.IsSuccess(await userMgr.AddToRoleAsync(user, role.Name)); + // Assert.Equal(1, role.Users.Count); + // IdentityResultAssert.IsSuccess(await userMgr.DeleteAsync(user)); + // role = await roleMgr.FindByIdAsync(role.Id); + // Assert.Equal(0, role.Users.Count); + //} + + [Fact] + public async Task CreateRoleFailsIfExists() + { + var manager = CreateRoleManager(); + var role = new IdentityRole("CreateRoleFailsIfExists"); + Assert.False(await manager.RoleExistsAsync(role.Name)); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Assert.True(await manager.RoleExistsAsync(role.Name)); + var role2 = new IdentityRole("CreateRoleFailsIfExists"); + IdentityResultAssert.IsFailure(await manager.CreateAsync(role2)); + } + + [Fact] + public async Task CanAddUsersToRole() + { + var context = CreateContext(); + var manager = CreateManager(context); + var roleManager = CreateRoleManager(context); + var role = new IdentityRole("CanAddUsersToRole"); + IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(role)); + var users = GenerateUsers("CanAddUsersToRole", 4); + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await manager.CreateAsync(u)); + IdentityResultAssert.IsSuccess(await manager.AddToRoleAsync(u, role.Name)); + Assert.True(await manager.IsInRoleAsync(u, role.Name)); + } + } + + private List GenerateUsers(string userNamePrefix, int count) + { + var users = new List(count); + for (var i=0; i GenerateRoles(string namePrefix, int count) + { + var roles = new List(count); + for (var i = 0; i < count; i++) + { + roles.Add(new IdentityRole(namePrefix + i)); + } + return roles; + } + + [Fact] + public async Task CanGetRolesForUser() + { + var context = CreateContext(); + var userManager = CreateManager(context); + var roleManager = CreateRoleManager(context); + var users = GenerateUsers("CanGetRolesForUser", 4); + var roles = GenerateRoles("CanGetRolesForUserRole", 4); + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await userManager.CreateAsync(u)); + } + foreach (var r in roles) + { + IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(r)); + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await userManager.AddToRoleAsync(u, r.Name)); + Assert.True(await userManager.IsInRoleAsync(u, r.Name)); + } + } + + foreach (var u in users) + { + var rs = await userManager.GetRolesAsync(u); + Assert.Equal(roles.Count, rs.Count); + foreach (var r in roles) + { + Assert.True(rs.Any(role => role == r.Name)); + } + } + } + + [Fact] + public async Task RemoveUserFromRoleWithMultipleRoles() + { + var context = CreateContext(); + var userManager = CreateManager(context); + var roleManager = CreateRoleManager(context); + var user = new User("RemoveUserFromRoleWithMultipleRoles"); + IdentityResultAssert.IsSuccess(await userManager.CreateAsync(user)); + var roles = GenerateRoles("RemoveUserFromRoleWithMultipleRoles", 4); + foreach (var r in roles) + { + IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(r)); + IdentityResultAssert.IsSuccess(await userManager.AddToRoleAsync(user, r.Name)); + Assert.True(await userManager.IsInRoleAsync(user, r.Name)); + } + IdentityResultAssert.IsSuccess(await userManager.RemoveFromRoleAsync(user, roles[2].Name)); + Assert.False(await userManager.IsInRoleAsync(user, roles[2].Name)); + } + + [Fact] + public async Task CanRemoveUsersFromRole() + { + var context = CreateContext(); + var userManager = CreateManager(context); + var roleManager = CreateRoleManager(context); + var users = GenerateUsers("CanRemoveUsersFromRole", 4); + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await userManager.CreateAsync(u)); + } + var r = new IdentityRole("CanRemoveUsersFromRole"); + IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(r)); + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await userManager.AddToRoleAsync(u, r.Name)); + Assert.True(await userManager.IsInRoleAsync(u, r.Name)); + } + foreach (var u in users) + { + IdentityResultAssert.IsSuccess(await userManager.RemoveFromRoleAsync(u, r.Name)); + Assert.False(await userManager.IsInRoleAsync(u, r.Name)); + } + } + + [Fact] + public async Task RemoveUserNotInRoleFails() + { + var context = CreateContext(); + var userMgr = CreateManager(context); + var roleMgr = CreateRoleManager(context); + var role = new IdentityRole("RemoveUserNotInRoleFails"); + var user = new User("RemoveUserNotInRoleFails"); + IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + var result = await userMgr.RemoveFromRoleAsync(user, role.Name); + IdentityResultAssert.IsFailure(result, "User is not in role."); + } + + [Fact] + public async Task AddUserToUnknownRoleFails() + { + var manager = CreateManager(); + var u = new User("AddUserToUnknownRoleFails"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(u)); + await Assert.ThrowsAsync( + async () => await manager.AddToRoleAsync(u, "bogus")); + } + + [Fact] + public async Task AddUserToRoleFailsIfAlreadyInRole() + { + var context = CreateContext(); + var userMgr = CreateManager(context); + var roleMgr = CreateRoleManager(context); + var role = new IdentityRole("AddUserToRoleFailsIfAlreadyInRole"); + var user = new User("AddUserToRoleFailsIfAlreadyInRoleUser"); + IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user)); + IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + IdentityResultAssert.IsSuccess(await userMgr.AddToRoleAsync(user, role.Name)); + Assert.True(await userMgr.IsInRoleAsync(user, role.Name)); + IdentityResultAssert.IsFailure(await userMgr.AddToRoleAsync(user, role.Name), "User already in role."); + } + + [Fact] + public async Task CanFindRoleByNameWithManager() + { + var roleMgr = CreateRoleManager(); + var role = new IdentityRole("CanFindRoleByNameWithManager"); + IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + Assert.Equal(role.Id, (await roleMgr.FindByNameAsync(role.Name)).Id); + } + + [Fact] + public async Task CanFindRoleWithManager() + { + var roleMgr = CreateRoleManager(); + var role = new IdentityRole("CanFindRoleWithManager"); + IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role)); + Assert.Equal(role, await roleMgr.FindByIdAsync(role.Id)); + } + + [Fact] + public async Task SetPhoneNumberTest() + { + var manager = CreateManager(); + const string userName = "SetPhoneNumberTest"; + var user = new User(userName) { PhoneNumber = "123-456-7890" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + Assert.Equal(await manager.GetPhoneNumberAsync(user), "123-456-7890"); + IdentityResultAssert.IsSuccess(await manager.SetPhoneNumberAsync(user, "111-111-1111")); + Assert.Equal(await manager.GetPhoneNumberAsync(user), "111-111-1111"); + Assert.NotEqual(stamp, user.SecurityStamp); + } + +#if NET45 + [Fact] + public async Task CanChangePhoneNumber() + { + var manager = CreateManager(); + const string userName = "CanChangePhoneNumber"; + var user = new User(userName) { PhoneNumber = "123-456-7890" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.IsPhoneNumberConfirmedAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + var token1 = await manager.GenerateChangePhoneNumberTokenAsync(user, "111-111-1111"); + IdentityResultAssert.IsSuccess(await manager.ChangePhoneNumberAsync(user, "111-111-1111", token1)); + Assert.True(await manager.IsPhoneNumberConfirmedAsync(user)); + Assert.Equal(await manager.GetPhoneNumberAsync(user), "111-111-1111"); + Assert.NotEqual(stamp, user.SecurityStamp); + } + + [Fact] + public async Task ChangePhoneNumberFailsWithWrongToken() + { + var manager = CreateManager(); + const string userName = "ChangePhoneNumberFailsWithWrongToken"; + var user = new User(userName) { PhoneNumber = "123-456-7890" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.IsPhoneNumberConfirmedAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + IdentityResultAssert.IsFailure(await manager.ChangePhoneNumberAsync(user, "111-111-1111", "bogus"), + "Invalid token."); + Assert.False(await manager.IsPhoneNumberConfirmedAsync(user)); + Assert.Equal(await manager.GetPhoneNumberAsync(user), "123-456-7890"); + Assert.Equal(stamp, user.SecurityStamp); + } + + [Fact] + public async Task CanVerifyPhoneNumber() + { + var manager = CreateManager(); + const string userName = "CanVerifyPhoneNumber"; + var user = new User(userName); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + const string num1 = "111-123-4567"; + const string num2 = "111-111-1111"; + var token1 = await manager.GenerateChangePhoneNumberTokenAsync(user, num1); + var token2 = await manager.GenerateChangePhoneNumberTokenAsync(user, num2); + Assert.NotEqual(token1, token2); + Assert.True(await manager.VerifyChangePhoneNumberTokenAsync(user, token1, num1)); + Assert.True(await manager.VerifyChangePhoneNumberTokenAsync(user, token2, num2)); + Assert.False(await manager.VerifyChangePhoneNumberTokenAsync(user, token2, num1)); + Assert.False(await manager.VerifyChangePhoneNumberTokenAsync(user, token1, num2)); + } +#endif + + private class EmailTokenProvider : IUserTokenProvider + { + public Task GenerateAsync(string purpose, UserManager manager, User user, CancellationToken token) + { + return Task.FromResult(MakeToken(purpose)); + } + + public Task ValidateAsync(string purpose, string token, UserManager manager, + User user, CancellationToken cancellationToken) + { + return Task.FromResult(token == MakeToken(purpose)); + } + + public Task NotifyAsync(string token, UserManager manager, User user, CancellationToken cancellationToken) + { + return manager.SendEmailAsync(user, token, token); + } + + public async Task IsValidProviderForUserAsync(UserManager manager, User user, CancellationToken token) + { + return !string.IsNullOrEmpty(await manager.GetEmailAsync(user)); + } + + private static string MakeToken(string purpose) + { + return "email:" + purpose; + } + } + + private class SmsTokenProvider : IUserTokenProvider + { + public Task GenerateAsync(string purpose, UserManager manager, User user, CancellationToken token) + { + return Task.FromResult(MakeToken(purpose)); + } + + public Task ValidateAsync(string purpose, string token, UserManager manager, + User user, CancellationToken cancellationToken) + { + return Task.FromResult(token == MakeToken(purpose)); + } + + public Task NotifyAsync(string token, UserManager manager, User user, CancellationToken cancellationToken) + { + return manager.SendSmsAsync(user, token); + } + + public async Task IsValidProviderForUserAsync(UserManager manager, User user, CancellationToken token) + { + return !string.IsNullOrEmpty(await manager.GetPhoneNumberAsync(user)); + } + + private static string MakeToken(string purpose) + { + return "sms:" + purpose; + } + } + + [Fact] + public async Task CanEmailTwoFactorToken() + { + var manager = CreateManager(); + var messageService = new TestMessageService(); + manager.EmailService = messageService; + const string factorId = "EmailCode"; + manager.RegisterTwoFactorProvider(factorId, new EmailTokenProvider()); + var user = new User("CanEmailTwoFactorToken") { Email = "foo@foo.com" }; + const string password = "password"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + var token = await manager.GenerateTwoFactorTokenAsync(user, factorId); + Assert.NotNull(token); + Assert.Null(messageService.Message); + IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token)); + Assert.NotNull(messageService.Message); + Assert.Equal(token, messageService.Message.Subject); + Assert.Equal(token, messageService.Message.Body); + Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token)); + } + + [Fact] + public async Task NotifyWithUnknownProviderFails() + { + var manager = CreateManager(); + var user = new User("NotifyFail"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + await + ExceptionAssert.ThrowsAsync( + async () => await manager.NotifyTwoFactorTokenAsync(user, "Bogus", "token"), + "No IUserTwoFactorProvider for 'Bogus' is registered."); + } + + + //[Fact] + //public async Task EmailTokenFactorWithFormatTest() + //{ + // var manager = CreateManager(); + // var messageService = new TestMessageService(); + // manager.EmailService = messageService; + // const string factorId = "EmailCode"; + // manager.RegisterTwoFactorProvider(factorId, new EmailTokenProvider + // { + // Subject = "Security Code", + // BodyFormat = "Your code is: {0}" + // }); + // var user = new User("EmailCodeTest") { Email = "foo@foo.com" }; + // const string password = "password"; + // IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password)); + // var stamp = user.SecurityStamp; + // Assert.NotNull(stamp); + // var token = await manager.GenerateTwoFactorTokenAsync(user, factorId); + // Assert.NotNull(token); + // Assert.Null(messageService.Message); + // IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token)); + // Assert.NotNull(messageService.Message); + // Assert.Equal("Security Code", messageService.Message.Subject); + // Assert.Equal("Your code is: " + token, messageService.Message.Body); + // Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token)); + //} + + //[Fact] + //public async Task EmailFactorFailsAfterSecurityStampChangeTest() + //{ + // var manager = CreateManager(); + // const string factorId = "EmailCode"; + // manager.RegisterTwoFactorProvider(factorId, new EmailTokenProvider()); + // var user = new User("EmailCodeTest") { Email = "foo@foo.com" }; + // IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + // var stamp = user.SecurityStamp; + // Assert.NotNull(stamp); + // var token = await manager.GenerateTwoFactorTokenAsync(user, factorId); + // Assert.NotNull(token); + // IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user)); + // Assert.False(await manager.VerifyTwoFactorTokenAsync(user, factorId, token)); + //} + + [Fact] + public async Task EnableTwoFactorChangesSecurityStamp() + { + var manager = CreateManager(); + var user = new User("EnableTwoFactorChangesSecurityStamp"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + IdentityResultAssert.IsSuccess(await manager.SetTwoFactorEnabledAsync(user, true)); + Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user)); + Assert.True(await manager.GetTwoFactorEnabledAsync(user)); + } + + [Fact] + public async Task CanSendSms() + { + var manager = CreateManager(); + var messageService = new TestMessageService(); + manager.SmsService = messageService; + var user = new User("SmsTest") { PhoneNumber = "4251234567" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + await manager.SendSmsAsync(user, "Hi"); + Assert.NotNull(messageService.Message); + Assert.Equal("Hi", messageService.Message.Body); + } + + [Fact] + public async Task CanSendEmail() + { + var manager = CreateManager(); + var messageService = new TestMessageService(); + manager.EmailService = messageService; + var user = new User("CanSendEmail") { Email = "foo@foo.com" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + await manager.SendEmailAsync(user, "Hi", "Body"); + Assert.NotNull(messageService.Message); + Assert.Equal("Hi", messageService.Message.Subject); + Assert.Equal("Body", messageService.Message.Body); + } + + [Fact] + public async Task CanSmsTwoFactorToken() + { + var manager = CreateManager(); + var messageService = new TestMessageService(); + manager.SmsService = messageService; + const string factorId = "PhoneCode"; + manager.RegisterTwoFactorProvider(factorId, new SmsTokenProvider()); + var user = new User("CanSmsTwoFactorToken") { PhoneNumber = "4251234567" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var stamp = user.SecurityStamp; + Assert.NotNull(stamp); + var token = await manager.GenerateTwoFactorTokenAsync(user, factorId); + Assert.NotNull(token); + Assert.Null(messageService.Message); + IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token)); + Assert.NotNull(messageService.Message); + Assert.Equal(token, messageService.Message.Body); + Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token)); + } + + //[Fact] + //public async Task PhoneTokenFactorFormatTest() + //{ + // var manager = CreateManager(); + // var messageService = new TestMessageService(); + // manager.SmsService = messageService; + // const string factorId = "PhoneCode"; + // manager.RegisterTwoFactorProvider(factorId, new PhoneNumberTokenProvider + // { + // MessageFormat = "Your code is: {0}" + // }); + // var user = new User("PhoneCodeTest") { PhoneNumber = "4251234567" }; + // IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + // var stamp = user.SecurityStamp; + // Assert.NotNull(stamp); + // var token = await manager.GenerateTwoFactorTokenAsync(user, factorId); + // Assert.NotNull(token); + // Assert.Null(messageService.Message); + // IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token)); + // Assert.NotNull(messageService.Message); + // Assert.Equal("Your code is: " + token, messageService.Message.Body); + // Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token)); + //} + + [Fact] + public async Task GenerateTwoFactorWithUnknownFactorProviderWillThrow() + { + var manager = CreateManager(); + var user = new User("GenerateTwoFactorWithUnknownFactorProviderWillThrow"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + const string error = "No IUserTwoFactorProvider for 'bogus' is registered."; + await + ExceptionAssert.ThrowsAsync( + () => manager.GenerateTwoFactorTokenAsync(user, "bogus"), error); + await ExceptionAssert.ThrowsAsync( + () => manager.VerifyTwoFactorTokenAsync(user, "bogus", "bogus"), error); + } + + [Fact] + public async Task GetValidTwoFactorTestEmptyWithNoProviders() + { + var manager = CreateManager(); + var user = new User("GetValidTwoFactorTestEmptyWithNoProviders"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var factors = await manager.GetValidTwoFactorProvidersAsync(user); + Assert.NotNull(factors); + Assert.True(!factors.Any()); + } + + [Fact] + public async Task CanGetValidTwoFactor() + { + var manager = CreateManager(); + manager.RegisterTwoFactorProvider("phone", new SmsTokenProvider()); + manager.RegisterTwoFactorProvider("email", new EmailTokenProvider()); + var user = new User("CanGetValidTwoFactor"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var factors = await manager.GetValidTwoFactorProvidersAsync(user); + Assert.NotNull(factors); + Assert.True(!factors.Any()); + IdentityResultAssert.IsSuccess(await manager.SetPhoneNumberAsync(user, "111-111-1111")); + factors = await manager.GetValidTwoFactorProvidersAsync(user); + Assert.NotNull(factors); + Assert.True(factors.Count() == 1); + Assert.Equal("phone", factors[0]); + IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, "test@test.com")); + factors = await manager.GetValidTwoFactorProvidersAsync(user); + Assert.NotNull(factors); + Assert.True(factors.Count() == 2); + IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, null)); + factors = await manager.GetValidTwoFactorProvidersAsync(user); + Assert.NotNull(factors); + Assert.True(factors.Count() == 1); + Assert.Equal("phone", factors[0]); + } + + //[Fact] + //public async Task PhoneFactorFailsAfterSecurityStampChangeTest() + //{ + // var manager = CreateManager(); + // var factorId = "PhoneCode"; + // manager.RegisterTwoFactorProvider(factorId, new PhoneNumberTokenProvider()); + // var user = new User("PhoneCodeTest"); + // user.PhoneNumber = "4251234567"; + // IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + // var stamp = user.SecurityStamp; + // Assert.NotNull(stamp); + // var token = await manager.GenerateTwoFactorTokenAsync(user, factorId); + // Assert.NotNull(token); + // IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user)); + // Assert.False(await manager.VerifyTwoFactorTokenAsync(user, factorId, token)); + //} + + [Fact] + public async Task VerifyTokenFromWrongTokenProviderFails() + { + var manager = CreateManager(); + manager.RegisterTwoFactorProvider("PhoneCode", new SmsTokenProvider()); + manager.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider()); + var user = new User("WrongTokenProviderTest") { PhoneNumber = "4251234567" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + var token = await manager.GenerateTwoFactorTokenAsync(user, "PhoneCode"); + Assert.NotNull(token); + Assert.False(await manager.VerifyTwoFactorTokenAsync(user, "EmailCode", token)); + } + + [Fact] + public async Task VerifyWithWrongSmsTokenFails() + { + var manager = CreateManager(); + const string factorId = "PhoneCode"; + manager.RegisterTwoFactorProvider(factorId, new SmsTokenProvider()); + var user = new User("VerifyWithWrongSmsTokenFails") { PhoneNumber = "4251234567" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.VerifyTwoFactorTokenAsync(user, factorId, "bogus")); + } + + public class TestMessageService : IIdentityMessageService + { + public IdentityMessage Message { get; set; } + + public Task SendAsync(IdentityMessage message, CancellationToken token) + { + Message = message; + return Task.FromResult(0); + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.InMemory.Test/IdentityBuilderExtensions.cs b/test/Microsoft.AspNet.Identity.InMemory.Test/IdentityBuilderExtensions.cs new file mode 100644 index 0000000000..69d2f145a2 --- /dev/null +++ b/test/Microsoft.AspNet.Identity.InMemory.Test/IdentityBuilderExtensions.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNet.Identity.InMemory; +using Microsoft.Framework.DependencyInjection; + +namespace Microsoft.AspNet.Identity +{ + public static class IdentityBuilderExtensions + { + public static IdentityBuilder AddInMemory(this IdentityBuilder builder) + where TUser : IdentityUser + where TRole : IdentityRole + { + builder.Services.AddSingleton, InMemoryUserStore>(); + builder.Services.AddScoped, UserManager>(); + builder.Services.AddSingleton, InMemoryRoleStore>(); + builder.Services.AddScoped, RoleManager>(); + return builder; + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.InMemory.Test/InMemoryRoleStore.cs b/test/Microsoft.AspNet.Identity.InMemory.Test/InMemoryRoleStore.cs new file mode 100644 index 0000000000..5acd53943e --- /dev/null +++ b/test/Microsoft.AspNet.Identity.InMemory.Test/InMemoryRoleStore.cs @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Identity.InMemory +{ + public class InMemoryRoleStore : IQueryableRoleStore, IRoleClaimStore where TRole : IdentityRole + + { + private readonly Dictionary _roles = new Dictionary(); + + public Task CreateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + _roles[role.Id] = role; + return Task.FromResult(0); + } + + public Task DeleteAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + if (role == null || !_roles.ContainsKey(role.Id)) + { + throw new InvalidOperationException("Unknown role"); + } + _roles.Remove(role.Id); + return Task.FromResult(0); + } + + public Task GetRoleIdAsync(TRole role, CancellationToken cancellationToken = new CancellationToken()) + { + return Task.FromResult(role.Id); + } + + public Task GetRoleNameAsync(TRole role, CancellationToken cancellationToken = new CancellationToken()) + { + return Task.FromResult(role.Name); + } + + public Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken = new CancellationToken()) + { + role.Name = roleName; + return Task.FromResult(0); + } + + public Task UpdateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + _roles[role.Id] = role; + return Task.FromResult(0); + } + + public Task FindByIdAsync(string roleId, CancellationToken cancellationToken = default(CancellationToken)) + { + if (_roles.ContainsKey(roleId)) + { + return Task.FromResult(_roles[roleId]); + } + return Task.FromResult(null); + } + + public Task FindByNameAsync(string roleName, CancellationToken cancellationToken = default(CancellationToken)) + { + return + Task.FromResult( + Roles.SingleOrDefault(r => String.Equals(r.Name, roleName, StringComparison.OrdinalIgnoreCase))); + } + + public void Dispose() + { + } + + public Task> GetClaimsAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken)) + { + var claims = role.Claims.Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToList(); + return Task.FromResult>(claims); + } + + public Task AddClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken)) + { + role.Claims.Add(new IdentityRoleClaim { ClaimType = claim.Type, ClaimValue = claim.Value, RoleId = role.Id }); + return Task.FromResult(0); + } + + public Task RemoveClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken)) + { + var entity = + role.Claims.FirstOrDefault( + ur => ur.RoleId == role.Id && ur.ClaimType == claim.Type && ur.ClaimValue == claim.Value); + if (entity != null) + { + role.Claims.Remove(entity); + } + return Task.FromResult(0); + } + + public IQueryable Roles + { + get { return _roles.Values.AsQueryable(); } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.InMemory.Test/InMemoryUserStore.cs b/test/Microsoft.AspNet.Identity.InMemory.Test/InMemoryUserStore.cs new file mode 100644 index 0000000000..c3f7a4ba39 --- /dev/null +++ b/test/Microsoft.AspNet.Identity.InMemory.Test/InMemoryUserStore.cs @@ -0,0 +1,329 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNet.Identity.InMemory +{ + public class InMemoryUserStore : + IUserLoginStore, + IUserRoleStore, + IUserClaimStore, + IUserPasswordStore, + IUserSecurityStampStore, + IUserEmailStore, + IUserLockoutStore, + IUserPhoneNumberStore, + IQueryableUserStore, + IUserTwoFactorStore + where TUser : IdentityUser + { + private readonly Dictionary _logins = + new Dictionary(new LoginComparer()); + + private readonly Dictionary _users = new Dictionary(); + + public IQueryable Users + { + get { return _users.Values.AsQueryable(); } + } + + public Task> GetClaimsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + var claims = user.Claims.Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToList(); + return Task.FromResult>(claims); + } + + public Task AddClaimAsync(TUser user, Claim claim, CancellationToken cancellationToken = default(CancellationToken)) + { + user.Claims.Add(new IdentityUserClaim { ClaimType = claim.Type, ClaimValue = claim.Value, UserId = user.Id }); + return Task.FromResult(0); + } + + public Task RemoveClaimAsync(TUser user, Claim claim, CancellationToken cancellationToken = default(CancellationToken)) + { + var entity = + user.Claims.FirstOrDefault( + uc => uc.UserId == user.Id && uc.ClaimType == claim.Type && uc.ClaimValue == claim.Value); + if (entity != null) + { + user.Claims.Remove(entity); + } + return Task.FromResult(0); + } + + public Task SetEmailAsync(TUser user, string email, CancellationToken cancellationToken = default(CancellationToken)) + { + user.Email = email; + return Task.FromResult(0); + } + + public Task GetEmailAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult(user.Email); + } + + public Task GetEmailConfirmedAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult(user.EmailConfirmed); + } + + public Task SetEmailConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken)) + { + user.EmailConfirmed = confirmed; + return Task.FromResult(0); + } + + public Task FindByEmailAsync(string email, CancellationToken cancellationToken = default(CancellationToken)) + { + return + Task.FromResult( + Users.FirstOrDefault(u => String.Equals(u.Email, email, StringComparison.OrdinalIgnoreCase))); + } + + public Task GetLockoutEndDateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult(user.LockoutEnd); + } + + public Task SetLockoutEndDateAsync(TUser user, DateTimeOffset lockoutEnd, CancellationToken cancellationToken = default(CancellationToken)) + { + user.LockoutEnd = lockoutEnd; + return Task.FromResult(0); + } + + public Task IncrementAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + user.AccessFailedCount++; + return Task.FromResult(user.AccessFailedCount); + } + + public Task ResetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + user.AccessFailedCount = 0; + return Task.FromResult(0); + } + + public Task GetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult(user.AccessFailedCount); + } + + public Task GetLockoutEnabledAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult(user.LockoutEnabled); + } + + public Task SetLockoutEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken)) + { + user.LockoutEnabled = enabled; + return Task.FromResult(0); + } + + public Task AddLoginAsync(TUser user, UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken)) + { + user.Logins.Add(new IdentityUserLogin + { + UserId = user.Id, + LoginProvider = login.LoginProvider, + ProviderKey = login.ProviderKey + }); + _logins[login] = user; + return Task.FromResult(0); + } + + public Task RemoveLoginAsync(TUser user, UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken)) + { + var loginEntity = + user.Logins.SingleOrDefault( + l => + l.ProviderKey == login.ProviderKey && l.LoginProvider == login.LoginProvider && + l.UserId == user.Id); + if (loginEntity != null) + { + user.Logins.Remove(loginEntity); + } + _logins[login] = null; + return Task.FromResult(0); + } + + public Task> GetLoginsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + var logins = user.Logins.Select(l => new UserLoginInfo(l.LoginProvider, l.ProviderKey)).ToList(); + return Task.FromResult>(logins); + } + + public Task FindByLoginAsync(UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken)) + { + if (_logins.ContainsKey(login)) + { + return Task.FromResult(_logins[login]); + } + return Task.FromResult(null); + } + + public Task GetUserIdAsync(TUser user, CancellationToken cancellationToken = new CancellationToken()) + { + return Task.FromResult(user.Id); + } + + public Task GetUserNameAsync(TUser user, CancellationToken cancellationToken = new CancellationToken()) + { + return Task.FromResult(user.UserName); + } + + public Task SetUserNameAsync(TUser user, string userName, CancellationToken cancellationToken = new CancellationToken()) + { + user.UserName = userName; + return Task.FromResult(0); + } + + public Task CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + _users[user.Id] = user; + return Task.FromResult(0); + } + + public Task UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + _users[user.Id] = user; + return Task.FromResult(0); + } + + public Task FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken)) + { + if (_users.ContainsKey(userId)) + { + return Task.FromResult(_users[userId]); + } + return Task.FromResult(null); + } + + public void Dispose() + { + } + + public Task FindByNameAsync(string userName, CancellationToken cancellationToken = default(CancellationToken)) + { + return + Task.FromResult( + Users.FirstOrDefault(u => String.Equals(u.UserName, userName, StringComparison.OrdinalIgnoreCase))); + } + + public Task DeleteAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + if (user == null || !_users.ContainsKey(user.Id)) + { + throw new InvalidOperationException("Unknown user"); + } + _users.Remove(user.Id); + return Task.FromResult(0); + } + + public Task SetPasswordHashAsync(TUser user, string passwordHash, CancellationToken cancellationToken = default(CancellationToken)) + { + user.PasswordHash = passwordHash; + return Task.FromResult(0); + } + + public Task GetPasswordHashAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult(user.PasswordHash); + } + + public Task HasPasswordAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult(user.PasswordHash != null); + } + + public Task SetPhoneNumberAsync(TUser user, string phoneNumber, CancellationToken cancellationToken = default(CancellationToken)) + { + user.PhoneNumber = phoneNumber; + return Task.FromResult(0); + } + + public Task GetPhoneNumberAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult(user.PhoneNumber); + } + + public Task GetPhoneNumberConfirmedAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult(user.PhoneNumberConfirmed); + } + + public Task SetPhoneNumberConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken)) + { + user.PhoneNumberConfirmed = confirmed; + return Task.FromResult(0); + } + + // RoleId == roleName for InMemory + public Task AddToRoleAsync(TUser user, string role, CancellationToken cancellationToken = default(CancellationToken)) + { + user.Roles.Add(new IdentityUserRole { RoleId = role, UserId = user.Id }); + return Task.FromResult(0); + } + + // RoleId == roleName for InMemory + public Task RemoveFromRoleAsync(TUser user, string role, CancellationToken cancellationToken = default(CancellationToken)) + { + var roleEntity = user.Roles.SingleOrDefault(ur => ur.RoleId == role); + if (roleEntity != null) + { + user.Roles.Remove(roleEntity); + } + return Task.FromResult(0); + } + + public Task> GetRolesAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult>(user.Roles.Select(ur => ur.RoleId).ToList()); + } + + public Task IsInRoleAsync(TUser user, string role, CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult(user.Roles.Any(ur => ur.RoleId == role)); + } + + public Task SetSecurityStampAsync(TUser user, string stamp, CancellationToken cancellationToken = default(CancellationToken)) + { + user.SecurityStamp = stamp; + return Task.FromResult(0); + } + + public Task GetSecurityStampAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult(user.SecurityStamp); + } + + public Task SetTwoFactorEnabledAsync(TUser user, bool enabled, CancellationToken cancellationToken = default(CancellationToken)) + { + user.TwoFactorEnabled = enabled; + return Task.FromResult(0); + } + + public Task GetTwoFactorEnabledAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) + { + return Task.FromResult(user.TwoFactorEnabled); + } + + private class LoginComparer : IEqualityComparer + { + public bool Equals(UserLoginInfo x, UserLoginInfo y) + { + return x.LoginProvider == y.LoginProvider && x.ProviderKey == y.ProviderKey; + } + + public int GetHashCode(UserLoginInfo obj) + { + return (obj.ProviderKey + "--" + obj.LoginProvider).GetHashCode(); + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.InMemory.Test/Microsoft.AspNet.Identity.InMemory.Test.kproj b/test/Microsoft.AspNet.Identity.InMemory.Test/Microsoft.AspNet.Identity.InMemory.Test.kproj index d4e86bce7f..e73267a5a0 100644 --- a/test/Microsoft.AspNet.Identity.InMemory.Test/Microsoft.AspNet.Identity.InMemory.Test.kproj +++ b/test/Microsoft.AspNet.Identity.InMemory.Test/Microsoft.AspNet.Identity.InMemory.Test.kproj @@ -21,8 +21,11 @@ + + + - + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.InMemory.Test/project.json b/test/Microsoft.AspNet.Identity.InMemory.Test/project.json index 4874ecf7dd..3f668b4d65 100644 --- a/test/Microsoft.AspNet.Identity.InMemory.Test/project.json +++ b/test/Microsoft.AspNet.Identity.InMemory.Test/project.json @@ -2,7 +2,6 @@ "dependencies": { "Microsoft.AspNet.Http" : "1.0.0-*", "Microsoft.AspNet.Identity" : "", - "Microsoft.AspNet.Identity.InMemory" : "", "Microsoft.AspNet.PipelineCore" : "1.0.0-*", "Microsoft.AspNet.RequestContainer" : "1.0.0-*", "Microsoft.AspNet.Testing" : "1.0.0-*", diff --git a/test/Microsoft.AspNet.Identity.Test/ClaimsIdentityFactoryTest.cs b/test/Microsoft.AspNet.Identity.Test/ClaimsIdentityFactoryTest.cs index 5e38e9c42c..9e14660fa3 100644 --- a/test/Microsoft.AspNet.Identity.Test/ClaimsIdentityFactoryTest.cs +++ b/test/Microsoft.AspNet.Identity.Test/ClaimsIdentityFactoryTest.cs @@ -6,8 +6,6 @@ using System.Linq; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; -using Microsoft.Framework.DependencyInjection; -using Microsoft.Framework.DependencyInjection.Fallback; using Moq; using Xunit; @@ -18,43 +16,64 @@ namespace Microsoft.AspNet.Identity.Test [Fact] public async Task CreateIdentityNullChecks() { - var factory = new ClaimsIdentityFactory(); - var manager = MockHelpers.MockUserManager().Object; - await Assert.ThrowsAsync("manager", - async () => await factory.CreateAsync(null, null, "whatever")); + var userManager = MockHelpers.MockUserManager().Object; + var roleManager = MockHelpers.MockRoleManager().Object; + var factory = new ClaimsIdentityFactory(userManager, roleManager); await Assert.ThrowsAsync("user", - async () => await factory.CreateAsync(manager, null, "whatever")); + async () => await factory.CreateAsync(null, "whatever")); await Assert.ThrowsAsync("value", - async () => await factory.CreateAsync(manager, new TestUser(), null)); + async () => await factory.CreateAsync(new TestUser(), null)); } #if NET45 //TODO: Mock fails in K (this works fine in net45) [Theory] - [InlineData(false, false)] - [InlineData(false, true)] - [InlineData(true, false)] - [InlineData(true, true)] - public async Task EnsureClaimsIdentityHasExpectedClaims(bool supportRoles, bool supportClaims) + [InlineData(false, false, false)] + [InlineData(false, true, false)] + [InlineData(true, false, false)] + [InlineData(true, true, false)] + [InlineData(true, false, true)] + [InlineData(true, true, true)] + public async Task EnsureClaimsIdentityHasExpectedClaims(bool supportRoles, bool supportClaims, bool supportRoleClaims) { // Setup var userManager = MockHelpers.MockUserManager(); + var roleManager = MockHelpers.MockRoleManager(); var user = new TestUser { UserName = "Foo" }; - userManager.Setup(m => m.SupportsUserRole).Returns(supportRoles); userManager.Setup(m => m.SupportsUserClaim).Returns(supportClaims); + userManager.Setup(m => m.SupportsUserRole).Returns(supportRoles); userManager.Setup(m => m.GetUserIdAsync(user, CancellationToken.None)).ReturnsAsync(user.Id); userManager.Setup(m => m.GetUserNameAsync(user, CancellationToken.None)).ReturnsAsync(user.UserName); - var roleClaims = new[] { "Admin", "Local" }; - userManager.Setup(m => m.GetRolesAsync(user, CancellationToken.None)).ReturnsAsync(roleClaims); + var roleClaims = new[] { "Admin", "Local" }; + if (supportRoles) + { + userManager.Setup(m => m.GetRolesAsync(user, CancellationToken.None)).ReturnsAsync(roleClaims); + roleManager.Setup(m => m.SupportsRoleClaims).Returns(supportRoleClaims); + } var userClaims = new[] { new Claim("Whatever", "Value"), new Claim("Whatever2", "Value2") }; - userManager.Setup(m => m.GetClaimsAsync(user, CancellationToken.None)).ReturnsAsync(userClaims); + if (supportClaims) + { + userManager.Setup(m => m.GetClaimsAsync(user, CancellationToken.None)).ReturnsAsync(userClaims); + } userManager.Object.Options = new IdentityOptions(); + var admin = new TestRole() { Name = "Admin" }; + var local = new TestRole() { Name = "Local" }; + var adminClaims = new[] { new Claim("AdminClaim1", "Value1"), new Claim("AdminClaim2", "Value2") }; + var localClaims = new[] { new Claim("LocalClaim1", "Value1"), new Claim("LocalClaim2", "Value2") }; + if (supportRoleClaims) + { + roleManager.Setup(m => m.FindByNameAsync("Admin", CancellationToken.None)).ReturnsAsync(admin); + roleManager.Setup(m => m.FindByNameAsync("Local", CancellationToken.None)).ReturnsAsync(local); + roleManager.Setup(m => m.GetClaimsAsync(admin, CancellationToken.None)).ReturnsAsync(adminClaims); + roleManager.Setup(m => m.GetClaimsAsync(local, CancellationToken.None)).ReturnsAsync(localClaims); + } + const string authType = "Microsoft.AspNet.Identity"; - var factory = new ClaimsIdentityFactory(); + var factory = new ClaimsIdentityFactory(userManager.Object, roleManager.Object); // Act - var identity = await factory.CreateAsync(userManager.Object, user, authType); + var identity = await factory.CreateAsync(user, authType); // Assert var manager = userManager.Object; @@ -71,6 +90,16 @@ namespace Microsoft.AspNet.Identity.Test { Assert.Equal(supportClaims, claims.Any(c => c.Type == cl.Type && c.Value == cl.Value)); } + foreach (var cl in adminClaims) + { + Assert.Equal(supportRoleClaims, claims.Any(c => c.Type == cl.Type && c.Value == cl.Value)); + } + foreach (var cl in localClaims) + { + Assert.Equal(supportRoleClaims, claims.Any(c => c.Type == cl.Type && c.Value == cl.Value)); + } + userManager.VerifyAll(); + roleManager.VerifyAll(); } #endif } diff --git a/test/Microsoft.AspNet.Identity.Test/IdentityBuilderTest.cs b/test/Microsoft.AspNet.Identity.Test/IdentityBuilderTest.cs index 35352e7f2c..8eb3374c99 100644 --- a/test/Microsoft.AspNet.Identity.Test/IdentityBuilderTest.cs +++ b/test/Microsoft.AspNet.Identity.Test/IdentityBuilderTest.cs @@ -34,12 +34,6 @@ namespace Microsoft.AspNet.Identity.Test CanOverride(new PasswordHasher()); } - [Fact] - public void CanSpecifyClaimsIdentityFactoryInstance() - { - CanOverride>(new ClaimsIdentityFactory()); - } - [Fact] public void EnsureDefaultServices() { @@ -55,9 +49,6 @@ namespace Microsoft.AspNet.Identity.Test var hasher = provider.GetService() as PasswordHasher; Assert.NotNull(hasher); - - var claimsFactory = provider.GetService>() as ClaimsIdentityFactory; - Assert.NotNull(claimsFactory); } private static void CanOverride(TService instance) diff --git a/test/Microsoft.AspNet.Identity.Test/UserManagerTest.cs b/test/Microsoft.AspNet.Identity.Test/UserManagerTest.cs index f6c549ce73..1b41f0b0fd 100644 --- a/test/Microsoft.AspNet.Identity.Test/UserManagerTest.cs +++ b/test/Microsoft.AspNet.Identity.Test/UserManagerTest.cs @@ -23,8 +23,8 @@ namespace Microsoft.AspNet.Identity.Test public TestManager(IUserStore store, IOptionsAccessor optionsAccessor, IPasswordHasher passwordHasher, IUserValidator userValidator, - IPasswordValidator passwordValidator, IClaimsIdentityFactory claimsIdentityFactory) - : base(store, optionsAccessor, passwordHasher, userValidator, passwordValidator, claimsIdentityFactory) { } + IPasswordValidator passwordValidator) + : base(store, optionsAccessor, passwordHasher, userValidator, passwordValidator) { } } [Fact] @@ -426,29 +426,18 @@ namespace Microsoft.AspNet.Identity.Test var passwordHasher = new PasswordHasher(); var userValidator = new UserValidator(); var passwordValidator = new PasswordValidator(); - var claimsIdentityFactory = new ClaimsIdentityFactory(); Assert.Throws("store", - () => new UserManager(null, null, null, null, null, null)); + () => new UserManager(null, null, null, null, null)); Assert.Throws("optionsAccessor", - () => new UserManager(store, null, null, null, null, null)); + () => new UserManager(store, null, null, null, null)); Assert.Throws("passwordHasher", - () => new UserManager(store, optionsAccessor, null, null, null, null)); - Assert.Throws("userValidator", - () => new UserManager(store, optionsAccessor, passwordHasher, null, null, null)); - Assert.Throws("passwordValidator", - () => new UserManager(store, optionsAccessor, passwordHasher, userValidator, null, null)); - Assert.Throws("claimsIdentityFactory", - () => new UserManager(store, optionsAccessor, passwordHasher, userValidator, passwordValidator, null)); + () => new UserManager(store, optionsAccessor, null, null, null)); - var manager = new UserManager(store, optionsAccessor, passwordHasher, userValidator, passwordValidator, claimsIdentityFactory); + var manager = new UserManager(store, optionsAccessor, passwordHasher, userValidator, passwordValidator); - Assert.Throws("value", () => manager.ClaimsIdentityFactory = null); Assert.Throws("value", () => manager.PasswordHasher = null); Assert.Throws("value", () => manager.Options = null); - await - Assert.ThrowsAsync("user", - async () => await manager.CreateIdentityAsync(null, "whatever")); await Assert.ThrowsAsync("user", async () => await manager.CreateAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.CreateAsync(null, null)); await @@ -580,7 +569,6 @@ namespace Microsoft.AspNet.Identity.Test { var manager = MockHelpers.TestUserManager(new NoopUserStore()); manager.Dispose(); - Assert.Throws(() => manager.ClaimsIdentityFactory); await Assert.ThrowsAsync(() => manager.AddClaimAsync(null, null)); await Assert.ThrowsAsync(() => manager.AddLoginAsync(null, null)); await Assert.ThrowsAsync(() => manager.AddPasswordAsync(null, null)); @@ -603,7 +591,6 @@ namespace Microsoft.AspNet.Identity.Test await Assert.ThrowsAsync(() => manager.FindByNameAsync(null)); await Assert.ThrowsAsync(() => manager.CreateAsync(null)); await Assert.ThrowsAsync(() => manager.CreateAsync(null, null)); - await Assert.ThrowsAsync(() => manager.CreateIdentityAsync(null, null)); await Assert.ThrowsAsync(() => manager.UpdateAsync(null)); await Assert.ThrowsAsync(() => manager.DeleteAsync(null)); await Assert.ThrowsAsync(() => manager.UpdateSecurityStampAsync(null)); diff --git a/test/Shared/IdentityConfig.cs b/test/Shared/IdentityConfig.cs index d5efb8ed40..8aa6e0961e 100644 --- a/test/Shared/IdentityConfig.cs +++ b/test/Shared/IdentityConfig.cs @@ -10,8 +10,8 @@ namespace Microsoft.AspNet.Identity.Test { public ApplicationUserManager(IUserStore store, IOptionsAccessor options, IPasswordHasher passwordHasher, IUserValidator userValidator, - IPasswordValidator passwordValidator, IClaimsIdentityFactory claimsIdentityFactory) - : base(store, options, passwordHasher, userValidator, passwordValidator, claimsIdentityFactory) { } + IPasswordValidator passwordValidator) + : base(store, options, passwordHasher, userValidator, passwordValidator) { } } public class ApplicationRoleManager : RoleManager diff --git a/test/Shared/MockHelpers.cs b/test/Shared/MockHelpers.cs index f7f353d70d..81d24bb51e 100644 --- a/test/Shared/MockHelpers.cs +++ b/test/Shared/MockHelpers.cs @@ -33,7 +33,13 @@ namespace Microsoft.AspNet.Identity.Test { var store = new Mock>(); var options = new OptionsAccessor(null); - return new Mock>(store.Object, options, new PasswordHasher(), new UserValidator(), new PasswordValidator(), new ClaimsIdentityFactory()); + return new Mock>(store.Object, options, new PasswordHasher(), new UserValidator(), new PasswordValidator()); + } + + public static Mock> MockRoleManager() where TRole : class + { + var store = new Mock>(); + return new Mock>(store.Object, new RoleValidator()); } public static UserManager TestUserManager() where TUser : class @@ -45,7 +51,7 @@ namespace Microsoft.AspNet.Identity.Test { var options = new OptionsAccessor(null); var validator = new Mock>(); - var userManager = new UserManager(store, options, new PasswordHasher(), validator.Object, new PasswordValidator(), new ClaimsIdentityFactory()); + var userManager = new UserManager(store, options, new PasswordHasher(), validator.Object, new PasswordValidator()); validator.Setup(v => v.ValidateAsync(userManager, It.IsAny(), CancellationToken.None)).Returns(Task.FromResult(IdentityResult.Success)).Verifiable(); return userManager; } diff --git a/test/Shared/UserManagerTestBase.cs b/test/Shared/UserManagerTestBase.cs index e7f7ef267a..80eed4d87f 100644 --- a/test/Shared/UserManagerTestBase.cs +++ b/test/Shared/UserManagerTestBase.cs @@ -7,7 +7,6 @@ using System.Security.Claims; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNet.Testing; -using Microsoft.Framework.OptionsModel; using Xunit; namespace Microsoft.AspNet.Identity.Test @@ -61,6 +60,19 @@ namespace Microsoft.AspNet.Identity.Test Assert.Null(await manager.FindByNameAsync("UpdateAsync")); } + [Fact] + public async Task CanUpdatePasswordUsingHasher() + { + var manager = CreateManager(); + var user = new TUser() { UserName = "UpdatePassword" }; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); + Assert.True(await manager.CheckPasswordAsync(user, "password")); + user.PasswordHash = manager.PasswordHasher.HashPassword("New"); + IdentityResultAssert.IsSuccess(await manager.UpdateAsync(user)); + Assert.False(await manager.CheckPasswordAsync(user, "password")); + Assert.True(await manager.CheckPasswordAsync(user, "New")); + } + [Fact] public async Task CanFindById() { @@ -397,9 +409,8 @@ namespace Microsoft.AspNet.Identity.Test IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c)); } - var identity = await manager.CreateIdentityAsync(user, "test"); - var claimsFactory = (ClaimsIdentityFactory)manager.ClaimsIdentityFactory; - Assert.NotNull(claimsFactory); + var claimsFactory = new ClaimsIdentityFactory(manager, role); + var identity = await claimsFactory.CreateAsync(user, "test"); var claims = identity.Claims.ToList(); Assert.NotNull(claims); Assert.True( @@ -805,6 +816,30 @@ namespace Microsoft.AspNet.Identity.Test Assert.False(await manager.RoleExistsAsync(role.Name)); } + [Fact] + public async Task CanAddRemoveRoleClaim() + { + var manager = CreateRoleManager(); + var role = CreateRole("ClaimsAddRemove"); + IdentityResultAssert.IsSuccess(await manager.CreateAsync(role)); + Claim[] claims = { new Claim("c", "v"), new Claim("c2", "v2"), new Claim("c2", "v3") }; + foreach (Claim c in claims) + { + IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(role, c)); + } + var roleClaims = await manager.GetClaimsAsync(role); + Assert.Equal(3, roleClaims.Count); + IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(role, claims[0])); + roleClaims = await manager.GetClaimsAsync(role); + Assert.Equal(2, roleClaims.Count); + IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(role, claims[1])); + roleClaims = await manager.GetClaimsAsync(role); + Assert.Equal(1, roleClaims.Count); + IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(role, claims[2])); + roleClaims = await manager.GetClaimsAsync(role); + Assert.Equal(0, roleClaims.Count); + } + [Fact] public async Task CanRoleFindByIdTest() { @@ -968,7 +1003,6 @@ namespace Microsoft.AspNet.Identity.Test } } - [Fact] public async Task RemoveUserFromRoleWithMultipleRoles() {