Merge branch 'release' of github.com:aspnet/Identity into release

This commit is contained in:
Wei Wang 2015-01-28 18:17:25 -08:00
commit 8080826e4a
83 changed files with 3111 additions and 1945 deletions

View File

@ -19,10 +19,10 @@ IF EXIST packages\KoreBuild goto run
.nuget\NuGet.exe install KoreBuild -ExcludeVersion -o packages -nocache -pre
.nuget\NuGet.exe install Sake -version 0.2 -o packages -ExcludeVersion
IF "%SKIP_KRE_INSTALL%"=="1" goto run
CALL packages\KoreBuild\build\kvm upgrade -runtime CLR -x86
CALL packages\KoreBuild\build\kvm install default -runtime CoreCLR -x86
IF "%SKIP_DOTNET_INSTALL%"=="1" goto run
CALL packages\KoreBuild\build\dotnetsdk upgrade -runtime CLR -x86
CALL packages\KoreBuild\build\dotnetsdk install default -runtime CoreCLR -x86
:run
CALL packages\KoreBuild\build\kvm use default -runtime CLR -x86
CALL packages\KoreBuild\build\dotnetsdk use default -runtime CLR -x86
packages\Sake\tools\Sake.exe -I packages\KoreBuild\build -f makefile.shade %*

View File

@ -28,11 +28,11 @@ if test ! -d packages/KoreBuild; then
fi
if ! type k > /dev/null 2>&1; then
source packages/KoreBuild/build/kvm.sh
source packages/KoreBuild/build/dotnetsdk.sh
fi
if ! type k > /dev/null 2>&1; then
kvm upgrade
dotnetsdk upgrade
fi
mono packages/Sake/tools/Sake.exe -I packages/KoreBuild/build -f makefile.shade "$@"

View File

@ -1,11 +1,10 @@
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Rendering;
using System.Linq;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Rendering;
namespace IdentitySample.Models
{
@ -43,20 +42,23 @@ namespace IdentitySample.Models
ViewBag.ReturnUrl = returnUrl;
if (ModelState.IsValid)
{
var signInStatus = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);
switch (signInStatus)
var result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);
if (result.Succeeded)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
ModelState.AddModelError("", "User is locked out, try again later.");
return View(model);
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid username or password.");
return View(model);
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
}
if (result.IsLockedOut)
{
return View("Lockout");
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
return View(model);
}
}
@ -143,22 +145,26 @@ namespace IdentitySample.Models
// Sign in the user with this external login provider if the user already has a login
var result = await SignInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey,
isPersistent: false);
switch (result)
if (result.Succeeded)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl });
case SignInStatus.Failure:
default:
// If the user does not have an account, then prompt the user to create an account
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = info.LoginProvider;
// REVIEW: handle case where email not in claims?
var email = info.ExternalIdentity.FindFirstValue(ClaimTypes.Email);
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email });
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl });
}
if (result.IsLockedOut)
{
return View("Lockout");
}
else
{
// If the user does not have an account, then prompt the user to create an account
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = info.LoginProvider;
// REVIEW: handle case where email not in claims?
var email = info.ExternalIdentity.FindFirstValue(ClaimTypes.Email);
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email });
}
}
@ -380,15 +386,18 @@ namespace IdentitySample.Models
}
var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, model.RememberMe, model.RememberBrowser);
switch (result)
if (result.Succeeded)
{
case SignInStatus.Success:
return RedirectToLocal(model.ReturnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
default:
ModelState.AddModelError("", "Invalid code.");
return View(model);
return RedirectToLocal(model.ReturnUrl);
}
if (result.IsLockedOut)
{
return View("Lockout");
}
else
{
ModelState.AddModelError("", "Invalid code.");
return View(model);
}
}
@ -398,7 +407,7 @@ namespace IdentitySample.Models
{
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error);
ModelState.AddModelError("", error.Description);
}
}

View File

@ -346,7 +346,7 @@ namespace IdentitySample
{
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error);
ModelState.AddModelError("", error.Description);
}
}

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
@ -12,6 +12,7 @@
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<DevelopmentServerPort>5131</DevelopmentServerPort>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
</Project>

View File

@ -3,7 +3,7 @@
"DefaultAdminPassword": "YouShouldChangeThisPassword1!",
"Data": {
"IdentityConnection": {
"Connectionstring": "Server=(localdb)\\mssqllocaldb;Database=IdentityMvc-11-24-14;Trusted_Connection=True;MultipleActiveResultSets=true"
"Connectionstring": "Server=(localdb)\\mssqllocaldb;Database=IdentityMvc-1-7-15;Trusted_Connection=True;MultipleActiveResultSets=true"
}
},
"Identity": {

View File

@ -72,7 +72,7 @@
<a href="http://www.windowsazure.com/en-us/documentation/articles/web-sites-dotnet-deploy-aspnet-mvc-app-membership-oauth-sql-database/">Add Social Logins</a>
</li>
<li>
<a href="http://blogs.msdn.com/b/webdev/archive/2013/10/16/get-more-information-from-social-providers-used-in-the-vs-2013-project-templates.aspx">Get more data about the user when they login suing Facebook</a>
<a href="http://blogs.msdn.com/b/webdev/archive/2013/10/16/get-more-information-from-social-providers-used-in-the-vs-2013-project-templates.aspx">Get more data about the user when they log in using Facebook</a>
</li>
</ul>
</dd>
@ -102,7 +102,7 @@
<dt>Account Confirmation</dt>
<dd>
When you register a new account, you will be sent an email confirmation.
You can use an email service such as <a href="http://www.windowsazure.com/en-us/documentation/articles/sendgrid-dotnet-how-to-send-email/">SendGrid</a> which integrate nicely with Windows Azure and requires no configuration or
You can use an email service such as <a href="http://www.windowsazure.com/en-us/documentation/articles/sendgrid-dotnet-how-to-send-email/">SendGrid</a> which integrates nicely with Windows Azure and requires no configuration or
set up an SMTP server to send email.
You can send email using the EmailService which is registered in App_Start\IdentityConfig.cs
</dd>

View File

@ -9,7 +9,7 @@
<appSettings>
<!-- This will turn on detailed errors when deployed to remote servers -->
<!-- This setting is not recommended for production -->
<add key="K_DETAILED_ERRORS" value="true" />
<add key="ASPNET_DETAILED_ERRORS" value="true" />
</appSettings>
</configuration>

View File

@ -30,45 +30,30 @@ namespace Microsoft.AspNet.Identity.EntityFramework
{
b.Key(u => u.Id);
b.ForRelational().Table("AspNetUsers");
b.Property(u => u.ConcurrencyStamp).ConcurrencyToken();
});
builder.Entity<TRole>(b =>
{
b.Key(r => r.Id);
b.ForRelational().Table("AspNetRoles");
b.Property(r => r.ConcurrencyStamp).ConcurrencyToken();
});
builder.Entity<IdentityUserClaim<TKey>>(b =>
{
b.Key(uc => uc.Id);
b.ManyToOne<TUser>().ForeignKey(uc => uc.UserId);
b.HasOne<TUser>().WithMany().ForeignKey(uc => uc.UserId);
b.ForRelational().Table("AspNetUserClaims");
});
builder.Entity<IdentityRoleClaim<TKey>>(b =>
{
b.Key(rc => rc.Id);
b.ManyToOne<TRole>().ForeignKey(rc => rc.RoleId);
b.HasOne<TRole>().WithMany().ForeignKey(rc => rc.RoleId);
b.ForRelational().Table("AspNetRoleClaims");
});
var userType = builder.Model.GetEntityType(typeof(TUser));
var roleType = builder.Model.GetEntityType(typeof(TRole));
var userClaimType = builder.Model.GetEntityType(typeof(IdentityUserClaim<TKey>));
var roleClaimType = builder.Model.GetEntityType(typeof(IdentityRoleClaim<TKey>));
var userRoleType = builder.Model.GetEntityType(typeof(IdentityUserRole<TKey>));
//var ucfk = userClaimType.GetOrAddForeignKey(userType.GetPrimaryKey(), new[] { userClaimType.GetProperty("UserId") });
//userType.AddNavigation(new Navigation(ucfk, "Claims", false));
//userClaimType.AddNavigation(new Navigation(ucfk, "User", true));
//var urfk = userRoleType.GetOrAddForeignKey(userType.GetPrimaryKey(), new[] { userRoleType.GetProperty("UserId") });
//userType.AddNavigation(new Navigation(urfk, "Roles", false));
//var urfk2 = userRoleType.GetOrAddForeignKey(roleType.GetPrimaryKey(), new[] { userRoleType.GetProperty("RoleId") });
//roleType.AddNavigation(new Navigation(urfk2, "Users", false));
var rcfk = roleClaimType.GetOrAddForeignKey(new[] { roleClaimType.GetProperty("RoleId") }, roleType.GetPrimaryKey());
roleType.AddNavigation("Claims", rcfk, false);
builder.Entity<IdentityUserRole<TKey>>(b =>
{
b.Key(r => new { r.UserId, r.RoleId });
@ -81,7 +66,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework
builder.Entity<IdentityUserLogin<TKey>>(b =>
{
b.Key(l => new { l.LoginProvider, l.ProviderKey });
b.ManyToOne<TUser>().ForeignKey(uc => uc.UserId);
b.HasOne<TUser>().WithMany().ForeignKey(uc => uc.UserId);
b.ForRelational().Table("AspNetUserLogins");
});
}

View File

@ -4,41 +4,42 @@
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;
using Microsoft.Data.Entity.Update;
namespace Microsoft.AspNet.Identity.EntityFramework
{
public class RoleStore<TRole> : RoleStore<TRole, DbContext, string>
where TRole : IdentityRole
{
public RoleStore(DbContext context) : base(context) { }
public RoleStore(DbContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
}
public class RoleStore<TRole, TContext> : RoleStore<TRole, TContext, string>
where TRole : IdentityRole
where TContext : DbContext
{
public RoleStore(TContext context) : base(context) { }
public RoleStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
}
public class RoleStore<TRole, TContext, TKey> :
IQueryableRoleStore<TRole>,
public class RoleStore<TRole, TContext, TKey> :
IQueryableRoleStore<TRole>,
IRoleClaimStore<TRole>
where TRole : IdentityRole<TKey>
where TKey : IEquatable<TKey>
where TContext : DbContext
{
public RoleStore(TContext context)
public RoleStore(TContext context, IdentityErrorDescriber describer = null)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
Context = context;
ErrorDescriber = describer ?? new IdentityErrorDescriber();
}
private bool _disposed;
@ -46,6 +47,11 @@ namespace Microsoft.AspNet.Identity.EntityFramework
public TContext Context { get; private set; }
/// <summary>
/// Used to generate public API error messages
/// </summary>
public IdentityErrorDescriber ErrorDescriber { get; set; }
/// <summary>
/// If true will call SaveChanges after CreateAsync/UpdateAsync/DeleteAsync
/// </summary>
@ -59,12 +65,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework
}
}
public virtual Task<TRole> GetRoleAggregate(Expression<Func<TRole, bool>> filter, CancellationToken cancellationToken = default(CancellationToken))
{
return Roles.FirstOrDefaultAsync(filter);
}
public async virtual Task CreateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
public async virtual Task<IdentityResult> CreateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
@ -74,9 +75,10 @@ namespace Microsoft.AspNet.Identity.EntityFramework
}
await Context.AddAsync(role, cancellationToken);
await SaveChanges(cancellationToken);
return IdentityResult.Success;
}
public async virtual Task UpdateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
public async virtual Task<IdentityResult> UpdateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
@ -84,11 +86,21 @@ namespace Microsoft.AspNet.Identity.EntityFramework
{
throw new ArgumentNullException("role");
}
Context.Attach(role);
role.ConcurrencyStamp = Guid.NewGuid().ToString();
Context.Update(role);
await SaveChanges(cancellationToken);
try
{
await SaveChanges(cancellationToken);
}
catch (DbUpdateConcurrencyException)
{
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
}
return IdentityResult.Success;
}
public async virtual Task DeleteAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
public async virtual Task<IdentityResult> DeleteAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
@ -97,7 +109,15 @@ namespace Microsoft.AspNet.Identity.EntityFramework
throw new ArgumentNullException("role");
}
Context.Remove(role);
await SaveChanges(cancellationToken);
try
{
await SaveChanges(cancellationToken);
}
catch (DbUpdateConcurrencyException)
{
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
}
return IdentityResult.Success;
}
public Task<string> GetRoleIdAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
@ -163,20 +183,43 @@ namespace Microsoft.AspNet.Identity.EntityFramework
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
var roleId = ConvertIdFromString(id);
return GetRoleAggregate(u => u.Id.Equals(roleId), cancellationToken);
return Roles.FirstOrDefaultAsync(u => u.Id.Equals(roleId), cancellationToken);
}
/// <summary>
/// Find a role by name
/// Find a role by normalized name
/// </summary>
/// <param name="name"></param>
/// <param name="normalizedName"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<TRole> FindByNameAsync(string name, CancellationToken cancellationToken = default(CancellationToken))
public virtual Task<TRole> FindByNameAsync(string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
return GetRoleAggregate(u => u.Name.ToUpper() == name.ToUpper(), cancellationToken);
return Roles.FirstOrDefaultAsync(r => r.NormalizedName == normalizedName, cancellationToken);
}
public virtual Task<string> GetNormalizedRoleNameAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (role == null)
{
throw new ArgumentNullException(nameof(role));
}
return Task.FromResult(role.NormalizedName);
}
public virtual Task SetNormalizedRoleNameAsync(TRole role, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (role == null)
{
throw new ArgumentNullException(nameof(role));
}
role.NormalizedName = normalizedName;
return Task.FromResult(0);
}
private void ThrowIfDisposed()
@ -203,7 +246,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework
throw new ArgumentNullException("role");
}
return await RoleClaims.Where(rc => rc.RoleId.Equals(role.Id)).Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToListAsync();
return await RoleClaims.Where(rc => rc.RoleId.Equals(role.Id)).Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToListAsync(cancellationToken);
}
public Task AddClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken))
@ -218,7 +261,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework
throw new ArgumentNullException("claim");
}
return RoleClaims.AddAsync(new IdentityRoleClaim<TKey> { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value });
return RoleClaims.AddAsync(new IdentityRoleClaim<TKey> { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value }, cancellationToken);
}
public async Task RemoveClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken))
@ -232,7 +275,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework
{
throw new ArgumentNullException("claim");
}
var claims = await RoleClaims.Where(uc => uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToListAsync();
var claims = await RoleClaims.Where(uc => uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToListAsync(cancellationToken);
foreach (var c in claims)
{
RoleClaims.Remove(c);

View File

@ -5,23 +5,23 @@ 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;
using Microsoft.Data.Entity.Update;
namespace Microsoft.AspNet.Identity.EntityFramework
{
public class UserStore : UserStore<IdentityUser>
{
public UserStore(DbContext context) : base(context) { }
public UserStore(DbContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
}
public class UserStore<TUser> : UserStore<TUser, IdentityRole, DbContext>
where TUser : IdentityUser, new()
{
public UserStore(DbContext context) : base(context) { }
public UserStore(DbContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
}
public class UserStore<TUser, TRole, TContext> : UserStore<TUser, TRole, TContext, string>
@ -29,7 +29,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework
where TRole : IdentityRole, new()
where TContext : DbContext
{
public UserStore(TContext context) : base(context) { }
public UserStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
}
public class UserStore<TUser, TRole, TContext, TKey> :
@ -49,19 +49,25 @@ namespace Microsoft.AspNet.Identity.EntityFramework
where TKey : IEquatable<TKey>
{
public UserStore(TContext context)
public UserStore(TContext context, IdentityErrorDescriber describer = null)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
Context = context;
ErrorDescriber = describer ?? new IdentityErrorDescriber();
}
private bool _disposed;
public TContext Context { get; private set; }
/// <summary>
/// Used to generate public API error messages
/// </summary>
public IdentityErrorDescriber ErrorDescriber { get; set; }
/// <summary>
/// If true will call SaveChanges after CreateAsync/UpdateAsync/DeleteAsync
/// </summary>
@ -72,15 +78,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework
return AutoSaveChanges ? Context.SaveChangesAsync(cancellationToken) : Task.FromResult(0);
}
protected virtual Task<TUser> GetUserAggregate(Expression<Func<TUser, bool>> filter, CancellationToken cancellationToken = default(CancellationToken))
{
return Users.FirstOrDefaultAsync(filter, cancellationToken);
// TODO: .Include(u => u.Roles)
//.Include(u => u.Claims)
//.Include(u => u.Logins);
}
public Task<string> GetUserIdAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
public virtual Task<string> GetUserIdAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
@ -91,7 +89,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework
return Task.FromResult(ConvertIdToString(user.Id));
}
public Task<string> GetUserNameAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
public virtual Task<string> GetUserNameAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
@ -102,7 +100,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework
return Task.FromResult(user.UserName);
}
public Task SetUserNameAsync(TUser user, string userName, CancellationToken cancellationToken = default(CancellationToken))
public virtual Task SetUserNameAsync(TUser user, string userName, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
@ -114,7 +112,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework
return Task.FromResult(0);
}
public Task<string> GetNormalizedUserNameAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
public virtual Task<string> GetNormalizedUserNameAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
@ -125,7 +123,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework
return Task.FromResult(user.NormalizedUserName);
}
public Task SetNormalizedUserNameAsync(TUser user, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
public virtual Task SetNormalizedUserNameAsync(TUser user, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
@ -137,7 +135,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework
return Task.FromResult(0);
}
public async virtual Task CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
public async virtual Task<IdentityResult> CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
@ -147,9 +145,10 @@ namespace Microsoft.AspNet.Identity.EntityFramework
}
await Context.AddAsync(user, cancellationToken);
await SaveChanges(cancellationToken);
return IdentityResult.Success;
}
public async virtual Task UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
public async virtual Task<IdentityResult> UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
@ -157,11 +156,22 @@ namespace Microsoft.AspNet.Identity.EntityFramework
{
throw new ArgumentNullException("user");
}
Context.Attach(user);
user.ConcurrencyStamp = Guid.NewGuid().ToString();
Context.Update(user);
await SaveChanges(cancellationToken);
try
{
await SaveChanges(cancellationToken);
}
catch (DbUpdateConcurrencyException)
{
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
}
return IdentityResult.Success;
}
public async virtual Task DeleteAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
public async virtual Task<IdentityResult> DeleteAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
@ -169,8 +179,17 @@ namespace Microsoft.AspNet.Identity.EntityFramework
{
throw new ArgumentNullException("user");
}
Context.Remove(user);
await SaveChanges(cancellationToken);
try
{
await SaveChanges(cancellationToken);
}
catch (DbUpdateConcurrencyException)
{
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
}
return IdentityResult.Success;
}
/// <summary>
@ -184,7 +203,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
var id = ConvertIdFromString(userId);
return GetUserAggregate(u => u.Id.Equals(id), cancellationToken);
return Users.FirstOrDefaultAsync(u => u.Id.Equals(id), cancellationToken);
}
public virtual TKey ConvertIdFromString(string id)
@ -215,7 +234,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
return GetUserAggregate(u => u.NormalizedUserName == normalizedUserName, cancellationToken);
return Users.FirstOrDefaultAsync(u => u.NormalizedUserName == normalizedUserName, cancellationToken);
}
public IQueryable<TUser> Users
@ -290,16 +309,13 @@ namespace Microsoft.AspNet.Identity.EntityFramework
{
throw new ArgumentException(Resources.ValueCannotBeNullOrEmpty, "roleName");
}
var roleEntity = await Roles.SingleOrDefaultAsync(r => r.Name.ToUpper() == roleName.ToUpper());
var roleEntity = await Roles.SingleOrDefaultAsync(r => r.Name.ToUpper() == roleName.ToUpper(), cancellationToken);
if (roleEntity == null)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.RoleNotFound, roleName));
}
var ur = new IdentityUserRole<TKey> { UserId = user.Id, RoleId = roleEntity.Id };
// TODO: rely on fixup?
await UserRoles.AddAsync(ur);
user.Roles.Add(ur);
roleEntity.Users.Add(ur);
}
/// <summary>
@ -321,14 +337,13 @@ namespace Microsoft.AspNet.Identity.EntityFramework
{
throw new ArgumentException(Resources.ValueCannotBeNullOrEmpty, "roleName");
}
var roleEntity = await Roles.SingleOrDefaultAsync(r => r.Name.ToUpper() == roleName.ToUpper());
var roleEntity = await Roles.SingleOrDefaultAsync(r => r.Name.ToUpper() == roleName.ToUpper(), cancellationToken);
if (roleEntity != null)
{
var userRole = await UserRoles.FirstOrDefaultAsync(r => roleEntity.Id.Equals(r.RoleId) && r.UserId.Equals(user.Id));
var userRole = await UserRoles.FirstOrDefaultAsync(r => roleEntity.Id.Equals(r.RoleId) && r.UserId.Equals(user.Id), cancellationToken);
if (userRole != null)
{
UserRoles.Remove(userRole);
user.Roles.Remove(userRole);
}
}
}
@ -339,7 +354,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<IList<string>> GetRolesAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
public virtual async Task<IList<string>> GetRolesAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
@ -348,12 +363,11 @@ namespace Microsoft.AspNet.Identity.EntityFramework
throw new ArgumentNullException("user");
}
var userId = user.Id;
// TODO: var query = from userRole in UserRoles
var query = from userRole in user.Roles
var query = from userRole in UserRoles
join role in Roles on userRole.RoleId equals role.Id
where userRole.UserId.Equals(userId)
select role.Name;
//return await query.ToListAsync();
return Task.FromResult<IList<string>>(query.ToList());
return await query.ToListAsync();
}
/// <summary>
@ -371,18 +385,16 @@ namespace Microsoft.AspNet.Identity.EntityFramework
{
throw new ArgumentNullException("user");
}
if (String.IsNullOrWhiteSpace(roleName))
if (string.IsNullOrWhiteSpace(roleName))
{
throw new ArgumentException(Resources.ValueCannotBeNullOrEmpty, "roleName");
}
var role = await Roles.SingleOrDefaultAsync(r => r.Name.ToUpper() == roleName.ToUpper());
var role = await Roles.SingleOrDefaultAsync(r => r.Name.ToUpper() == roleName.ToUpper(), cancellationToken);
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 await UserRoles.AnyAsync(ur => ur.RoleId.Equals(roleId) && ur.UserId.Equals(userId));
}
return false;
}
@ -408,7 +420,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework
private DbSet<IdentityUserRole<TKey>> UserRoles { get { return Context.Set<IdentityUserRole<TKey>>(); } }
private DbSet<IdentityUserLogin<TKey>> UserLogins { get { return Context.Set<IdentityUserLogin<TKey>>(); } }
public async Task<IList<Claim>> GetClaimsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
public async virtual Task<IList<Claim>> GetClaimsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
@ -416,10 +428,10 @@ namespace Microsoft.AspNet.Identity.EntityFramework
throw new ArgumentNullException("user");
}
return await UserClaims.Where(uc => uc.UserId.Equals(user.Id)).Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToListAsync();
return await UserClaims.Where(uc => uc.UserId.Equals(user.Id)).Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToListAsync(cancellationToken);
}
public async Task AddClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken))
public async virtual Task AddClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
@ -432,11 +444,11 @@ namespace Microsoft.AspNet.Identity.EntityFramework
}
foreach (var claim in claims)
{
await UserClaims.AddAsync(new IdentityUserClaim<TKey> { UserId = user.Id, ClaimType = claim.Type, ClaimValue = claim.Value });
await UserClaims.AddAsync(new IdentityUserClaim<TKey> { UserId = user.Id, ClaimType = claim.Type, ClaimValue = claim.Value }, cancellationToken);
}
}
public async Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken))
public async virtual Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
@ -452,15 +464,15 @@ namespace Microsoft.AspNet.Identity.EntityFramework
throw new ArgumentNullException("newClaim");
}
var matchedClaims = await UserClaims.Where(uc => uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToListAsync();
foreach(var matchedClaim in matchedClaims)
var matchedClaims = await UserClaims.Where(uc => uc.UserId.Equals(user.Id) && uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToListAsync(cancellationToken);
foreach (var matchedClaim in matchedClaims)
{
matchedClaim.ClaimValue = newClaim.Value;
matchedClaim.ClaimType = newClaim.Type;
}
}
public async Task RemoveClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken))
public async virtual Task RemoveClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
@ -471,8 +483,9 @@ namespace Microsoft.AspNet.Identity.EntityFramework
{
throw new ArgumentNullException("claims");
}
foreach (var claim in claims) {
var matchedClaims = await UserClaims.Where(uc => uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToListAsync();
foreach (var claim in claims)
{
var matchedClaims = await UserClaims.Where(uc => uc.UserId.Equals(user.Id) && uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToListAsync(cancellationToken);
foreach (var c in matchedClaims)
{
UserClaims.Remove(c);
@ -502,7 +515,6 @@ namespace Microsoft.AspNet.Identity.EntityFramework
};
// TODO: fixup so we don't have to update both
await UserLogins.AddAsync(l);
user.Logins.Add(l);
}
public virtual async Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey,
@ -515,12 +527,10 @@ namespace Microsoft.AspNet.Identity.EntityFramework
throw new ArgumentNullException("user");
}
var userId = user.Id;
// todo: ensure logins loaded
var entry = await UserLogins.SingleOrDefaultAsync(l => l.UserId.Equals(userId) && l.LoginProvider == loginProvider && l.ProviderKey == providerKey);
var entry = await UserLogins.SingleOrDefaultAsync(l => l.UserId.Equals(userId) && l.LoginProvider == loginProvider && l.ProviderKey == providerKey, cancellationToken);
if (entry != null)
{
UserLogins.Remove(entry);
user.Logins.Remove(entry);
}
}
@ -532,13 +542,9 @@ namespace Microsoft.AspNet.Identity.EntityFramework
{
throw new ArgumentNullException("user");
}
// todo: ensure logins loaded
//IList<UserLoginInfo> result = user.Logins
// .Select(l => new UserLoginInfo(l.LoginProvider, l.ProviderKey, l.ProviderDisplayName)).ToList();
var userId = user.Id;
return await UserLogins.Where(l => l.UserId.Equals(userId))
.Select(l => new UserLoginInfo(l.LoginProvider, l.ProviderKey, l.ProviderDisplayName)).ToListAsync();
.Select(l => new UserLoginInfo(l.LoginProvider, l.ProviderKey, l.ProviderDisplayName)).ToListAsync(cancellationToken);
}
public async virtual Task<TUser> FindByLoginAsync(string loginProvider, string providerKey,
@ -546,12 +552,11 @@ namespace Microsoft.AspNet.Identity.EntityFramework
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
// todo: ensure logins loaded
var userLogin = await
UserLogins.FirstOrDefaultAsync(l => l.LoginProvider == loginProvider && l.ProviderKey == providerKey);
UserLogins.FirstOrDefaultAsync(l => l.LoginProvider == loginProvider && l.ProviderKey == providerKey, cancellationToken);
if (userLogin != null)
{
return await GetUserAggregate(u => u.Id.Equals(userLogin.UserId), cancellationToken);
return await Users.FirstOrDefaultAsync(u => u.Id.Equals(userLogin.UserId), cancellationToken);
}
return null;
}
@ -628,19 +633,40 @@ namespace Microsoft.AspNet.Identity.EntityFramework
return Task.FromResult(user.Email);
}
public virtual Task<string> GetNormalizedEmailAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
return Task.FromResult(user.NormalizedEmail);
}
public virtual Task SetNormalizedEmailAsync(TUser user, string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
user.NormalizedEmail = normalizedEmail;
return Task.FromResult(0);
}
/// <summary>
/// Find an user by email
/// </summary>
/// <param name="email"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<TUser> FindByEmailAsync(string email, CancellationToken cancellationToken = default(CancellationToken))
public virtual Task<TUser> FindByEmailAsync(string normalizedEmail, 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);
return Users.FirstOrDefaultAsync(u => u.NormalizedEmail == normalizedEmail, cancellationToken);
}
/// <summary>
@ -913,5 +939,58 @@ namespace Microsoft.AspNet.Identity.EntityFramework
}
return Task.FromResult(user.TwoFactorEnabled);
}
/// <summary>
/// Get all users with given claim
/// </summary>
/// <param name="claim"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async virtual Task<IList<TUser>> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (claim == null)
{
throw new ArgumentNullException("claim");
}
var query = from userclaims in UserClaims
join user in Users on userclaims.UserId equals user.Id
where userclaims.ClaimValue == claim.Value
&& userclaims.ClaimType == claim.Type
select user;
return await query.ToListAsync(cancellationToken);
}
/// <summary>
/// Get all users in given role
/// </summary>
/// <param name="roleName"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public async virtual Task<IList<TUser>> GetUsersInRoleAsync(string roleName, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (String.IsNullOrEmpty(roleName))
{
throw new ArgumentNullException("role");
}
var role = await Roles.Where(x => x.Name.Equals(roleName)).FirstOrDefaultAsync(cancellationToken);
if (role != null)
{
var query = from userrole in UserRoles
join user in Users on userrole.UserId equals user.Id
where userrole.RoleId.Equals(role.Id)
select user;
return await query.ToListAsync(cancellationToken);
}
return new List<TUser>();
}
}
}

View File

@ -2,15 +2,21 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNet.Identity;
namespace Microsoft.AspNet.Builder
{
/// <summary>
/// Startup extensions
/// Identity extensions for <see cref="IApplicationBuilder"/>.
/// </summary>
public static class BuilderExtensions
{
/// <summary>
/// Enables the ASP.NET identity for the current application.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder"/> instance this method extends.</param>
/// <returns>The <see cref="IApplicationBuilder"/> instance this method extends.</returns>
public static IApplicationBuilder UseIdentity(this IApplicationBuilder app)
{
if (app == null)

View File

@ -6,15 +6,16 @@ using System.Security.Claims;
namespace System.Security.Principal
{
/// <summary>
/// Extensions making it easier to get the user name/user id claims off of an identity
/// Claims related extensions for <see cref="IIdentity"/>.
/// </summary>
public static class ClaimsIdentityExtensions
{
/// <summary>
/// Return the user name using the UserNameClaimType
/// Returns the Name claim value if present otherwise returns null.
/// </summary>
/// <param name="identity"></param>
/// <returns></returns>
/// <param name="identity">The <see cref="IIdentity"/> instance this method extends.</param>
/// <returns>The Name claim value, or null if the claim is not present.</returns>
/// <remarks>The name claim is identified by <see cref="ClaimsIdentity.DefaultNameClaimType"/>.</remarks>
public static string GetUserName(this IIdentity identity)
{
if (identity == null)
@ -26,10 +27,11 @@ namespace System.Security.Principal
}
/// <summary>
/// Return the user id using the UserIdClaimType
/// Returns the User ID claim value if present otherwise returns null.
/// </summary>
/// <param name="identity"></param>
/// <returns></returns>
/// <param name="identity">The <see cref="IIdentity"/> instance this method extends.</param>
/// <returns>The User ID claim value, or null if the claim is not present.</returns>
/// <remarks>The name claim is identified by <see cref="ClaimTypes.NameIdentifier"/>.</remarks>
public static string GetUserId(this IIdentity identity)
{
if (identity == null)
@ -41,11 +43,11 @@ namespace System.Security.Principal
}
/// <summary>
/// Return the claim value for the first claim with the specified type if it exists, null otherwise
/// Returns the value for the first claim of the specified type otherwise null the claim is not present.
/// </summary>
/// <param name="identity"></param>
/// <param name="claimType"></param>
/// <returns></returns>
/// <param name="identity">The <see cref="IIdentity"/> instance this method extends.</param>
/// <param name="claimType">The claim type whose first value should be returned.</param>
/// <returns>The value of the first instance of the specifed claim type, or null if the claim is not present.</returns>
public static string FindFirstValue(this ClaimsIdentity identity, string claimType)
{
if (identity == null)

View File

@ -5,19 +5,29 @@ using System;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Framework.OptionsModel;
namespace Microsoft.AspNet.Identity
{
/// <summary>
/// Creates a ClaimsIdentity from a User
/// Provides methods to create a claims identity for a given user.
/// </summary>
/// <typeparam name="TUser"></typeparam>
/// <typeparam name="TUser">The type used to represent a user.</typeparam>
/// <typeparam name="TRole">The type used to represent a role.</typeparam>
public class ClaimsIdentityFactory<TUser, TRole> : IClaimsIdentityFactory<TUser>
where TUser : class
where TRole : class
{
public ClaimsIdentityFactory(UserManager<TUser> userManager, RoleManager<TRole> roleManager,
/// <summary>
/// Initializes a new instance of the <see cref="ClaimsIdentityFactory"/> class.
/// </summary>
/// <param name="userManager">The <see cref="UserManager{TUser}"/> to retrieve user information from.</param>
/// <param name="roleManager">The <see cref="RoleManager{TRole}"/> to retrieve a user's roles from.</param>
/// <param name="optionsAccessor">The configured <see cref="IdentityOptions"/>.</param>
public ClaimsIdentityFactory(
UserManager<TUser> userManager,
RoleManager<TRole> roleManager,
IOptions<IdentityOptions> optionsAccessor)
{
if (userManager == null)
@ -37,19 +47,38 @@ namespace Microsoft.AspNet.Identity
Options = optionsAccessor.Options;
}
/// <summary>
/// Gets the <see cref="UserManager{TUser}"/> for this factory.
/// </summary>
/// <value>
/// The current <see cref="UserManager{TUser}"/> for this factory instance.
/// </value>
public UserManager<TUser> UserManager { get; private set; }
/// <summary>
/// Gets the <see cref="RoleManager{TRole}"/> for this factory.
/// </summary>
/// <value>
/// The current <see cref="RoleManager{TRole}"/> for this factory instance.
/// </value>
public RoleManager<TRole> RoleManager { get; private set; }
/// <summary>
/// Gets the <see cref="IdentityOptions"/> for this factory.
/// </summary>
/// <value>
/// The current <see cref="IdentityOptions"/> for this factory instance.
/// </value>
public IdentityOptions Options { get; private set; }
/// <summary>
/// CreateAsync a ClaimsIdentity from a user
/// Creates a populated <see cref="ClaimsIdentity"/> for the specified <paramref name="user"/>.
/// </summary>
/// <param name="manager"></param>
/// <param name="user"></param>
/// <param name="authenticationType"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<ClaimsIdentity> CreateAsync(TUser user,
/// <param name="user">The user instance to create claims on.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the tasks to complete.</param>
/// <returns>A <see cref="Task{TResult}"/> that represents the started task.</returns>
public virtual async Task<ClaimsIdentity> CreateAsync(
TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
if (user == null)

View File

@ -5,28 +5,41 @@ using System.Security.Claims;
namespace Microsoft.AspNet.Identity
{
/// <summary>
/// Options for ClaimType names.
/// </summary>
public class ClaimsIdentityOptions
{
public static readonly string DefaultSecurityStampClaimType = "AspNet.Identity.SecurityStamp";
/// <summary>
/// Claim type used for role claims
/// Gets the ClaimType used for a Role claim.
/// </summary>
/// <remarks>
/// This defaults to <see cref="ClaimTypes.Role"/>.
/// </remarks>
public string RoleClaimType { get; set; } = ClaimTypes.Role;
/// <summary>
/// Claim type used for the user name
/// Gets the ClaimType used for the user name claim.
/// </summary>
/// <remarks>
/// This defaults to <see cref="ClaimTypes.Name"/>.
/// </remarks>
public string UserNameClaimType { get; set; } = ClaimTypes.Name;
/// <summary>
/// Claim type used for the user id
/// Gets the ClaimType used for the user identifier claim.
/// </summary>
/// <remarks>
/// This defaults to <see cref="ClaimTypes.NameIdentifier"/>.
/// </remarks>
public string UserIdClaimType { get; set; } = ClaimTypes.NameIdentifier;
/// <summary>
/// Claim type used for the user security stamp
/// Gets the ClaimType used for the security stamp claim..
/// </summary>
public string SecurityStampClaimType { get; set; } = DefaultSecurityStampClaimType;
/// <remarks>
/// This defaults to "AspNet.Identity.SecurityStamp".
/// </remarks>
public string SecurityStampClaimType { get; set; } = "AspNet.Identity.SecurityStamp";
}
}

View File

@ -1,51 +1,71 @@
using Microsoft.AspNet.Security.DataProtection;
using Microsoft.Framework.OptionsModel;
// 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.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Security.DataProtection;
using Microsoft.Framework.OptionsModel;
namespace Microsoft.AspNet.Identity
{
public class DataProtectionTokenProviderOptions
{
public string Name { get; set; } = "DataProtection";
public TimeSpan TokenLifespan { get; set; } = TimeSpan.FromDays(1);
}
/// <summary>
/// Token provider that uses an IDataProtector to generate encrypted tokens based off of the security stamp
/// Provides protection and validation of identity tokens.
/// </summary>
/// <typeparam name="TUser">The type used to represent a user.</typeparam>
public class DataProtectorTokenProvider<TUser> : IUserTokenProvider<TUser> where TUser : class
{
/// <summary>
/// Initializes a new instance of the <see cref="DataProtectorTokenProvider{TUser}"/> class.
/// </summary>
/// <param name="dataProtectionProvider">The system data protection provider.</param>
/// <param name="options">The configured <see cref="DataProtectionTokenProviderOptions"/>.</param>
public DataProtectorTokenProvider(IDataProtectionProvider dataProtectionProvider, IOptions<DataProtectionTokenProviderOptions> options)
{
if (options == null || options.Options == null)
{
throw new ArgumentNullException(nameof(options));
}
if (dataProtectionProvider == null)
{
throw new ArgumentNullException(nameof(dataProtectionProvider));
}
Options = options.Options;
Options = options?.Options ?? new DataProtectionTokenProviderOptions();
// Use the Name as the purpose which should usually be distinct from others
Protector = dataProtectionProvider.CreateProtector(Name ?? "DataProtectorTokenProvider");
}
public DataProtectionTokenProviderOptions Options { get; private set; }
public IDataProtector Protector { get; private set; }
/// <summary>
/// Gets the <see cref="DataProtectionTokenProviderOptions"/> for this instance.
/// </summary>
/// <value>
/// The <see cref="DataProtectionTokenProviderOptions"/> for this instance.
/// </value>
protected DataProtectionTokenProviderOptions Options { get; private set; }
/// <summary>
/// Gets the <see cref="IDataProtector"/> for this instance.
/// </summary>
/// <value>
/// The <see cref="IDataProtector"/> for this instance.
/// </value>
protected IDataProtector Protector { get; private set; }
/// <summary>
/// Gets the name of this instance.
/// </summary>
/// <value>
/// The name of this instance.
/// </value>
public string Name { get { return Options.Name; } }
/// <summary>
/// Generate a protected string for a user
/// Generates a protected token for the specified <paramref name="User"/>.
/// </summary>
/// <param name="purpose"></param>
/// <param name="manager"></param>
/// <param name="user"></param>
/// <returns></returns>
/// <param name="purpose">The purpose the token will be used for.</param>
/// <param name="manager">The <see cref="UserManager{TUser}"/> to retrieve user properties from.</param>
/// <param name="user">The <see cref="TUser"/> the token will be generated from.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the tasks to complete.</param>
/// <returns>A <see cref="Task{TResult}"/> that contains the protected token.</returns>
public async Task<string> GenerateAsync(string purpose, UserManager<TUser> manager, TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
@ -72,13 +92,14 @@ namespace Microsoft.AspNet.Identity
}
/// <summary>
/// Return false if the token is not valid
/// Validates the protected <paramref name="token"/> for the specified <paramref name="user"/> and <paramref name="purpose"/>.
/// </summary>
/// <param name="purpose"></param>
/// <param name="token"></param>
/// <param name="manager"></param>
/// <param name="user"></param>
/// <returns></returns>
/// <param name="purpose">The purpose the token was be used for.</param>
/// <param name="token">The token to validate.</param>
/// <param name="manager">The <see cref="UserManager{TUser}"/> to retrieve user properties from.</param>
/// <param name="user">The <see cref="TUser"/> the token was generated for.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the tasks to complete.</param>
/// <returns>A <see cref="Task{TResult}"/> that is true if the token is valid, otherwise false.</returns>
public async Task<bool> ValidateAsync(string purpose, string token, UserManager<TUser> manager, TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
@ -128,11 +149,13 @@ namespace Microsoft.AspNet.Identity
}
/// <summary>
/// Returns false because tokens are two long to be used for two factor
/// Returns a <see cref="boolean"/> indicating whether a token generated by this instance
/// can be used as a Two Factor Authentication token.
/// </summary>
/// <param name="manager"></param>
/// <param name="user"></param>
/// <returns></returns>
/// <param name="manager">The <see cref="UserManager{TUser}"/> to retrieve user properties from.</param>
/// <param name="user">The <see cref="TUser"/> the token was generated for.</param>
/// <returns>True if a token generated by this instance can be used as a Two Factor Authentication token, otherwise false.</returns>
/// <remarks>This method will always return false for instances of <see cref="DataProtectorTokenProvider{TUser}"/>.</remarks>
public Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<TUser> manager, TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
@ -140,12 +163,13 @@ namespace Microsoft.AspNet.Identity
}
/// <summary>
/// This provider no-ops by default when asked to notify a user
/// Creates a notification task for A <paramref name="user"/> based on the supplied <paramref name="token"/>.
/// </summary>
/// <param name="token"></param>
/// <param name="manager"></param>
/// <param name="user"></param>
/// <returns></returns>
/// <param name="token">The token to generate notifications for..</param>
/// <param name="manager">The <see cref="UserManager{TUser}"/> to retrieve user properties from.</param>
/// <param name="user">The <see cref="TUser"/> the token was generated for.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the tasks to complete.</param>
/// <returns>A <see cref="Task{TResult}"/> that represents the started task.</returns>
public Task NotifyAsync(string token, UserManager<TUser> manager, TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
@ -153,7 +177,9 @@ namespace Microsoft.AspNet.Identity
}
}
// Based on Levi's authentication sample
/// <summary>
/// Utility extensions to streams
/// </summary>
internal static class StreamExtensions
{
internal static readonly Encoding DefaultEncoding = new UTF8Encoding(false, true);

View File

@ -0,0 +1,29 @@
// 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
{
/// <summary>
/// Contains options for the <see cref="DataProtectorTokenProvider{TUser}"/>.
/// </summary>
public class DataProtectionTokenProviderOptions
{
/// <summary>
/// Gets or sets the name of the <see cref="DataProtectorTokenProvider{TUser}"/>.
/// </summary>
/// <value>
/// The name of the <see cref="DataProtectorTokenProvider{TUser}"/>.
/// </value>
public string Name { get; set; } = "DataProtectorTokenProvider";
/// <summary>
/// Gets or sets the amount of time a generated token remains valid.
/// </summary>
/// <value>
/// The amount of time a generated token remains valid.
/// </value>
public TimeSpan TokenLifespan { get; set; } = TimeSpan.FromDays(1);
}
}

View File

@ -4,15 +4,15 @@
namespace Microsoft.AspNet.Identity
{
/// <summary>
/// Used to normalize a user name
/// Used to normalize keys for consistent lookups
/// </summary>
public interface IUserNameNormalizer
public interface ILookupNormalizer
{
/// <summary>
/// Returns the normalized user name
/// Returns the normalized key
/// </summary>
/// <param name="userName"></param>
/// <param name="key"></param>
/// <returns></returns>
string Normalize(string userName);
string Normalize(string key);
}
}

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNet.Identity
/// <param name="role"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task CreateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken));
Task<IdentityResult> CreateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Update a role
@ -27,7 +27,7 @@ namespace Microsoft.AspNet.Identity
/// <param name="role"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task UpdateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken));
Task<IdentityResult> UpdateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// DeleteAsync a role
@ -35,7 +35,7 @@ namespace Microsoft.AspNet.Identity
/// <param name="role"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task DeleteAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken));
Task<IdentityResult> DeleteAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Returns a role's id
@ -63,6 +63,26 @@ namespace Microsoft.AspNet.Identity
Task SetRoleNameAsync(TRole role, string roleName,
CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Get a role's normalized name
/// </summary>
/// <param name="role"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<string> GetNormalizedRoleNameAsync(TRole role,
CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Set a role's normalized name
/// </summary>
/// <param name="role"></param>
/// <param name="normalizedName"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task SetNormalizedRoleNameAsync(TRole role, string normalizedName,
CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Finds a role by id
/// </summary>
@ -72,11 +92,11 @@ namespace Microsoft.AspNet.Identity
Task<TRole> FindByIdAsync(string roleId, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Find a role by name
/// Find a role by normalized name
/// </summary>
/// <param name="roleName"></param>
/// <param name="normalizedRoleName"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<TRole> FindByNameAsync(string roleName, CancellationToken cancellationToken = default(CancellationToken));
Task<TRole> FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken = default(CancellationToken));
}
}

View File

@ -51,5 +51,13 @@ namespace Microsoft.AspNet.Identity
/// <returns></returns>
Task RemoveClaimsAsync(TUser user, IEnumerable<Claim> claims,
CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Get users having a specific claim
/// </summary>
/// <param name="claim">Claim to look up</param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<IList<TUser>> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken));
}
}

View File

@ -51,9 +51,28 @@ namespace Microsoft.AspNet.Identity
/// <summary>
/// Returns the user associated with this email
/// </summary>
/// <param name="email"></param>
/// <param name="normalizedEmail"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<TUser> FindByEmailAsync(string email, CancellationToken cancellationToken = default(CancellationToken));
Task<TUser> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Returns the normalized email
/// </summary>
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<string> GetNormalizedEmailAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Set the normalized email
/// </summary>
/// <param name="user"></param>
/// <param name="normalizedEmail"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task SetNormalizedEmailAsync(TUser user, string normalizedEmail,
CancellationToken cancellationToken = default(CancellationToken));
}
}

View File

@ -51,5 +51,13 @@ namespace Microsoft.AspNet.Identity
/// <returns></returns>
Task<bool> IsInRoleAsync(TUser user, string roleName,
CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Returns all users in given role
/// </summary>
/// <param name="roleName"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<IList<TUser>> GetUsersInRoleAsync(string roleName, CancellationToken cancellationToken = default(CancellationToken));
}
}

View File

@ -63,7 +63,7 @@ namespace Microsoft.AspNet.Identity
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken));
Task<IdentityResult> CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// UpdateAsync a user
@ -71,7 +71,7 @@ namespace Microsoft.AspNet.Identity
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken));
Task<IdentityResult> UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// DeleteAsync a user
@ -79,7 +79,7 @@ namespace Microsoft.AspNet.Identity
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task DeleteAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken));
Task<IdentityResult> DeleteAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Finds a user

View File

@ -35,6 +35,12 @@ namespace Microsoft.AspNet.Identity
return AddScoped(typeof(IRoleValidator<>).MakeGenericType(RoleType), typeof(T));
}
public IdentityBuilder AddErrorDescriber<TDescriber>() where TDescriber : IdentityErrorDescriber
{
Services.AddScoped<IdentityErrorDescriber, TDescriber>();
return this;
}
public IdentityBuilder AddPasswordValidator<T>() where T : class
{
return AddScoped(typeof(IPasswordValidator<>).MakeGenericType(UserType), typeof(T));
@ -76,5 +82,15 @@ namespace Microsoft.AspNet.Identity
.AddTokenProvider(typeof(PhoneNumberTokenProvider<>).MakeGenericType(UserType))
.AddTokenProvider(typeof(EmailTokenProvider<>).MakeGenericType(UserType));
}
public IdentityBuilder AddUserManager<TUserManager>() where TUserManager : class
{
return AddScoped(typeof(UserManager<>).MakeGenericType(UserType), typeof(TUserManager));
}
public IdentityBuilder AddRoleManager<TRoleManager>() where TRoleManager : class
{
return AddScoped(typeof(RoleManager<>).MakeGenericType(RoleType), typeof(TRoleManager));
}
}
}

View File

@ -3,12 +3,9 @@
namespace Microsoft.AspNet.Identity
{
public enum SignInStatus
public class IdentityError
{
Success,
LockedOut,
RequiresVerification,
NotAllowed,
Failure
public string Code { get; set; }
public string Description { get; set; }
}
}

View File

@ -0,0 +1,189 @@
// 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.
namespace Microsoft.AspNet.Identity
{
public class IdentityErrorDescriber
{
public static IdentityErrorDescriber Default = new IdentityErrorDescriber();
public virtual IdentityError DefaultError()
{
return new IdentityError
{
Code = nameof(DefaultError),
Description = Resources.DefaultError
};
}
public virtual IdentityError ConcurrencyFailure()
{
return new IdentityError
{
Code = nameof(ConcurrencyFailure),
Description = Resources.ConcurrencyFailure
};
}
public virtual IdentityError PasswordMismatch()
{
return new IdentityError
{
Code = nameof(PasswordMismatch),
Description = Resources.PasswordMismatch
};
}
public virtual IdentityError InvalidToken()
{
return new IdentityError
{
Code = nameof(InvalidToken),
Description = Resources.InvalidToken
};
}
public virtual IdentityError LoginAlreadyAssociated()
{
return new IdentityError
{
Code = nameof(LoginAlreadyAssociated),
Description = Resources.LoginAlreadyAssociated
};
}
public virtual IdentityError InvalidUserName(string name)
{
return new IdentityError
{
Code = nameof(InvalidUserName),
Description = Resources.FormatInvalidUserName(name)
};
}
public virtual IdentityError InvalidEmail(string email)
{
return new IdentityError
{
Code = nameof(InvalidEmail),
Description = Resources.FormatInvalidEmail(email)
};
}
public virtual IdentityError DuplicateUserName(string name)
{
return new IdentityError
{
Code = nameof(DuplicateUserName),
Description = Resources.FormatDuplicateUserName(name)
};
}
public virtual IdentityError DuplicateEmail(string email)
{
return new IdentityError
{
Code = nameof(DuplicateEmail),
Description = Resources.FormatDuplicateEmail(email)
};
}
public virtual IdentityError InvalidRoleName(string name)
{
return new IdentityError
{
Code = nameof(InvalidRoleName),
Description = Resources.FormatInvalidRoleName(name)
};
}
public virtual IdentityError DuplicateRoleName(string name)
{
return new IdentityError
{
Code = nameof(DuplicateRoleName),
Description = Resources.FormatDuplicateRoleName(name)
};
}
public virtual IdentityError UserAlreadyHasPassword()
{
return new IdentityError
{
Code = nameof(UserAlreadyHasPassword),
Description = Resources.UserAlreadyHasPassword
};
}
public virtual IdentityError UserLockoutNotEnabled()
{
return new IdentityError
{
Code = nameof(UserLockoutNotEnabled),
Description = Resources.UserLockoutNotEnabled
};
}
public virtual IdentityError UserAlreadyInRole(string role)
{
return new IdentityError
{
Code = nameof(UserAlreadyInRole),
Description = Resources.FormatUserAlreadyInRole(role)
};
}
public virtual IdentityError UserNotInRole(string role)
{
return new IdentityError
{
Code = nameof(UserNotInRole),
Description = Resources.FormatUserNotInRole(role)
};
}
public virtual IdentityError PasswordTooShort(int length)
{
return new IdentityError
{
Code = nameof(PasswordTooShort),
Description = Resources.FormatPasswordTooShort(length)
};
}
public virtual IdentityError PasswordRequiresNonLetterAndDigit()
{
return new IdentityError
{
Code = nameof(PasswordRequiresNonLetterAndDigit),
Description = Resources.PasswordRequiresNonLetterAndDigit
};
}
public virtual IdentityError PasswordRequiresDigit()
{
return new IdentityError
{
Code = nameof(PasswordRequiresDigit),
Description = Resources.PasswordRequiresDigit
};
}
public virtual IdentityError PasswordRequiresLower()
{
return new IdentityError
{
Code = nameof(PasswordRequiresLower),
Description = Resources.PasswordRequiresLower
};
}
public virtual IdentityError PasswordRequiresUpper()
{
return new IdentityError
{
Code = nameof(PasswordRequiresUpper),
Description = Resources.PasswordRequiresUpper
};
}
}
}

View File

@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.Framework.Logging;
namespace Microsoft.AspNet.Identity
{
@ -11,45 +12,19 @@ namespace Microsoft.AspNet.Identity
/// </summary>
public class IdentityResult
{
private static readonly IdentityResult _success = new IdentityResult(true);
private static readonly IdentityResult _success = new IdentityResult { Succeeded = true };
/// <summary>
/// Failure constructor that takes error messages
/// </summary>
/// <param name="errors"></param>
public IdentityResult(params string[] errors) : this((IEnumerable<string>)errors)
{
}
/// <summary>
/// Failure constructor that takes error messages
/// </summary>
/// <param name="errors"></param>
public IdentityResult(IEnumerable<string> errors)
{
if (errors == null || !errors.Any())
{
errors = new[] { Resources.DefaultError };
}
Succeeded = false;
Errors = errors;
}
protected IdentityResult(bool success)
{
Succeeded = success;
Errors = new string[0];
}
private List<IdentityError> _errors = new List<IdentityError>();
/// <summary>
/// True if the operation was successful
/// </summary>
public bool Succeeded { get; private set; }
public bool Succeeded { get; protected set; }
/// <summary>
/// List of errors
/// </summary>
public IEnumerable<string> Errors { get; private set; }
public IEnumerable<IdentityError> Errors { get { return _errors; } }
/// <summary>
/// Static success result
@ -65,9 +40,32 @@ namespace Microsoft.AspNet.Identity
/// </summary>
/// <param name="errors"></param>
/// <returns></returns>
public static IdentityResult Failed(params string[] errors)
public static IdentityResult Failed(params IdentityError[] errors)
{
return new IdentityResult(errors);
var result = new IdentityResult { Succeeded = false };
if (errors != null)
{
result._errors.AddRange(errors);
}
return result;
}
/// <summary>
/// Log Identity result
/// </summary>
/// <param name="logger"></param>
/// <param name="message"></param>
public virtual void Log(ILogger logger, string message)
{
// TODO: Take logging level as a parameter
if (Succeeded)
{
logger.WriteInformation(Resources.FormatLogIdentityResultSuccess(message));
}
else
{
logger.WriteWarning(Resources.FormatLogIdentityResultFailure(message, string.Join(",", Errors.Select(x => x.Code).ToList())));
}
}
}
}

View File

@ -65,5 +65,11 @@ namespace Microsoft.AspNet.Identity
/// Role name
/// </summary>
public virtual string Name { get; set; }
public virtual string NormalizedName { get; set; }
/// <summary>
/// A random value that should change whenever a role is persisted to the store
/// </summary>
public virtual string ConcurrencyStamp { get; set; } = Guid.NewGuid().ToString();
}
}

View File

@ -57,8 +57,10 @@ namespace Microsoft.Framework.DependencyInjection
services.TryAdd(describe.Transient<IUserValidator<TUser>, UserValidator<TUser>>());
services.TryAdd(describe.Transient<IPasswordValidator<TUser>, PasswordValidator<TUser>>());
services.TryAdd(describe.Transient<IPasswordHasher<TUser>, PasswordHasher<TUser>>());
services.TryAdd(describe.Transient<IUserNameNormalizer, UpperInvariantUserNameNormalizer>());
services.TryAdd(describe.Transient<ILookupNormalizer, UpperInvariantLookupNormalizer>());
services.TryAdd(describe.Transient<IRoleValidator<TRole>, RoleValidator<TRole>>());
// No interface for the error describer so we can add errors without rev'ing the interface
services.TryAdd(describe.Transient<IdentityErrorDescriber, IdentityErrorDescriber>());
services.TryAdd(describe.Scoped<ISecurityStampValidator, SecurityStampValidator<TUser>>());
services.TryAdd(describe.Scoped<IClaimsIdentityFactory<TUser>, ClaimsIdentityFactory<TUser, TRole>>());
services.TryAdd(describe.Scoped<UserManager<TUser>, UserManager<TUser>>());

View File

@ -2,7 +2,6 @@
// 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
{
@ -37,6 +36,8 @@ namespace Microsoft.AspNet.Identity
/// </summary>
public virtual string Email { get; set; }
public virtual string NormalizedEmail { get; set; }
/// <summary>
/// True if the email is confirmed, default is false
/// </summary>
@ -52,6 +53,11 @@ namespace Microsoft.AspNet.Identity
/// </summary>
public virtual string SecurityStamp { get; set; }
/// <summary>
/// A random value that should change whenever a user is persisted to the store
/// </summary>
public virtual string ConcurrencyStamp { get; set; } = Guid.NewGuid().ToString();
/// <summary>
/// PhoneNumber for the user
/// </summary>
@ -81,20 +87,5 @@ namespace Microsoft.AspNet.Identity
/// Used to record failures for the purposes of lockout
/// </summary>
public virtual int AccessFailedCount { get; set; }
/// <summary>
/// Roles for the user
/// </summary>
public virtual ICollection<IdentityUserRole<TKey>> Roles { get; } = new List<IdentityUserRole<TKey>>();
/// <summary>
/// Claims for the user
/// </summary>
public virtual ICollection<IdentityUserClaim<TKey>> Claims { get; } = new List<IdentityUserClaim<TKey>>();
/// <summary>
/// Associated logins for the user
/// </summary>
public virtual ICollection<IdentityUserLogin<TKey>> Logins { get; } = new List<IdentityUserLogin<TKey>>();
}
}

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
@ -13,5 +13,13 @@
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<ProduceOutputsOnBuild>False</ProduceOutputsOnBuild>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
<ProjectExtensions>
<VisualStudio>
<UserProperties project_1json__JSONSchema="http://www.asp.net/media/4878834/project.json" />
</VisualStudio>
</ProjectExtensions>
</Project>

View File

@ -37,14 +37,11 @@ namespace Microsoft.AspNet.Identity
/// Constructs a PasswordHasher using the specified options
/// </summary>
/// <param name="options"></param>
public PasswordHasher(IOptions<PasswordHasherOptions> options)
public PasswordHasher(IOptions<PasswordHasherOptions> optionsAccessor = null)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
var options = optionsAccessor?.Options ?? new PasswordHasherOptions();
_compatibilityMode = options.Options.CompatibilityMode;
_compatibilityMode = options.CompatibilityMode;
switch (_compatibilityMode)
{
case PasswordHasherCompatibilityMode.IdentityV2:
@ -52,7 +49,7 @@ namespace Microsoft.AspNet.Identity
break;
case PasswordHasherCompatibilityMode.IdentityV3:
_iterCount = options.Options.IterationCount;
_iterCount = options.IterationCount;
if (_iterCount < 1)
{
throw new InvalidOperationException(Resources.InvalidPasswordHasherIterationCount);
@ -63,7 +60,7 @@ namespace Microsoft.AspNet.Identity
throw new InvalidOperationException(Resources.InvalidPasswordHasherCompatibilityMode);
}
_rng = options.Options.Rng;
_rng = options.Rng;
}
// Compares two byte arrays for equality. The method is specifically written so that the loop is not optimized.

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Security.Cryptography;
using Microsoft.AspNet.Security.DataProtection;
namespace Microsoft.AspNet.Identity
{

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -15,6 +14,13 @@ namespace Microsoft.AspNet.Identity
/// </summary>
public class PasswordValidator<TUser> : IPasswordValidator<TUser> where TUser : class
{
public PasswordValidator(IdentityErrorDescriber errors = null)
{
Describer = errors ?? new IdentityErrorDescriber();
}
public IdentityErrorDescriber Describer { get; private set; }
/// <summary>
/// Ensures that the password is of the required length and meets the configured requirements
/// </summary>
@ -33,33 +39,32 @@ namespace Microsoft.AspNet.Identity
{
throw new ArgumentNullException("manager");
}
var errors = new List<string>();
var errors = new List<IdentityError>();
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(Describer.PasswordTooShort(options.RequiredLength));
}
if (options.RequireNonLetterOrDigit && password.All(IsLetterOrDigit))
{
errors.Add(Resources.PasswordRequireNonLetterOrDigit);
errors.Add(Describer.PasswordRequiresNonLetterAndDigit());
}
if (options.RequireDigit && !password.Any(IsDigit))
{
errors.Add(Resources.PasswordRequireDigit);
errors.Add(Describer.PasswordRequiresDigit());
}
if (options.RequireLowercase && !password.Any(IsLower))
{
errors.Add(Resources.PasswordRequireLower);
errors.Add(Describer.PasswordRequiresLower());
}
if (options.RequireUppercase && !password.Any(IsUpper))
{
errors.Add(Resources.PasswordRequireUpper);
errors.Add(Describer.PasswordRequiresUpper());
}
return
Task.FromResult(errors.Count == 0
? IdentityResult.Success
: IdentityResult.Failed(String.Join(" ", errors)));
: IdentityResult.Failed(errors.ToArray()));
}
/// <summary>

View File

@ -10,6 +10,22 @@ namespace Microsoft.AspNet.Identity
private static readonly ResourceManager _resourceManager
= new ResourceManager("Microsoft.AspNet.Identity.Resources", typeof(Resources).GetTypeInfo().Assembly);
/// <summary>
/// Optimistic concurrency failure, object has been modified.
/// </summary>
internal static string ConcurrencyFailure
{
get { return GetString("ConcurrencyFailure"); }
}
/// <summary>
/// Optimistic concurrency failure, object has been modified.
/// </summary>
internal static string FormatConcurrencyFailure()
{
return GetString("ConcurrencyFailure");
}
/// <summary>
/// Your security code is: {0}
/// </summary>
@ -43,7 +59,7 @@ namespace Microsoft.AspNet.Identity
}
/// <summary>
/// Security Code
/// Security code
/// </summary>
internal static string DefaultEmailTokenProviderSubject
{
@ -51,7 +67,7 @@ namespace Microsoft.AspNet.Identity
}
/// <summary>
/// Security Code
/// Security code
/// </summary>
internal static string FormatDefaultEmailTokenProviderSubject()
{
@ -107,7 +123,7 @@ namespace Microsoft.AspNet.Identity
}
/// <summary>
/// DefaultTokenProvider
/// Default Token Provider
/// </summary>
internal static string DefaultTokenProvider
{
@ -115,7 +131,7 @@ namespace Microsoft.AspNet.Identity
}
/// <summary>
/// DefaultTokenProvider
/// Default Token Provider
/// </summary>
internal static string FormatDefaultTokenProvider()
{
@ -139,35 +155,35 @@ namespace Microsoft.AspNet.Identity
}
/// <summary>
/// Name {0} is already taken.
/// Role name '{0}' is already taken.
/// </summary>
internal static string DuplicateName
internal static string DuplicateRoleName
{
get { return GetString("DuplicateName"); }
get { return GetString("DuplicateRoleName"); }
}
/// <summary>
/// Name {0} is already taken.
/// Role name '{0}' is already taken.
/// </summary>
internal static string FormatDuplicateName(object p0)
internal static string FormatDuplicateRoleName(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("DuplicateName"), p0);
return string.Format(CultureInfo.CurrentCulture, GetString("DuplicateRoleName"), p0);
}
/// <summary>
/// A user with that external login already exists.
/// User name '{0}' is already taken.
/// </summary>
internal static string ExternalLoginExists
internal static string DuplicateUserName
{
get { return GetString("ExternalLoginExists"); }
get { return GetString("DuplicateUserName"); }
}
/// <summary>
/// A user with that external login already exists.
/// User name '{0}' is already taken.
/// </summary>
internal static string FormatExternalLoginExists()
internal static string FormatDuplicateUserName(object p0)
{
return GetString("ExternalLoginExists");
return string.Format(CultureInfo.CurrentCulture, GetString("DuplicateUserName"), p0);
}
/// <summary>
@ -218,6 +234,22 @@ namespace Microsoft.AspNet.Identity
return GetString("InvalidPasswordHasherIterationCount");
}
/// <summary>
/// Role name '{0}' is invalid.
/// </summary>
internal static string InvalidRoleName
{
get { return GetString("InvalidRoleName"); }
}
/// <summary>
/// Role name '{0}' is invalid.
/// </summary>
internal static string FormatInvalidRoleName(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("InvalidRoleName"), p0);
}
/// <summary>
/// Invalid token.
/// </summary>
@ -235,7 +267,7 @@ namespace Microsoft.AspNet.Identity
}
/// <summary>
/// User name {0} is invalid, can only contain letters or digits.
/// User name '{0}' is invalid, can only contain letters or digits.
/// </summary>
internal static string InvalidUserName
{
@ -243,7 +275,7 @@ namespace Microsoft.AspNet.Identity
}
/// <summary>
/// User name {0} is invalid, can only contain letters or digits.
/// User name '{0}' is invalid, can only contain letters or digits.
/// </summary>
internal static string FormatInvalidUserName(object p0)
{
@ -251,19 +283,19 @@ namespace Microsoft.AspNet.Identity
}
/// <summary>
/// Lockout is not enabled for this user.
/// A user with this login already exists.
/// </summary>
internal static string LockoutNotEnabled
internal static string LoginAlreadyAssociated
{
get { return GetString("LockoutNotEnabled"); }
get { return GetString("LoginAlreadyAssociated"); }
}
/// <summary>
/// Lockout is not enabled for this user.
/// A user with this login already exists.
/// </summary>
internal static string FormatLockoutNotEnabled()
internal static string FormatLoginAlreadyAssociated()
{
return GetString("LockoutNotEnabled");
return GetString("LoginAlreadyAssociated");
}
/// <summary>
@ -317,65 +349,65 @@ namespace Microsoft.AspNet.Identity
/// <summary>
/// Passwords must have at least one digit ('0'-'9').
/// </summary>
internal static string PasswordRequireDigit
internal static string PasswordRequiresDigit
{
get { return GetString("PasswordRequireDigit"); }
get { return GetString("PasswordRequiresDigit"); }
}
/// <summary>
/// Passwords must have at least one digit ('0'-'9').
/// </summary>
internal static string FormatPasswordRequireDigit()
internal static string FormatPasswordRequiresDigit()
{
return GetString("PasswordRequireDigit");
return GetString("PasswordRequiresDigit");
}
/// <summary>
/// Passwords must have at least one lowercase ('a'-'z').
/// </summary>
internal static string PasswordRequireLower
internal static string PasswordRequiresLower
{
get { return GetString("PasswordRequireLower"); }
get { return GetString("PasswordRequiresLower"); }
}
/// <summary>
/// Passwords must have at least one lowercase ('a'-'z').
/// </summary>
internal static string FormatPasswordRequireLower()
internal static string FormatPasswordRequiresLower()
{
return GetString("PasswordRequireLower");
return GetString("PasswordRequiresLower");
}
/// <summary>
/// Passwords must have at least one non letter and non digit character.
/// </summary>
internal static string PasswordRequireNonLetterOrDigit
internal static string PasswordRequiresNonLetterAndDigit
{
get { return GetString("PasswordRequireNonLetterOrDigit"); }
get { return GetString("PasswordRequiresNonLetterAndDigit"); }
}
/// <summary>
/// Passwords must have at least one non letter and non digit character.
/// </summary>
internal static string FormatPasswordRequireNonLetterOrDigit()
internal static string FormatPasswordRequiresNonLetterAndDigit()
{
return GetString("PasswordRequireNonLetterOrDigit");
return GetString("PasswordRequiresNonLetterAndDigit");
}
/// <summary>
/// Passwords must have at least one uppercase ('A'-'Z').
/// </summary>
internal static string PasswordRequireUpper
internal static string PasswordRequiresUpper
{
get { return GetString("PasswordRequireUpper"); }
get { return GetString("PasswordRequiresUpper"); }
}
/// <summary>
/// Passwords must have at least one uppercase ('A'-'Z').
/// </summary>
internal static string FormatPasswordRequireUpper()
internal static string FormatPasswordRequiresUpper()
{
return GetString("PasswordRequireUpper");
return GetString("PasswordRequiresUpper");
}
/// <summary>
@ -394,22 +426,6 @@ namespace Microsoft.AspNet.Identity
return string.Format(CultureInfo.CurrentCulture, GetString("PasswordTooShort"), p0);
}
/// <summary>
/// {0} cannot be null or empty.
/// </summary>
internal static string PropertyTooShort
{
get { return GetString("PropertyTooShort"); }
}
/// <summary>
/// {0} cannot be null or empty.
/// </summary>
internal static string FormatPropertyTooShort(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("PropertyTooShort"), p0);
}
/// <summary>
/// Role {0} does not exist.
/// </summary>
@ -651,7 +667,7 @@ namespace Microsoft.AspNet.Identity
}
/// <summary>
/// User already in role.
/// User already in role '{0}'.
/// </summary>
internal static string UserAlreadyInRole
{
@ -659,11 +675,43 @@ namespace Microsoft.AspNet.Identity
}
/// <summary>
/// User already in role.
/// User already in role '{0}'.
/// </summary>
internal static string FormatUserAlreadyInRole()
internal static string FormatUserAlreadyInRole(object p0)
{
return GetString("UserAlreadyInRole");
return string.Format(CultureInfo.CurrentCulture, GetString("UserAlreadyInRole"), p0);
}
/// <summary>
/// User is locked out.
/// </summary>
internal static string UserLockedOut
{
get { return GetString("UserLockedOut"); }
}
/// <summary>
/// User is locked out.
/// </summary>
internal static string FormatUserLockedOut()
{
return GetString("UserLockedOut");
}
/// <summary>
/// Lockout is not enabled for this user.
/// </summary>
internal static string UserLockoutNotEnabled
{
get { return GetString("UserLockoutNotEnabled"); }
}
/// <summary>
/// Lockout is not enabled for this user.
/// </summary>
internal static string FormatUserLockoutNotEnabled()
{
return GetString("UserLockoutNotEnabled");
}
/// <summary>
@ -683,7 +731,7 @@ namespace Microsoft.AspNet.Identity
}
/// <summary>
/// User is not in role.
/// User is not in role '{0}'.
/// </summary>
internal static string UserNotInRole
{
@ -691,11 +739,91 @@ namespace Microsoft.AspNet.Identity
}
/// <summary>
/// User is not in role.
/// User is not in role '{0}'.
/// </summary>
internal static string FormatUserNotInRole()
internal static string FormatUserNotInRole(object p0)
{
return GetString("UserNotInRole");
return string.Format(CultureInfo.CurrentCulture, GetString("UserNotInRole"), p0);
}
/// <summary>
/// {0} : Failed : {1}
/// </summary>
internal static string LogIdentityResultFailure
{
get { return GetString("LogIdentityResultFailure"); }
}
/// <summary>
/// {0} : Failed : {1}
/// </summary>
internal static string FormatLogIdentityResultFailure(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("LogIdentityResultFailure"), p0, p1);
}
/// <summary>
/// {0} : Success
/// </summary>
internal static string LogIdentityResultSuccess
{
get { return GetString("LogIdentityResultSuccess"); }
}
/// <summary>
/// {0} : Success
/// </summary>
internal static string FormatLogIdentityResultSuccess(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("LogIdentityResultSuccess"), p0);
}
/// <summary>
/// {0} : Result : {1}
/// </summary>
internal static string LoggingSigninResult
{
get { return GetString("LoggingSigninResult"); }
}
/// <summary>
/// {0} : Result : {1}
/// </summary>
internal static string FormatLoggingSigninResult(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("LoggingSigninResult"), p0, p1);
}
/// <summary>
/// {0} for user: {1}
/// </summary>
internal static string LoggingResultMessage
{
get { return GetString("LoggingResultMessage"); }
}
/// <summary>
/// {0} for user: {1}
/// </summary>
internal static string FormatLoggingResultMessage(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("LoggingResultMessage"), p0, p1);
}
/// <summary>
/// {0} for role: {1}
/// </summary>
internal static string LoggingResultMessageForRole
{
get { return GetString("LoggingResultMessageForRole"); }
}
/// <summary>
/// {0} for role: {1}
/// </summary>
internal static string FormatLoggingResultMessageForRole(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("LoggingResultMessageForRole"), p0, p1);
}
private static string GetString(string name, params string[] formatterNames)

View File

@ -0,0 +1,3 @@
{
"Profiles": []
}

View File

@ -117,6 +117,10 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ConcurrencyFailure" xml:space="preserve">
<value>Optimistic concurrency failure, object has been modified.</value>
<comment>Error when optimistic concurrency fails</comment>
</data>
<data name="DefaultEmailTokenProviderBodyFormat" xml:space="preserve">
<value>Your security code is: {0}</value>
<comment>Default body format for the email</comment>
@ -126,7 +130,7 @@
<comment>Default name for the email token provider</comment>
</data>
<data name="DefaultEmailTokenProviderSubject" xml:space="preserve">
<value>Security Code</value>
<value>Security code</value>
<comment>Default subject for the email</comment>
</data>
<data name="DefaultError" xml:space="preserve">
@ -142,24 +146,24 @@
<comment>Default name for the phone number token provider</comment>
</data>
<data name="DefaultTokenProvider" xml:space="preserve">
<value>DefaultTokenProvider</value>
<value>Default Token Provider</value>
<comment>Name of the default token provider</comment>
</data>
<data name="DuplicateEmail" xml:space="preserve">
<value>Email '{0}' is already taken.</value>
<comment>error for duplicate emails</comment>
<comment>Error for duplicate emails</comment>
</data>
<data name="DuplicateName" xml:space="preserve">
<value>Name {0} is already taken.</value>
<comment>error for duplicate usernames</comment>
<data name="DuplicateRoleName" xml:space="preserve">
<value>Role name '{0}' is already taken.</value>
<comment>Error for duplicate user names</comment>
</data>
<data name="ExternalLoginExists" xml:space="preserve">
<value>A user with that external login already exists.</value>
<comment>Error when a login already linked</comment>
<data name="DuplicateUserName" xml:space="preserve">
<value>User name '{0}' is already taken.</value>
<comment>Error for duplicate user names</comment>
</data>
<data name="InvalidEmail" xml:space="preserve">
<value>Email '{0}' is invalid.</value>
<comment>invalid email</comment>
<comment>Invalid email</comment>
</data>
<data name="InvalidPasswordHasherCompatibilityMode" xml:space="preserve">
<value>The provided PasswordHasherCompatibilityMode is invalid.</value>
@ -169,17 +173,21 @@
<value>The iteration count must be a positive integer.</value>
<comment>Error when the iteration count is &lt; 1.</comment>
</data>
<data name="InvalidRoleName" xml:space="preserve">
<value>Role name '{0}' is invalid.</value>
<comment>Error for invalid role names</comment>
</data>
<data name="InvalidToken" xml:space="preserve">
<value>Invalid token.</value>
<comment>Error when a token is not recognized</comment>
</data>
<data name="InvalidUserName" xml:space="preserve">
<value>User name {0} is invalid, can only contain letters or digits.</value>
<comment>usernames can only contain letters or digits</comment>
<value>User name '{0}' is invalid, can only contain letters or digits.</value>
<comment>User names can only contain letters or digits</comment>
</data>
<data name="LockoutNotEnabled" xml:space="preserve">
<value>Lockout is not enabled for this user.</value>
<comment>error when lockout is not enabled</comment>
<data name="LoginAlreadyAssociated" xml:space="preserve">
<value>A user with this login already exists.</value>
<comment>Error when a login already linked</comment>
</data>
<data name="NoMessageProvider" xml:space="preserve">
<value>No IUserMessageProvider named '{0}' is registered.</value>
@ -193,19 +201,19 @@
<value>Incorrect password.</value>
<comment>Error when a password doesn't match</comment>
</data>
<data name="PasswordRequireDigit" xml:space="preserve">
<data name="PasswordRequiresDigit" xml:space="preserve">
<value>Passwords must have at least one digit ('0'-'9').</value>
<comment>Error when passwords do not have a digit</comment>
</data>
<data name="PasswordRequireLower" xml:space="preserve">
<data name="PasswordRequiresLower" xml:space="preserve">
<value>Passwords must have at least one lowercase ('a'-'z').</value>
<comment>Error when passwords do not have a lowercase letter</comment>
</data>
<data name="PasswordRequireNonLetterOrDigit" xml:space="preserve">
<data name="PasswordRequiresNonLetterAndDigit" xml:space="preserve">
<value>Passwords must have at least one non letter and non digit character.</value>
<comment>Error when password does not have enough letter or digit characters</comment>
</data>
<data name="PasswordRequireUpper" xml:space="preserve">
<data name="PasswordRequiresUpper" xml:space="preserve">
<value>Passwords must have at least one uppercase ('A'-'Z').</value>
<comment>Error when passwords do not have an uppercase letter</comment>
</data>
@ -213,80 +221,104 @@
<value>Passwords must be at least {0} characters.</value>
<comment>Error message for passwords that are too short</comment>
</data>
<data name="PropertyTooShort" xml:space="preserve">
<value>{0} cannot be null or empty.</value>
<comment>error for empty or null usernames</comment>
</data>
<data name="RoleNotFound" xml:space="preserve">
<value>Role {0} does not exist.</value>
<comment>error when a role does not exist</comment>
<comment>Error when a role does not exist</comment>
</data>
<data name="StoreNotIQueryableRoleStore" xml:space="preserve">
<value>Store does not implement IQueryableRoleStore&lt;TRole&gt;.</value>
<comment>error when the store does not implement this interface</comment>
<comment>Error when the store does not implement this interface</comment>
</data>
<data name="StoreNotIQueryableUserStore" xml:space="preserve">
<value>Store does not implement IQueryableUserStore&lt;TUser&gt;.</value>
<comment>error when the store does not implement this interface</comment>
<comment>Error when the store does not implement this interface</comment>
</data>
<data name="StoreNotIRoleClaimStore" xml:space="preserve">
<value>Store does not implement IRoleClaimStore&lt;TRole&gt;.</value>
<comment>error when the store does not implement this interface</comment>
<comment>Error when the store does not implement this interface</comment>
</data>
<data name="StoreNotIUserClaimStore" xml:space="preserve">
<value>Store does not implement IUserClaimStore&lt;TUser&gt;.</value>
<comment>error when the store does not implement this interface</comment>
<comment>Error when the store does not implement this interface</comment>
</data>
<data name="StoreNotIUserConfirmationStore" xml:space="preserve">
<value>Store does not implement IUserConfirmationStore&lt;TUser&gt;.</value>
<comment>error when the store does not implement this interface</comment>
<comment>Error when the store does not implement this interface</comment>
</data>
<data name="StoreNotIUserEmailStore" xml:space="preserve">
<value>Store does not implement IUserEmailStore&lt;TUser&gt;.</value>
<comment>error when the store does not implement this interface</comment>
<comment>Error when the store does not implement this interface</comment>
</data>
<data name="StoreNotIUserLockoutStore" xml:space="preserve">
<value>Store does not implement IUserLockoutStore&lt;TUser&gt;.</value>
<comment>error when the store does not implement this interface</comment>
<comment>Error when the store does not implement this interface</comment>
</data>
<data name="StoreNotIUserLoginStore" xml:space="preserve">
<value>Store does not implement IUserLoginStore&lt;TUser&gt;.</value>
<comment>error when the store does not implement this interface</comment>
<comment>Error when the store does not implement this interface</comment>
</data>
<data name="StoreNotIUserPasswordStore" xml:space="preserve">
<value>Store does not implement IUserPasswordStore&lt;TUser&gt;.</value>
<comment>error when the store does not implement this interface</comment>
<comment>Error when the store does not implement this interface</comment>
</data>
<data name="StoreNotIUserPhoneNumberStore" xml:space="preserve">
<value>Store does not implement IUserPhoneNumberStore&lt;TUser&gt;.</value>
<comment>error when the store does not implement this interface</comment>
<comment>Error when the store does not implement this interface</comment>
</data>
<data name="StoreNotIUserRoleStore" xml:space="preserve">
<value>Store does not implement IUserRoleStore&lt;TUser&gt;.</value>
<comment>error when the store does not implement this interface</comment>
<comment>Error when the store does not implement this interface</comment>
</data>
<data name="StoreNotIUserSecurityStampStore" xml:space="preserve">
<value>Store does not implement IUserSecurityStampStore&lt;TUser&gt;.</value>
<comment>error when the store does not implement this interface</comment>
<comment>Error when the store does not implement this interface</comment>
</data>
<data name="StoreNotIUserTwoFactorStore" xml:space="preserve">
<value>Store does not implement IUserTwoFactorStore&lt;TUser&gt;.</value>
<comment>error when the store does not implement this interface</comment>
<comment>Error when the store does not implement this interface</comment>
</data>
<data name="UserAlreadyHasPassword" xml:space="preserve">
<value>User already has a password set.</value>
<comment>error when AddPasswordAsync called when a user already has a password</comment>
<comment>Error when AddPasswordAsync called when a user already has a password</comment>
</data>
<data name="UserAlreadyInRole" xml:space="preserve">
<value>User already in role.</value>
<value>User already in role '{0}'.</value>
<comment>Error when a user is already in a role</comment>
</data>
<data name="UserLockedOut" xml:space="preserve">
<value>User is locked out.</value>
<comment>Error when a user is locked out</comment>
</data>
<data name="UserLockoutNotEnabled" xml:space="preserve">
<value>Lockout is not enabled for this user.</value>
<comment>Error when lockout is not enabled</comment>
</data>
<data name="UserNameNotFound" xml:space="preserve">
<value>User {0} does not exist.</value>
<comment>error when a user does not exist</comment>
<comment>Error when a user does not exist</comment>
</data>
<data name="UserNotInRole" xml:space="preserve">
<value>User is not in role.</value>
<value>User is not in role '{0}'.</value>
<comment>Error when a user is not in the role</comment>
</data>
<data name="LogIdentityResultFailure" xml:space="preserve">
<value>{0} : Failed : {1}</value>
<comment>Logging method execution failure</comment>
</data>
<data name="LogIdentityResultSuccess" xml:space="preserve">
<value>{0} : Success</value>
<comment>Logging method execution success</comment>
</data>
<data name="LoggingSigninResult" xml:space="preserve">
<value>{0} : Result : {1}</value>
<comment>Logging statement for SignInManager</comment>
</data>
<data name="LoggingResultMessage" xml:space="preserve">
<value>{0} for user: {1}</value>
<comment>Message prefix for Identity result</comment>
</data>
<data name="LoggingResultMessageForRole" xml:space="preserve">
<value>{0} for role: {1}</value>
<comment>Message prefix for Identity result for role operation</comment>
</data>
</root>

View File

@ -7,7 +7,7 @@ using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging;
namespace Microsoft.AspNet.Identity
{
@ -24,13 +24,19 @@ namespace Microsoft.AspNet.Identity
/// </summary>
/// <param name="store">The IRoleStore commits changes via the UpdateAsync/CreateAsync methods</param>
/// <param name="roleValidator"></param>
public RoleManager(IRoleStore<TRole> store, IEnumerable<IRoleValidator<TRole>> roleValidators)
public RoleManager(IRoleStore<TRole> store,
IEnumerable<IRoleValidator<TRole>> roleValidators = null,
ILookupNormalizer keyNormalizer = null,
IdentityErrorDescriber errors = null,
ILoggerFactory loggerFactory = null)
{
if (store == null)
{
throw new ArgumentNullException("store");
}
Store = store;
KeyNormalizer = keyNormalizer ?? new UpperInvariantLookupNormalizer();
ErrorDescriber = errors ?? new IdentityErrorDescriber();
if (roleValidators != null)
{
@ -39,6 +45,9 @@ namespace Microsoft.AspNet.Identity
RoleValidators.Add(v);
}
}
loggerFactory = loggerFactory ?? new LoggerFactory();
Logger = loggerFactory.Create(nameof(RoleManager<TRole>));
}
/// <summary>
@ -51,6 +60,21 @@ namespace Microsoft.AspNet.Identity
/// </summary>
public IList<IRoleValidator<TRole>> RoleValidators { get; } = new List<IRoleValidator<TRole>>();
/// <summary>
/// Used to generate public API error messages
/// </summary>
public IdentityErrorDescriber ErrorDescriber { get; set; }
/// <summary>
/// Used to log results
/// </summary>
public ILogger Logger { get; set; }
/// <summary>
/// Used to normalize user names, role names, emails for uniqueness
/// </summary>
public ILookupNormalizer KeyNormalizer { get; set; }
/// <summary>
/// Returns an IQueryable of roles if the store is an IQueryableRoleStore
/// </summary>
@ -102,7 +126,7 @@ namespace Microsoft.AspNet.Identity
private async Task<IdentityResult> ValidateRoleInternal(TRole role, CancellationToken cancellationToken)
{
var errors = new List<string>();
var errors = new List<IdentityError>();
foreach (var v in RoleValidators)
{
var result = await v.ValidateAsync(this, role, cancellationToken);
@ -120,7 +144,7 @@ namespace Microsoft.AspNet.Identity
/// <param name="role"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<IdentityResult> CreateAsync(TRole role,
public virtual async Task<IdentityResult> CreateAsync(TRole role,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
@ -134,17 +158,31 @@ namespace Microsoft.AspNet.Identity
{
return result;
}
await Store.CreateAsync(role, cancellationToken);
return IdentityResult.Success;
await UpdateNormalizedRoleNameAsync(role, cancellationToken);
return await LogResultAsync(await Store.CreateAsync(role, cancellationToken), role);
}
/// <summary>
/// Update the user's normalized user name
/// </summary>
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task UpdateNormalizedRoleNameAsync(TRole role,
CancellationToken cancellationToken = default(CancellationToken))
{
var name = await GetRoleNameAsync(role, cancellationToken);
await Store.SetNormalizedRoleNameAsync(role, NormalizeKey(name), cancellationToken);
}
/// <summary>
/// UpdateAsync an existing role
/// </summary>
/// <param name="role"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<IdentityResult> UpdateAsync(TRole role,
public virtual async Task<IdentityResult> UpdateAsync(TRole role,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
@ -153,13 +191,19 @@ namespace Microsoft.AspNet.Identity
throw new ArgumentNullException("role");
}
return await LogResultAsync(await UpdateRoleAsync(role, cancellationToken), role);
}
private async Task<IdentityResult> UpdateRoleAsync(TRole role,
CancellationToken cancellationToken = default(CancellationToken))
{
var result = await ValidateRoleInternal(role, cancellationToken);
if (!result.Succeeded)
{
return result;
}
await Store.UpdateAsync(role, cancellationToken);
return IdentityResult.Success;
await UpdateNormalizedRoleNameAsync(role, cancellationToken);
return await Store.UpdateAsync(role, cancellationToken);
}
/// <summary>
@ -168,7 +212,7 @@ namespace Microsoft.AspNet.Identity
/// <param name="role"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<IdentityResult> DeleteAsync(TRole role,
public virtual async Task<IdentityResult> DeleteAsync(TRole role,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
@ -176,9 +220,7 @@ namespace Microsoft.AspNet.Identity
{
throw new ArgumentNullException("role");
}
await Store.DeleteAsync(role, cancellationToken);
return IdentityResult.Success;
return await LogResultAsync(await Store.DeleteAsync(role, cancellationToken), role);
}
/// <summary>
@ -187,7 +229,7 @@ namespace Microsoft.AspNet.Identity
/// <param name="roleName"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<bool> RoleExistsAsync(string roleName,
public virtual async Task<bool> RoleExistsAsync(string roleName,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
@ -196,16 +238,27 @@ namespace Microsoft.AspNet.Identity
throw new ArgumentNullException("roleName");
}
return await FindByNameAsync(roleName, cancellationToken) != null;
return await FindByNameAsync(NormalizeKey(roleName), cancellationToken) != null;
}
/// <summary>
/// Normalize a key (role name) for uniqueness comparisons
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public virtual string NormalizeKey(string key)
{
return (KeyNormalizer == null) ? key : KeyNormalizer.Normalize(key);
}
/// <summary>
/// FindByLoginAsync a role by id
/// </summary>
/// <param name="roleId"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<TRole> FindByIdAsync(string roleId,
public virtual async Task<TRole> FindByIdAsync(string roleId,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
@ -218,7 +271,7 @@ namespace Microsoft.AspNet.Identity
/// <param name="role"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<string> GetRoleNameAsync(TRole role,
public virtual async Task<string> GetRoleNameAsync(TRole role,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
@ -232,12 +285,13 @@ namespace Microsoft.AspNet.Identity
/// <param name="name"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<IdentityResult> SetRoleNameAsync(TRole role, string name,
public virtual async Task<IdentityResult> SetRoleNameAsync(TRole role, string name,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
await Store.SetRoleNameAsync(role, name, cancellationToken);
return IdentityResult.Success;
await UpdateNormalizedRoleNameAsync(role, cancellationToken);
return await LogResultAsync(IdentityResult.Success, role);
}
/// <summary>
@ -246,7 +300,7 @@ namespace Microsoft.AspNet.Identity
/// <param name="role"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<string> GetRoleIdAsync(TRole role,
public virtual async Task<string> GetRoleIdAsync(TRole role,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
@ -259,7 +313,7 @@ namespace Microsoft.AspNet.Identity
/// <param name="roleName"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<TRole> FindByNameAsync(string roleName,
public virtual async Task<TRole> FindByNameAsync(string roleName,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
@ -268,7 +322,7 @@ namespace Microsoft.AspNet.Identity
throw new ArgumentNullException("roleName");
}
return await Store.FindByNameAsync(roleName, cancellationToken);
return await Store.FindByNameAsync(NormalizeKey(roleName), cancellationToken);
}
// IRoleClaimStore methods
@ -289,7 +343,7 @@ namespace Microsoft.AspNet.Identity
/// <param name="claim"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<IdentityResult> AddClaimAsync(TRole role, Claim claim,
public virtual async Task<IdentityResult> AddClaimAsync(TRole role, Claim claim,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
@ -303,7 +357,7 @@ namespace Microsoft.AspNet.Identity
throw new ArgumentNullException("role");
}
await claimStore.AddClaimAsync(role, claim, cancellationToken);
return await UpdateAsync(role, cancellationToken);
return await LogResultAsync(await UpdateRoleAsync(role, cancellationToken), role);
}
/// <summary>
@ -313,7 +367,7 @@ namespace Microsoft.AspNet.Identity
/// <param name="claim"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<IdentityResult> RemoveClaimAsync(TRole role, Claim claim,
public virtual async Task<IdentityResult> RemoveClaimAsync(TRole role, Claim claim,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
@ -323,7 +377,7 @@ namespace Microsoft.AspNet.Identity
throw new ArgumentNullException("role");
}
await claimStore.RemoveClaimAsync(role, claim, cancellationToken);
return await UpdateAsync(role, cancellationToken);
return await LogResultAsync(await UpdateRoleAsync(role, cancellationToken), role);
}
/// <summary>
@ -332,7 +386,7 @@ namespace Microsoft.AspNet.Identity
/// <param name="role"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<IList<Claim>> GetClaimsAsync(TRole role,
public virtual async Task<IList<Claim>> GetClaimsAsync(TRole role,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
@ -344,6 +398,21 @@ namespace Microsoft.AspNet.Identity
return await claimStore.GetClaimsAsync(role, cancellationToken);
}
/// <summary>
/// Logs the current Identity Result and returns result object
/// </summary>
/// <param name="result"></param>
/// <param name="user"></param>
/// <param name="methodName"></param>
/// <returns></returns>
protected async Task<IdentityResult> LogResultAsync(IdentityResult result,
TRole role, [System.Runtime.CompilerServices.CallerMemberName] string methodName = "")
{
result.Log(Logger, Resources.FormatLoggingResultMessageForRole(methodName, await GetRoleIdAsync(role)));
return result;
}
private void ThrowIfDisposed()
{
if (_disposed)

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
@ -15,6 +14,13 @@ namespace Microsoft.AspNet.Identity
/// <typeparam name="TRole"></typeparam>
public class RoleValidator<TRole> : IRoleValidator<TRole> where TRole : class
{
public RoleValidator(IdentityErrorDescriber errors = null)
{
Describer = errors ?? new IdentityErrorDescriber();
}
private IdentityErrorDescriber Describer { get; set; }
/// <summary>
/// Validates a role before saving
/// </summary>
@ -33,7 +39,7 @@ namespace Microsoft.AspNet.Identity
{
throw new ArgumentNullException("role");
}
var errors = new List<string>();
var errors = new List<IdentityError>();
await ValidateRoleName(manager, role, errors);
if (errors.Count > 0)
{
@ -42,13 +48,13 @@ namespace Microsoft.AspNet.Identity
return IdentityResult.Success;
}
private static async Task ValidateRoleName(RoleManager<TRole> manager, TRole role,
ICollection<string> errors)
private async Task ValidateRoleName(RoleManager<TRole> manager, TRole role,
ICollection<IdentityError> errors)
{
var roleName = await manager.GetRoleNameAsync(role);
if (string.IsNullOrWhiteSpace(roleName))
{
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.PropertyTooShort, "Name"));
errors.Add(Describer.InvalidRoleName(roleName));
}
else
{
@ -56,7 +62,7 @@ namespace Microsoft.AspNet.Identity
if (owner != null &&
!string.Equals(await manager.GetRoleIdAsync(owner), await manager.GetRoleIdAsync(role)))
{
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.DuplicateName, roleName));
errors.Add(Describer.DuplicateRoleName(roleName));
}
}
}

View File

@ -8,9 +8,10 @@ using System.Security.Claims;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Security;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging;
using Microsoft.Framework.OptionsModel;
namespace Microsoft.AspNet.Identity
@ -21,8 +22,11 @@ namespace Microsoft.AspNet.Identity
/// <typeparam name="TUser"></typeparam>
public class SignInManager<TUser> where TUser : class
{
public SignInManager(UserManager<TUser> userManager, IContextAccessor<HttpContext> contextAccessor,
IClaimsIdentityFactory<TUser> claimsFactory, IOptions<IdentityOptions> optionsAccessor)
public SignInManager(UserManager<TUser> userManager,
IHttpContextAccessor contextAccessor,
IClaimsIdentityFactory<TUser> claimsFactory,
IOptions<IdentityOptions> optionsAccessor = null,
ILoggerFactory loggerFactory = null)
{
if (userManager == null)
{
@ -36,20 +40,21 @@ namespace Microsoft.AspNet.Identity
{
throw new ArgumentNullException(nameof(claimsFactory));
}
if (optionsAccessor == null || optionsAccessor.Options == null)
{
throw new ArgumentNullException(nameof(optionsAccessor));
}
UserManager = userManager;
Context = contextAccessor.Value;
ClaimsFactory = claimsFactory;
Options = optionsAccessor.Options;
Options = optionsAccessor?.Options ?? new IdentityOptions();
loggerFactory = loggerFactory ?? new LoggerFactory();
Logger = loggerFactory.Create(nameof(SignInManager<TUser>));
}
public UserManager<TUser> UserManager { get; private set; }
public HttpContext Context { get; private set; }
public IClaimsIdentityFactory<TUser> ClaimsFactory { get; private set; }
public IdentityOptions Options { get; private set; }
public ILogger Logger { get; set; }
// Should this be a func?
public virtual async Task<ClaimsIdentity> CreateUserIdentityAsync(TUser user,
@ -63,13 +68,13 @@ namespace Microsoft.AspNet.Identity
{
if (Options.SignIn.RequireConfirmedEmail && !(await UserManager.IsEmailConfirmedAsync(user, cancellationToken)))
{
return false;
return await LogResultAsync(false, user);
}
if (Options.SignIn.RequireConfirmedPhoneNumber && !(await UserManager.IsPhoneNumberConfirmedAsync(user, cancellationToken)))
{
return false;
return await LogResultAsync(false, user);
}
return true;
return await LogResultAsync(true, user);
}
public virtual async Task SignInAsync(TUser user, bool isPersistent, string authenticationMethod = null,
@ -96,15 +101,15 @@ namespace Microsoft.AspNet.Identity
return UserManager.SupportsUserLockout && await UserManager.IsLockedOutAsync(user, token);
}
private async Task<SignInStatus?> PreSignInCheck(TUser user, CancellationToken token)
private async Task<SignInResult> PreSignInCheck(TUser user, CancellationToken token)
{
if (!await CanSignInAsync(user, token))
{
return SignInStatus.NotAllowed;
return SignInResult.NotAllowed;
}
if (await IsLockedOut(user, token))
{
return SignInStatus.LockedOut;
return SignInResult.LockedOut;
}
return null;
}
@ -141,18 +146,26 @@ namespace Microsoft.AspNet.Identity
return null;
}
public virtual async Task<SignInStatus> PasswordSignInAsync(TUser user, string password,
public virtual async Task<SignInResult> PasswordSignInAsync(TUser user, string password,
bool isPersistent, bool shouldLockout, CancellationToken cancellationToken = default(CancellationToken))
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
var error = await PreSignInCheck(user, cancellationToken);
if (error != null)
{
return error.Value;
return await LogResultAsync(error, user);
}
if (await IsLockedOut(user, cancellationToken))
{
return await LogResultAsync(SignInResult.LockedOut, user);
}
if (await UserManager.CheckPasswordAsync(user, password, cancellationToken))
{
await ResetLockout(user, cancellationToken);
return await SignInOrTwoFactorAsync(user, isPersistent, cancellationToken);
return await LogResultAsync(await SignInOrTwoFactorAsync(user, isPersistent, cancellationToken), user);
}
if (UserManager.SupportsUserLockout && shouldLockout)
{
@ -160,19 +173,20 @@ namespace Microsoft.AspNet.Identity
await UserManager.AccessFailedAsync(user, cancellationToken);
if (await UserManager.IsLockedOutAsync(user, cancellationToken))
{
return SignInStatus.LockedOut;
return await LogResultAsync(SignInResult.LockedOut, user);
}
}
return SignInStatus.Failure;
return await LogResultAsync(SignInResult.Failed, user);
}
public virtual async Task<SignInStatus> PasswordSignInAsync(string userName, string password,
public virtual async Task<SignInResult> PasswordSignInAsync(string userName, string password,
bool isPersistent, bool shouldLockout, CancellationToken cancellationToken = default(CancellationToken))
{
var user = await UserManager.FindByNameAsync(userName, cancellationToken);
if (user == null)
{
return SignInStatus.Failure;
return SignInResult.Failed;
}
return await PasswordSignInAsync(user, password, isPersistent, shouldLockout, cancellationToken);
}
@ -207,9 +221,8 @@ namespace Microsoft.AspNet.Identity
return false;
}
var token = await UserManager.GenerateTwoFactorTokenAsync(user, provider, cancellationToken);
// See IdentityConfig.cs to plug in Email/SMS services to actually send the code
await UserManager.NotifyTwoFactorTokenAsync(user, provider, token, cancellationToken);
return true;
return await LogResultAsync(true, user);
}
public async Task<bool> IsTwoFactorClientRememberedAsync(TUser user,
@ -236,23 +249,23 @@ namespace Microsoft.AspNet.Identity
return Task.FromResult(0);
}
public virtual async Task<SignInStatus> TwoFactorSignInAsync(string provider, string code, bool isPersistent,
public virtual async Task<SignInResult> TwoFactorSignInAsync(string provider, string code, bool isPersistent,
bool rememberClient, CancellationToken cancellationToken = default(CancellationToken))
{
var twoFactorInfo = await RetrieveTwoFactorInfoAsync(cancellationToken);
if (twoFactorInfo == null || twoFactorInfo.UserId == null)
{
return SignInStatus.Failure;
return SignInResult.Failed;
}
var user = await UserManager.FindByIdAsync(twoFactorInfo.UserId, cancellationToken);
if (user == null)
{
return SignInStatus.Failure;
return SignInResult.Failed;
}
var error = await PreSignInCheck(user, cancellationToken);
if (error != null)
{
return error.Value;
return await LogResultAsync(error, user);
}
if (await UserManager.VerifyTwoFactorTokenAsync(user, provider, code, cancellationToken))
{
@ -268,11 +281,13 @@ namespace Microsoft.AspNet.Identity
{
await RememberTwoFactorClientAsync(user, cancellationToken);
}
return SignInStatus.Success;
await UserManager.ResetAccessFailedCountAsync(user, cancellationToken);
await SignInAsync(user, isPersistent);
return await LogResultAsync(SignInResult.Success, user);
}
// If the token is incorrect, record the failure which also may cause the user to be locked out
await UserManager.AccessFailedAsync(user, cancellationToken);
return SignInStatus.Failure;
return await LogResultAsync(SignInResult.Failed, user);
}
/// <summary>
@ -292,20 +307,20 @@ namespace Microsoft.AspNet.Identity
return await UserManager.FindByIdAsync(info.UserId, cancellationToken);
}
public virtual async Task<SignInStatus> ExternalLoginSignInAsync(string loginProvider, string providerKey, bool isPersistent,
public async Task<SignInResult> ExternalLoginSignInAsync(string loginProvider, string providerKey, bool isPersistent,
CancellationToken cancellationToken = default(CancellationToken))
{
var user = await UserManager.FindByLoginAsync(loginProvider, providerKey, cancellationToken);
if (user == null)
{
return SignInStatus.Failure;
return SignInResult.Failed;
}
var error = await PreSignInCheck(user, cancellationToken);
if (error != null)
{
return error.Value;
return await LogResultAsync(error, user);
}
return await SignInOrTwoFactorAsync(user, isPersistent, cancellationToken, loginProvider);
return await LogResultAsync(await SignInOrTwoFactorAsync(user, isPersistent, cancellationToken, loginProvider), user);
}
private const string LoginProviderKey = "LoginProvider";
@ -316,7 +331,7 @@ namespace Microsoft.AspNet.Identity
return Context.GetAuthenticationTypes().Where(d => !string.IsNullOrEmpty(d.Caption));
}
public virtual async Task<ExternalLoginInfo> GetExternalLoginInfoAsync(string expectedXsrf = null,
public virtual async Task<ExternalLoginInfo> GetExternalLoginInfoAsync(string expectedXsrf = null,
CancellationToken cancellationToken = default(CancellationToken))
{
var auth = await Context.AuthenticateAsync(IdentityOptions.ExternalCookieAuthenticationType);
@ -358,10 +373,10 @@ namespace Microsoft.AspNet.Identity
return properties;
}
private async Task<SignInStatus> SignInOrTwoFactorAsync(TUser user, bool isPersistent,
private async Task<SignInResult> SignInOrTwoFactorAsync(TUser user, bool isPersistent,
CancellationToken cancellationToken, string loginProvider = null)
{
if (UserManager.SupportsUserTwoFactor &&
if (UserManager.SupportsUserTwoFactor &&
await UserManager.GetTwoFactorEnabledAsync(user, cancellationToken) &&
(await UserManager.GetValidTwoFactorProvidersAsync(user, cancellationToken)).Count > 0)
{
@ -370,7 +385,7 @@ namespace Microsoft.AspNet.Identity
// Store the userId for use after two factor check
var userId = await UserManager.GetUserIdAsync(user, cancellationToken);
Context.Response.SignIn(StoreTwoFactorInfo(userId, loginProvider));
return SignInStatus.RequiresVerification;
return SignInResult.TwoFactorRequired;
}
}
// Cleanup external cookie
@ -379,7 +394,7 @@ namespace Microsoft.AspNet.Identity
Context.Response.SignOut(IdentityOptions.ExternalCookieAuthenticationType);
}
await SignInAsync(user, isPersistent, loginProvider, cancellationToken);
return SignInStatus.Success;
return SignInResult.Success;
}
private async Task<TwoFactorAuthenticationInfo> RetrieveTwoFactorInfoAsync(CancellationToken cancellationToken)
@ -396,6 +411,35 @@ namespace Microsoft.AspNet.Identity
return null;
}
/// <summary>
/// Log boolean result for user and return result
/// </summary>
/// <param name="result"></param>
/// <param name="user"></param>
/// <param name="methodName"></param>
/// <returns></returns>
protected async virtual Task<bool> LogResultAsync(bool result, TUser user, [System.Runtime.CompilerServices.CallerMemberName] string methodName = "")
{
Logger.WriteInformation(Resources.FormatLoggingSigninResult(Resources.FormatLoggingResultMessage(methodName,
await UserManager.GetUserIdAsync(user)), result));
return result;
}
/// <summary>
/// Log SignInStatus for user and return SignInStatus
/// </summary>
/// <param name="status"></param>
/// <param name="user"></param>
/// <param name="methodName"></param>
/// <returns></returns>
protected async virtual Task<SignInResult> LogResultAsync(SignInResult status, TUser user, [System.Runtime.CompilerServices.CallerMemberName] string methodName = "")
{
status.Log(Logger, Resources.FormatLoggingResultMessage(methodName, await UserManager.GetUserIdAsync(user)));
return status;
}
internal static ClaimsIdentity StoreTwoFactorInfo(string userId, string loginProvider)
{
var identity = new ClaimsIdentity(IdentityOptions.TwoFactorUserIdCookieAuthenticationType);

View File

@ -0,0 +1,113 @@
// 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.Logging;
namespace Microsoft.AspNet.Identity
{
/// <summary>
/// Represents the result of an sign in operation
/// </summary>
public class SignInResult
{
private static readonly SignInResult _success = new SignInResult { Succeeded = true };
private static readonly SignInResult _failed = new SignInResult();
private static readonly SignInResult _lockedOut = new SignInResult { IsLockedOut = true };
private static readonly SignInResult _notAllowed = new SignInResult { IsNotAllowed = true };
private static readonly SignInResult _twoFactorRequired = new SignInResult { RequiresTwoFactor = true };
/// <summary>
/// True if the operation was successful
/// </summary>
public bool Succeeded { get; protected set; }
/// <summary>
/// True if the user is locked out
/// </summary>
public bool IsLockedOut { get; protected set; }
/// <summary>
/// True if the user is not allowed to sign in
/// </summary>
public bool IsNotAllowed { get; protected set; }
/// <summary>
/// True if the sign in requires two factor
/// </summary>
public bool RequiresTwoFactor { get; protected set; }
/// <summary>
/// Static success result
/// </summary>
/// <returns></returns>
public static SignInResult Success
{
get { return _success; }
}
/// <summary>
/// Static failure result
/// </summary>
/// <returns></returns>
public static SignInResult Failed
{
get { return _failed; }
}
/// <summary>
/// Static locked out result
/// </summary>
/// <returns></returns>
public static SignInResult LockedOut
{
get { return _lockedOut; }
}
/// <summary>
/// Static not allowed result
/// </summary>
/// <returns></returns>
public static SignInResult NotAllowed
{
get { return _notAllowed; }
}
/// <summary>
/// Static two factor required result
/// </summary>
/// <returns></returns>
public static SignInResult TwoFactorRequired
{
get { return _twoFactorRequired; }
}
/// <summary>
/// Log result based on properties
/// </summary>
/// <param name="logger"></param>
/// <param name="message"></param>
public virtual void Log(ILogger logger, string message)
{
if (IsLockedOut)
{
logger.WriteInformation(Resources.FormatLoggingSigninResult(message, "Lockedout"));
}
else if (IsNotAllowed)
{
logger.WriteInformation(Resources.FormatLoggingSigninResult(message, "NotAllowed"));
}
else if (RequiresTwoFactor)
{
logger.WriteInformation(Resources.FormatLoggingSigninResult(message, "RequiresTwoFactor"));
}
else if (Succeeded)
{
logger.WriteInformation(Resources.FormatLoggingSigninResult(message, "Succeeded"));
}
else
{
logger.WriteInformation(Resources.FormatLoggingSigninResult(message, "Failed"));
}
}
}
}

View File

@ -6,22 +6,22 @@ using System;
namespace Microsoft.AspNet.Identity
{
/// <summary>
/// Normalizes user names via ToUpperInvariant()
/// Normalizes via ToUpperInvariant()
/// </summary>
public class UpperInvariantUserNameNormalizer : IUserNameNormalizer
public class UpperInvariantLookupNormalizer : ILookupNormalizer
{
/// <summary>
/// Normalizes user names via ToUpperInvariant()
/// Normalizes via ToUpperInvariant()
/// </summary>
/// <param name="userName"></param>
/// <param name="key"></param>
/// <returns></returns>
public string Normalize(string userName)
public string Normalize(string key)
{
if (userName == null)
if (key == null)
{
return null;
}
return userName.Normalize().ToUpperInvariant();
return key.Normalize().ToUpperInvariant();
}
}
}

View File

@ -9,6 +9,7 @@ using System.Security.Claims;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Framework.Logging;
using Microsoft.Framework.OptionsModel;
namespace Microsoft.AspNet.Identity
@ -30,39 +31,39 @@ namespace Microsoft.AspNet.Identity
private IdentityOptions _options;
/// <summary>
/// Constructor which takes a service provider and user store
/// Constructor
/// </summary>
/// <param name="store"></param>
/// <param name="optionsAccessor"></param>
/// <param name="passwordHasher"></param>
/// <param name="userValidator"></param>
/// <param name="passwordValidator"></param>
/// <param name="claimsIdentityFactory"></param>
public UserManager(IUserStore<TUser> store,
IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<TUser> passwordHasher,
IEnumerable<IUserValidator<TUser>> userValidators,
IEnumerable<IPasswordValidator<TUser>> passwordValidators,
IUserNameNormalizer userNameNormalizer,
IEnumerable<IUserTokenProvider<TUser>> tokenProviders,
IEnumerable<IIdentityMessageProvider> msgProviders)
/// <param name="userValidators"></param>
/// <param name="passwordValidators"></param>
/// <param name="keyNormalizer"></param>
/// <param name="errors"></param>
/// <param name="tokenProviders"></param>
/// <param name="msgProviders"></param>
/// <param name="loggerFactory"></param>
public UserManager(IUserStore<TUser> store,
IOptions<IdentityOptions> optionsAccessor = null,
IPasswordHasher<TUser> passwordHasher = null,
IEnumerable<IUserValidator<TUser>> userValidators = null,
IEnumerable<IPasswordValidator<TUser>> passwordValidators = null,
ILookupNormalizer keyNormalizer = null,
IdentityErrorDescriber errors = null,
IEnumerable<IUserTokenProvider<TUser>> tokenProviders = null,
IEnumerable<IIdentityMessageProvider> msgProviders = null,
ILoggerFactory loggerFactory = null)
{
if (store == null)
{
throw new ArgumentNullException(nameof(store));
}
if (optionsAccessor == null || optionsAccessor.Options == null)
{
throw new ArgumentNullException(nameof(optionsAccessor));
}
if (passwordHasher == null)
{
throw new ArgumentNullException(nameof(passwordHasher));
}
Store = store;
Options = optionsAccessor.Options;
PasswordHasher = passwordHasher;
UserNameNormalizer = userNameNormalizer;
Options = optionsAccessor?.Options ?? new IdentityOptions();
PasswordHasher = passwordHasher ?? new PasswordHasher<TUser>();
KeyNormalizer = keyNormalizer ?? new UpperInvariantLookupNormalizer();
ErrorDescriber = errors ?? new IdentityErrorDescriber();
if (userValidators != null)
{
foreach (var v in userValidators)
@ -77,6 +78,10 @@ namespace Microsoft.AspNet.Identity
PasswordValidators.Add(v);
}
}
loggerFactory = loggerFactory ?? new LoggerFactory();
Logger = loggerFactory.Create(nameof(UserManager<TUser>));
if (tokenProviders != null)
{
foreach (var tokenProvider in tokenProviders)
@ -132,9 +137,19 @@ namespace Microsoft.AspNet.Identity
public IList<IPasswordValidator<TUser>> PasswordValidators { get; } = new List<IPasswordValidator<TUser>>();
/// <summary>
/// Used to normalize user names for uniqueness
/// Used to normalize user names and emails for uniqueness
/// </summary>
public IUserNameNormalizer UserNameNormalizer { get; set; }
public ILookupNormalizer KeyNormalizer { get; set; }
/// <summary>
/// Used to generate public API error messages
/// </summary>
public IdentityErrorDescriber ErrorDescriber { get; set; }
/// <summary>
/// Used to log IdentityResult
/// </summary>
public ILogger Logger { get; set; }
public IdentityOptions Options
{
@ -301,7 +316,7 @@ namespace Microsoft.AspNet.Identity
private async Task<IdentityResult> ValidateUserInternal(TUser user, CancellationToken cancellationToken)
{
var errors = new List<string>();
var errors = new List<IdentityError>();
foreach (var v in UserValidators)
{
var result = await v.ValidateAsync(this, user, cancellationToken);
@ -315,7 +330,7 @@ namespace Microsoft.AspNet.Identity
private async Task<IdentityResult> ValidatePasswordInternal(TUser user, string password, CancellationToken cancellationToken)
{
var errors = new List<string>();
var errors = new List<IdentityError>();
foreach (var v in PasswordValidators)
{
var result = await v.ValidateAsync(this, user, password, cancellationToken);
@ -327,6 +342,31 @@ namespace Microsoft.AspNet.Identity
return errors.Count > 0 ? IdentityResult.Failed(errors.ToArray()) : IdentityResult.Success;
}
public virtual Task<string> GenerateConcurrencyStampAsync(TUser user,
CancellationToken token = default(CancellationToken))
{
return Task.FromResult(Guid.NewGuid().ToString());
}
/// <summary>
/// Validate user and update. Called by other UserManager methods
/// </summary>
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private async Task<IdentityResult> UpdateUserAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
var result = await ValidateUserInternal(user, cancellationToken);
if (!result.Succeeded)
{
return result;
}
await UpdateNormalizedUserNameAsync(user, cancellationToken);
await UpdateNormalizedEmailAsync(user, cancellationToken);
return await Store.UpdateAsync(user, cancellationToken);
}
/// <summary>
/// Create a user with no password
/// </summary>
@ -348,8 +388,8 @@ namespace Microsoft.AspNet.Identity
await GetUserLockoutStore().SetLockoutEnabledAsync(user, true, cancellationToken);
}
await UpdateNormalizedUserNameAsync(user, cancellationToken);
await Store.CreateAsync(user, cancellationToken);
return IdentityResult.Success;
await UpdateNormalizedEmailAsync(user, cancellationToken);
return await LogResultAsync(await Store.CreateAsync(user, cancellationToken), user);
}
/// <summary>
@ -366,14 +406,7 @@ namespace Microsoft.AspNet.Identity
{
throw new ArgumentNullException("user");
}
var result = await ValidateUserInternal(user, cancellationToken);
if (!result.Succeeded)
{
return result;
}
await UpdateNormalizedUserNameAsync(user, cancellationToken);
await Store.UpdateAsync(user, cancellationToken);
return IdentityResult.Success;
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
/// <summary>
@ -390,8 +423,7 @@ namespace Microsoft.AspNet.Identity
{
throw new ArgumentNullException("user");
}
await Store.DeleteAsync(user, cancellationToken);
return IdentityResult.Success;
return await LogResultAsync(await Store.DeleteAsync(user, cancellationToken), user);
}
/// <summary>
@ -421,7 +453,7 @@ namespace Microsoft.AspNet.Identity
{
throw new ArgumentNullException("userName");
}
userName = NormalizeUserName(userName);
userName = NormalizeKey(userName);
return Store.FindByNameAsync(userName, cancellationToken);
}
@ -456,7 +488,7 @@ namespace Microsoft.AspNet.Identity
{
throw new ArgumentNullException("password");
}
var result = await UpdatePasswordInternal(passwordStore, user, password, cancellationToken);
var result = await UpdatePasswordHash(passwordStore, user, password, cancellationToken);
if (!result.Succeeded)
{
return result;
@ -465,13 +497,13 @@ namespace Microsoft.AspNet.Identity
}
/// <summary>
/// Normalize a user name for uniqueness comparisons
/// Normalize a key (user name, email) for uniqueness comparisons
/// </summary>
/// <param name="userName"></param>
/// <returns></returns>
public virtual string NormalizeUserName(string userName)
public virtual string NormalizeKey(string key)
{
return (UserNameNormalizer == null) ? userName : UserNameNormalizer.Normalize(userName);
return (KeyNormalizer == null) ? key : KeyNormalizer.Normalize(key);
}
/// <summary>
@ -483,8 +515,8 @@ namespace Microsoft.AspNet.Identity
public virtual async Task UpdateNormalizedUserNameAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
var userName = await GetUserNameAsync(user, cancellationToken);
await Store.SetNormalizedUserNameAsync(user, NormalizeUserName(userName), cancellationToken);
var normalizedName = NormalizeKey(await GetUserNameAsync(user, cancellationToken));
await Store.SetNormalizedUserNameAsync(user, normalizedName, cancellationToken);
}
/// <summary>
@ -520,7 +552,7 @@ namespace Microsoft.AspNet.Identity
throw new ArgumentNullException("user");
}
await UpdateUserName(user, userName, cancellationToken);
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
private async Task UpdateUserName(TUser user, string userName, CancellationToken cancellationToken)
@ -558,7 +590,14 @@ namespace Microsoft.AspNet.Identity
{
return false;
}
return await VerifyPasswordAsync(passwordStore, user, password, cancellationToken);
var result = await VerifyPasswordAsync(passwordStore, user, password, cancellationToken);
if (result == PasswordVerificationResult.SuccessRehashNeeded)
{
await UpdatePasswordHash(passwordStore, user, password, cancellationToken, validatePassword: false);
await UpdateUserAsync(user, cancellationToken);
}
return await LogResultAsync(result != PasswordVerificationResult.Failed, user);
}
/// <summary>
@ -576,7 +615,7 @@ namespace Microsoft.AspNet.Identity
{
throw new ArgumentNullException("user");
}
return await passwordStore.HasPasswordAsync(user, cancellationToken);
return await LogResultAsync(await passwordStore.HasPasswordAsync(user, cancellationToken), user);
}
/// <summary>
@ -598,14 +637,14 @@ namespace Microsoft.AspNet.Identity
var hash = await passwordStore.GetPasswordHashAsync(user, cancellationToken);
if (hash != null)
{
return new IdentityResult(Resources.UserAlreadyHasPassword);
return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.UserAlreadyHasPassword()), user);
}
var result = await UpdatePasswordInternal(passwordStore, user, password, cancellationToken);
var result = await UpdatePasswordHash(passwordStore, user, password, cancellationToken);
if (!result.Succeeded)
{
return result;
return await LogResultAsync(result, user);
}
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
/// <summary>
@ -625,16 +664,16 @@ namespace Microsoft.AspNet.Identity
{
throw new ArgumentNullException("user");
}
if (await VerifyPasswordAsync(passwordStore, user, currentPassword, cancellationToken))
if (await VerifyPasswordAsync(passwordStore, user, currentPassword, cancellationToken) != PasswordVerificationResult.Failed)
{
var result = await UpdatePasswordInternal(passwordStore, user, newPassword, cancellationToken);
var result = await UpdatePasswordHash(passwordStore, user, newPassword, cancellationToken);
if (!result.Succeeded)
{
return result;
return await LogResultAsync(result, user);
}
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
return IdentityResult.Failed(Resources.PasswordMismatch);
return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.PasswordMismatch()), user);
}
/// <summary>
@ -652,21 +691,24 @@ namespace Microsoft.AspNet.Identity
{
throw new ArgumentNullException("user");
}
await passwordStore.SetPasswordHashAsync(user, null, cancellationToken);
await UpdateSecurityStampInternal(user, cancellationToken);
return await UpdateAsync(user, cancellationToken);
await UpdatePasswordHash(passwordStore, user, null, cancellationToken, validatePassword: false);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
internal async Task<IdentityResult> UpdatePasswordInternal(IUserPasswordStore<TUser> passwordStore,
TUser user, string newPassword, CancellationToken cancellationToken)
internal async Task<IdentityResult> UpdatePasswordHash(IUserPasswordStore<TUser> passwordStore,
TUser user, string newPassword, CancellationToken cancellationToken, bool validatePassword = true)
{
var validate = await ValidatePasswordInternal(user, newPassword, cancellationToken);
if (!validate.Succeeded)
if (validatePassword)
{
return validate;
var validate = await ValidatePasswordInternal(user, newPassword, cancellationToken);
if (!validate.Succeeded)
{
return validate;
}
}
var hash = newPassword != null ? PasswordHasher.HashPassword(user, newPassword) : null;
await
passwordStore.SetPasswordHashAsync(user, PasswordHasher.HashPassword(user, newPassword), cancellationToken);
passwordStore.SetPasswordHashAsync(user, hash, cancellationToken);
await UpdateSecurityStampInternal(user, cancellationToken);
return IdentityResult.Success;
}
@ -679,11 +721,11 @@ namespace Microsoft.AspNet.Identity
/// <param name="password"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected virtual async Task<bool> VerifyPasswordAsync(IUserPasswordStore<TUser> store, TUser user,
protected virtual async Task<PasswordVerificationResult> VerifyPasswordAsync(IUserPasswordStore<TUser> store, TUser user,
string password, CancellationToken cancellationToken = default(CancellationToken))
{
var hash = await store.GetPasswordHashAsync(user, cancellationToken);
return PasswordHasher.VerifyHashedPassword(user, hash, password) != PasswordVerificationResult.Failed;
return PasswordHasher.VerifyHashedPassword(user, hash, password);
}
// IUserSecurityStampStore methods
@ -731,7 +773,7 @@ namespace Microsoft.AspNet.Identity
throw new ArgumentNullException("user");
}
await UpdateSecurityStampInternal(user, cancellationToken);
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
/// <summary>
@ -744,7 +786,10 @@ namespace Microsoft.AspNet.Identity
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
return await GenerateUserTokenAsync(user, Options.PasswordResetTokenProvider, "ResetPassword", cancellationToken);
var token = await GenerateUserTokenAsync(user, Options.PasswordResetTokenProvider, "ResetPassword", cancellationToken);
await LogResultAsync(IdentityResult.Success, user);
return token;
}
/// <summary>
@ -766,15 +811,15 @@ namespace Microsoft.AspNet.Identity
// Make sure the token is valid and the stamp matches
if (!await VerifyUserTokenAsync(user, Options.PasswordResetTokenProvider, "ResetPassword", token, cancellationToken))
{
return IdentityResult.Failed(Resources.InvalidToken);
return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.InvalidToken()), user);
}
var passwordStore = GetPasswordStore();
var result = await UpdatePasswordInternal(passwordStore, user, newPassword, cancellationToken);
var result = await UpdatePasswordHash(passwordStore, user, newPassword, cancellationToken);
if (!result.Succeeded)
{
return result;
return await LogResultAsync(result, user);
}
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
// Update the security stamp if the store supports it
@ -851,7 +896,7 @@ namespace Microsoft.AspNet.Identity
}
await loginStore.RemoveLoginAsync(user, loginProvider, providerKey, cancellationToken);
await UpdateSecurityStampInternal(user, cancellationToken);
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
/// <summary>
@ -877,10 +922,10 @@ namespace Microsoft.AspNet.Identity
var existingUser = await FindByLoginAsync(login.LoginProvider, login.ProviderKey, cancellationToken);
if (existingUser != null)
{
return IdentityResult.Failed(Resources.ExternalLoginExists);
return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.LoginAlreadyAssociated()), user);
}
await loginStore.AddLoginAsync(user, login, cancellationToken);
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
/// <summary>
@ -956,7 +1001,7 @@ namespace Microsoft.AspNet.Identity
throw new ArgumentNullException("user");
}
await claimStore.AddClaimsAsync(user, claims, cancellationToken);
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
/// <summary>
@ -985,7 +1030,7 @@ namespace Microsoft.AspNet.Identity
throw new ArgumentNullException("user");
}
await claimStore.ReplaceClaimAsync(user, claim, newClaim, cancellationToken);
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
/// <summary>
@ -1032,7 +1077,7 @@ namespace Microsoft.AspNet.Identity
throw new ArgumentNullException("claims");
}
await claimStore.RemoveClaimsAsync(user, claims, cancellationToken);
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
/// <summary>
@ -1082,10 +1127,10 @@ namespace Microsoft.AspNet.Identity
var userRoles = await userRoleStore.GetRolesAsync(user, cancellationToken);
if (userRoles.Contains(role))
{
return new IdentityResult(Resources.UserAlreadyInRole);
return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.UserAlreadyInRole(role)), user);
}
await userRoleStore.AddToRoleAsync(user, role, cancellationToken);
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
/// <summary>
@ -1113,11 +1158,11 @@ namespace Microsoft.AspNet.Identity
{
if (userRoles.Contains(role))
{
return new IdentityResult(Resources.UserAlreadyInRole);
return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.UserAlreadyInRole(role)), user);
}
await userRoleStore.AddToRoleAsync(user, role, cancellationToken);
}
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
/// <summary>
@ -1138,10 +1183,10 @@ namespace Microsoft.AspNet.Identity
}
if (!await userRoleStore.IsInRoleAsync(user, role, cancellationToken))
{
return new IdentityResult(Resources.UserNotInRole);
return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.UserNotInRole(role)), user);
}
await userRoleStore.RemoveFromRoleAsync(user, role, cancellationToken);
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
/// <summary>
@ -1168,11 +1213,11 @@ namespace Microsoft.AspNet.Identity
{
if (!await userRoleStore.IsInRoleAsync(user, role, cancellationToken))
{
return new IdentityResult(Resources.UserNotInRole);
return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.UserNotInRole(role)), user);
}
await userRoleStore.RemoveFromRoleAsync(user, role, cancellationToken);
}
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
/// <summary>
@ -1213,10 +1258,10 @@ namespace Microsoft.AspNet.Identity
}
// IUserEmailStore methods
internal IUserEmailStore<TUser> GetEmailStore()
internal IUserEmailStore<TUser> GetEmailStore(bool throwOnFail = true)
{
var cast = Store as IUserEmailStore<TUser>;
if (cast == null)
if (throwOnFail && cast == null)
{
throw new NotSupportedException(Resources.StoreNotIUserEmailStore);
}
@ -1261,7 +1306,7 @@ namespace Microsoft.AspNet.Identity
await store.SetEmailAsync(user, email, cancellationToken);
await store.SetEmailConfirmedAsync(user, false, cancellationToken);
await UpdateSecurityStampInternal(user, cancellationToken);
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
/// <summary>
@ -1279,20 +1324,41 @@ namespace Microsoft.AspNet.Identity
{
throw new ArgumentNullException("email");
}
return store.FindByEmailAsync(email, cancellationToken);
return store.FindByEmailAsync(NormalizeKey(email), cancellationToken);
}
/// <summary>
/// Update the user's normalized email
/// </summary>
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task UpdateNormalizedEmailAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
var store = GetEmailStore(throwOnFail: false);
if (store != null)
{
var email = await GetEmailAsync(user, cancellationToken);
await store.SetNormalizedEmailAsync(user, NormalizeKey(email), cancellationToken);
}
}
/// <summary>
/// Get the confirmation token for the user
/// </summary>
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<string> GenerateEmailConfirmationTokenAsync(TUser user,
public async virtual Task<string> GenerateEmailConfirmationTokenAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
return GenerateUserTokenAsync(user, Options.EmailConfirmationTokenProvider, "Confirmation", cancellationToken);
var token = await GenerateUserTokenAsync(user, Options.EmailConfirmationTokenProvider, "Confirmation", cancellationToken);
await LogResultAsync(IdentityResult.Success, user);
return token;
}
/// <summary>
@ -1313,10 +1379,10 @@ namespace Microsoft.AspNet.Identity
}
if (!await VerifyUserTokenAsync(user, Options.EmailConfirmationTokenProvider, "Confirmation", token, cancellationToken))
{
return IdentityResult.Failed(Resources.InvalidToken);
return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.InvalidToken()), user);
}
await store.SetEmailConfirmedAsync(user, true, cancellationToken);
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
/// <summary>
@ -1352,7 +1418,10 @@ namespace Microsoft.AspNet.Identity
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
return await GenerateUserTokenAsync(user, Options.ChangeEmailTokenProvider, GetChangeEmailPurpose(newEmail), cancellationToken);
var token = await GenerateUserTokenAsync(user, Options.ChangeEmailTokenProvider, GetChangeEmailPurpose(newEmail), cancellationToken);
await LogResultAsync(IdentityResult.Success, user);
return token;
}
/// <summary>
@ -1374,13 +1443,13 @@ namespace Microsoft.AspNet.Identity
// Make sure the token is valid and the stamp matches
if (!await VerifyUserTokenAsync(user, Options.ChangeEmailTokenProvider, GetChangeEmailPurpose(newEmail), token, cancellationToken))
{
return IdentityResult.Failed(Resources.InvalidToken);
return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.InvalidToken()), user);
}
var store = GetEmailStore();
await store.SetEmailAsync(user, newEmail, cancellationToken);
await store.SetEmailConfirmedAsync(user, true, cancellationToken);
await UpdateSecurityStampInternal(user, cancellationToken);
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
// IUserPhoneNumberStore methods
@ -1431,7 +1500,7 @@ namespace Microsoft.AspNet.Identity
await store.SetPhoneNumberAsync(user, phoneNumber, cancellationToken);
await store.SetPhoneNumberConfirmedAsync(user, false, cancellationToken);
await UpdateSecurityStampInternal(user, cancellationToken);
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
/// <summary>
@ -1453,12 +1522,12 @@ namespace Microsoft.AspNet.Identity
}
if (!await VerifyChangePhoneNumberTokenAsync(user, token, phoneNumber, cancellationToken))
{
return IdentityResult.Failed(Resources.InvalidToken);
return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.InvalidToken()), user);
}
await store.SetPhoneNumberAsync(user, phoneNumber, cancellationToken);
await store.SetPhoneNumberConfirmedAsync(user, true, cancellationToken);
await UpdateSecurityStampInternal(user, cancellationToken);
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
/// <summary>
@ -1497,9 +1566,12 @@ namespace Microsoft.AspNet.Identity
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
return Rfc6238AuthenticationService.GenerateCode(
var token = Rfc6238AuthenticationService.GenerateCode(
await CreateSecurityTokenAsync(user, cancellationToken), phoneNumber)
.ToString(CultureInfo.InvariantCulture);
await LogResultAsync(IdentityResult.Success, user);
return token;
}
/// <summary>
@ -1517,8 +1589,13 @@ namespace Microsoft.AspNet.Identity
int code;
if (securityToken != null && Int32.TryParse(token, out code))
{
return Rfc6238AuthenticationService.ValidateCode(securityToken, code, phoneNumber);
if (Rfc6238AuthenticationService.ValidateCode(securityToken, code, phoneNumber))
{
await LogResultAsync(IdentityResult.Success, user);
return true;
}
}
await LogResultAsync(IdentityResult.Failed(ErrorDescriber.InvalidToken()), user);
return false;
}
@ -1547,7 +1624,18 @@ namespace Microsoft.AspNet.Identity
throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, Resources.NoTokenProvider, tokenProvider));
}
// Make sure the token is valid
return await _tokenProviders[tokenProvider].ValidateAsync(purpose, token, this, user, cancellationToken);
var result = await _tokenProviders[tokenProvider].ValidateAsync(purpose, token, this, user, cancellationToken);
if (result)
{
await LogResultAsync(IdentityResult.Success, user);
}
else
{
await LogResultAsync(IdentityResult.Failed(ErrorDescriber.InvalidToken()), user);
}
return result;
}
/// <summary>
@ -1573,7 +1661,11 @@ namespace Microsoft.AspNet.Identity
{
throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, Resources.NoTokenProvider, tokenProvider));
}
return await _tokenProviders[tokenProvider].GenerateAsync(purpose, this, user, cancellationToken);
var token = await _tokenProviders[tokenProvider].GenerateAsync(purpose, this, user, cancellationToken);
await LogResultAsync(IdentityResult.Success, user);
return token;
}
/// <summary>
@ -1651,7 +1743,18 @@ namespace Microsoft.AspNet.Identity
Resources.NoTokenProvider, tokenProvider));
}
// Make sure the token is valid
return await _tokenProviders[tokenProvider].ValidateAsync("TwoFactor", token, this, user, cancellationToken);
var result = await _tokenProviders[tokenProvider].ValidateAsync("TwoFactor", token, this, user, cancellationToken);
if (result)
{
await LogResultAsync(IdentityResult.Success, user);
}
else
{
await LogResultAsync(IdentityResult.Failed(ErrorDescriber.InvalidToken()), user);
}
return result;
}
/// <summary>
@ -1674,7 +1777,10 @@ namespace Microsoft.AspNet.Identity
throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture,
Resources.NoTokenProvider, tokenProvider));
}
return await _tokenProviders[tokenProvider].GenerateAsync("TwoFactor", this, user, cancellationToken);
var token = await _tokenProviders[tokenProvider].GenerateAsync("TwoFactor", this, user, cancellationToken);
await LogResultAsync(IdentityResult.Success, user);
return token;
}
/// <summary>
@ -1703,7 +1809,7 @@ namespace Microsoft.AspNet.Identity
Resources.NoTokenProvider, tokenProvider));
}
await _tokenProviders[tokenProvider].NotifyAsync(token, this, user, cancellationToken);
return IdentityResult.Success;
return await LogResultAsync(IdentityResult.Success, user);
}
// IUserFactorStore methods
@ -1753,7 +1859,7 @@ namespace Microsoft.AspNet.Identity
}
await store.SetTwoFactorEnabledAsync(user, enabled, cancellationToken);
await UpdateSecurityStampInternal(user, cancellationToken);
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
// Messaging methods
@ -1765,7 +1871,7 @@ namespace Microsoft.AspNet.Identity
/// <param name="message"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<IdentityResult> SendMessageAsync(string messageProvider, IdentityMessage message,
public virtual async Task<IdentityResult> SendMessageAsync(string messageProvider, IdentityMessage message,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
@ -1833,7 +1939,7 @@ namespace Microsoft.AspNet.Identity
throw new ArgumentNullException("user");
}
await store.SetLockoutEnabledAsync(user, enabled, cancellationToken);
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
/// <summary>
@ -1890,10 +1996,10 @@ namespace Microsoft.AspNet.Identity
}
if (!await store.GetLockoutEnabledAsync(user, cancellationToken).ConfigureAwait((false)))
{
return IdentityResult.Failed(Resources.LockoutNotEnabled);
return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.UserLockoutNotEnabled()), user);
}
await store.SetLockoutEndDateAsync(user, lockoutEnd, cancellationToken);
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
/// <summary>
@ -1917,12 +2023,12 @@ namespace Microsoft.AspNet.Identity
var count = await store.IncrementAccessFailedCountAsync(user, cancellationToken);
if (count < Options.Lockout.MaxFailedAccessAttempts)
{
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
await store.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.Add(Options.Lockout.DefaultLockoutTimeSpan),
cancellationToken);
await store.ResetAccessFailedCountAsync(user, cancellationToken);
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
/// <summary>
@ -1941,7 +2047,7 @@ namespace Microsoft.AspNet.Identity
throw new ArgumentNullException("user");
}
await store.ResetAccessFailedCountAsync(user, cancellationToken);
return await UpdateAsync(user, cancellationToken);
return await LogResultAsync(await UpdateUserAsync(user, cancellationToken), user);
}
/// <summary>
@ -1962,6 +2068,75 @@ namespace Microsoft.AspNet.Identity
return await store.GetAccessFailedCountAsync(user, cancellationToken);
}
public virtual Task<IList<TUser>> GetUsersForClaimAsync(Claim claim,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var store = GetClaimStore();
if (claim == null)
{
throw new ArgumentNullException("claim");
}
return store.GetUsersForClaimAsync(claim, cancellationToken);
}
/// <summary>
/// Get all the users in a role
/// </summary>
/// <param name="role"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<IList<TUser>> GetUsersInRoleAsync(string roleName,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var store = GetUserRoleStore();
if (roleName == null)
{
throw new ArgumentNullException("role");
}
return store.GetUsersInRoleAsync(roleName, cancellationToken);
}
/// <summary>
/// Logs the current Identity Result and returns result object
/// </summary>
/// <param name="result"></param>
/// <param name="user"></param>
/// <param name="methodName"></param>
/// <returns></returns>
protected async Task<IdentityResult> LogResultAsync(IdentityResult result,
TUser user, [System.Runtime.CompilerServices.CallerMemberName] string methodName = "")
{
result.Log(Logger, Resources.FormatLoggingResultMessage(methodName, await GetUserIdAsync(user)));
return result;
}
/// <summary>
/// Logs result of operation being true/false
/// </summary>
/// <param name="result"></param>
/// <param name="user"></param>
/// <param name="methodName"></param>
/// <returns>result</returns>
protected async Task<bool> LogResultAsync(bool result,
TUser user, [System.Runtime.CompilerServices.CallerMemberName] string methodName = "")
{
var baseMessage = Resources.FormatLoggingResultMessage(methodName, await GetUserIdAsync(user));
if (result)
{
Logger.WriteInformation(string.Format("{0} : {1}", baseMessage, result.ToString()));
}
else
{
Logger.WriteWarning(string.Format("{0} : {1}", baseMessage, result.ToString()));
}
return result;
}
private void ThrowIfDisposed()
{
if (_disposed)

View File

@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
#if ASPNET50
using System.Net.Mail;
#endif
@ -19,6 +18,13 @@ namespace Microsoft.AspNet.Identity
/// <typeparam name="TUser"></typeparam>
public class UserValidator<TUser> : IUserValidator<TUser> where TUser : class
{
public UserValidator(IdentityErrorDescriber errors = null)
{
Describer = errors ?? new IdentityErrorDescriber();
}
public IdentityErrorDescriber Describer { get; private set; }
/// <summary>
/// Validates a user before saving
/// </summary>
@ -37,7 +43,7 @@ namespace Microsoft.AspNet.Identity
{
throw new ArgumentNullException("user");
}
var errors = new List<string>();
var errors = new List<IdentityError>();
await ValidateUserName(manager, user, errors);
if (manager.Options.User.RequireUniqueEmail)
{
@ -46,16 +52,16 @@ namespace Microsoft.AspNet.Identity
return errors.Count > 0 ? IdentityResult.Failed(errors.ToArray()) : IdentityResult.Success;
}
private async Task ValidateUserName(UserManager<TUser> manager, TUser user, ICollection<string> errors)
private async Task ValidateUserName(UserManager<TUser> manager, TUser user, ICollection<IdentityError> errors)
{
var userName = await manager.GetUserNameAsync(user);
if (string.IsNullOrWhiteSpace(userName))
{
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.PropertyTooShort, "UserName"));
errors.Add(Describer.InvalidUserName(userName));
}
else if (manager.Options.User.UserNameValidationRegex != null && !Regex.IsMatch(userName, manager.Options.User.UserNameValidationRegex))
{
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.InvalidUserName, userName));
errors.Add(Describer.InvalidUserName(userName));
}
else
{
@ -63,18 +69,18 @@ namespace Microsoft.AspNet.Identity
if (owner != null &&
!string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user)))
{
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.DuplicateName, userName));
errors.Add(Describer.DuplicateUserName(userName));
}
}
}
// make sure email is not empty, valid, and unique
private static async Task ValidateEmail(UserManager<TUser> manager, TUser user, List<string> errors)
private async Task ValidateEmail(UserManager<TUser> manager, TUser user, List<IdentityError> errors)
{
var email = await manager.GetEmailAsync(user);
if (string.IsNullOrWhiteSpace(email))
{
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.PropertyTooShort, "Email"));
errors.Add(Describer.InvalidEmail(email));
return;
}
#if ASPNET50
@ -84,7 +90,7 @@ namespace Microsoft.AspNet.Identity
}
catch (FormatException)
{
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.InvalidEmail, email));
errors.Add(Describer.InvalidEmail(email));
return;
}
#endif
@ -92,7 +98,7 @@ namespace Microsoft.AspNet.Identity
if (owner != null &&
!string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user)))
{
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.DuplicateEmail, email));
errors.Add(Describer.DuplicateEmail(email));
}
}
}

View File

@ -7,7 +7,8 @@
"Microsoft.AspNet.Security.DataProtection": "1.0.0-*",
"Microsoft.Framework.ConfigurationModel": "1.0.0-*",
"Microsoft.Framework.DependencyInjection" : "1.0.0-*",
"Microsoft.Framework.OptionsModel": "1.0.0-*"
"Microsoft.Framework.OptionsModel": "1.0.0-*",
"Microsoft.Framework.Logging": "1.0.0-*"
},
"frameworks": {
"aspnet50": {},

View File

@ -1,31 +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.Identity.EntityFramework;
using Microsoft.AspNet.Identity.EntityFramework.InMemory.Test;
using Microsoft.Data.Entity;
using Microsoft.Framework.DependencyInjection;
namespace Microsoft.AspNet.Identity
{
public static class EntityInMemoryTestServiceCollectionExtensions
{
public static IdentityBuilder AddIdentityInMemory(this ServiceCollection services, InMemoryContext context)
{
return services.AddIdentityInMemory<IdentityUser, IdentityRole, InMemoryContext>(context);
}
public static IdentityBuilder AddIdentityInMemory<TUser, TRole, TDbContext>(this ServiceCollection services, TDbContext context)
where TUser : IdentityUser
where TRole : IdentityRole
where TDbContext : DbContext
{
var builder = services.AddIdentity<TUser, TRole>();
builder.AddDefaultTokenProviders();
services.AddInstance<IUserStore<TUser>>(new InMemoryUserStore<TUser, TDbContext>(context));
var store = new RoleStore<TRole, TDbContext>(context);
services.AddInstance<IRoleStore<TRole>>(store);
return builder;
}
}
}

View File

@ -3,41 +3,25 @@
using System;
using Microsoft.Data.Entity;
using Microsoft.Data.Entity.Metadata;
namespace Microsoft.AspNet.Identity.EntityFramework.InMemory.Test
{
public class InMemoryContext :
InMemoryContext<IdentityUser, IdentityRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
InMemoryContext<IdentityUser, IdentityRole, string>
{
public InMemoryContext() { }
public InMemoryContext(IServiceProvider serviceProvider) : base(serviceProvider) { }
}
public class InMemoryContext<TUser> :
InMemoryContext<TUser, IdentityRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
InMemoryContext<TUser, IdentityRole, string>
where TUser : IdentityUser
{
public InMemoryContext() { }
public InMemoryContext(IServiceProvider serviceProvider) : base(serviceProvider) { }
}
public class InMemoryContext<TUser, TRole, TKey, TUserLogin, TUserRole, TUserClaim> : DbContext
public class InMemoryContext<TUser, TRole, TKey> : IdentityDbContext<TUser,TRole,TKey>
where TUser : IdentityUser<TKey>
where TRole : IdentityRole<TKey>
where TUserLogin : IdentityUserLogin<TKey>
where TUserRole : IdentityUserRole<TKey>
where TUserClaim : IdentityUserClaim<TKey>
where TKey : IEquatable<TKey>
{
public DbSet<TUser> Users { get; set; }
public DbSet<TRole> Roles { get; set; }
public DbSet<IdentityRoleClaim> RoleClaims { get; set; }
public InMemoryContext(IServiceProvider serviceProvider)
: base(serviceProvider) { }
public InMemoryContext() { }
protected override void OnConfiguring(DbContextOptions builder)
@ -45,50 +29,5 @@ namespace Microsoft.AspNet.Identity.EntityFramework.InMemory.Test
// Want fresh in memory store for tests always for now
builder.UseInMemoryStore(persist: false);
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<TUser>(b =>
{
b.Key(u => u.Id);
b.Property(u => u.UserName);
b.ForRelational().Table("AspNetUsers");
});
builder.Entity<TRole>(b =>
{
b.Key(r => r.Id);
b.ForRelational().Table("AspNetRoles");
});
builder.Entity<TUserRole>(b =>
{
b.Key(r => new { r.UserId, r.RoleId });
b.ForeignKey<TUser>(f => f.UserId);
b.ForeignKey<TRole>(f => f.RoleId);
b.ForRelational().Table("AspNetUserRoles");
});
builder.Entity<TUserLogin>(b =>
{
b.Key(l => new { l.LoginProvider, l.ProviderKey, l.UserId });
b.ForeignKey<TUser>(f => f.UserId);
b.ForRelational().Table("AspNetUserLogins");
});
builder.Entity<TUserClaim>(b =>
{
b.Key(c => c.Id);
b.ForeignKey<TUser>(f => f.UserId);
b.ForRelational().Table("AspNetUserClaims");
});
builder.Entity<IdentityRoleClaim<TKey>>(b =>
{
b.Key(c => c.Id);
b.ForeignKey<TRole>(f => f.RoleId);
b.ForRelational().Table("AspNetRoleClaims");
});
}
}
}

View File

@ -1,6 +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 Microsoft.AspNet.Identity.Test;
using Microsoft.Framework.DependencyInjection;
@ -15,7 +16,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.InMemory.Test
protected override void AddUserStore(IServiceCollection services, object context = null)
{
services.AddInstance<IUserStore<IdentityUser>>(new InMemoryUserStore<IdentityUser, InMemoryContext>((InMemoryContext)context));
services.AddInstance<IUserStore<IdentityUser>>(new UserStore<IdentityUser>((InMemoryContext)context));
}
protected override void AddRoleStore(IServiceCollection services, object context = null)

View File

@ -1,929 +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.EntityFramework.InMemory.Test
{
public class InMemoryUserStore : InMemoryUserStore<IdentityUser, InMemoryContext>
{
public InMemoryUserStore(InMemoryContext context) : base(context) { }
}
public class InMemoryUserStore<TUser> : InMemoryUserStore<TUser, InMemoryContext>
where TUser : IdentityUser
{
public InMemoryUserStore(InMemoryContext context) : base(context) { }
}
public class InMemoryUserStore<TUser, TContext> : InMemoryUserStore<TUser, IdentityRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim, TContext>
where TUser:IdentityUser
where TContext : DbContext
{
public InMemoryUserStore(TContext context) : base(context) { }
}
public class InMemoryUserStore<TUser, TRole, TKey, TUserLogin, TUserRole, TUserClaim, TContext> :
IUserLoginStore<TUser>,
IUserClaimStore<TUser>,
IUserRoleStore<TUser>,
IUserPasswordStore<TUser>,
IUserSecurityStampStore<TUser>,
IQueryableUserStore<TUser>,
IUserEmailStore<TUser>,
IUserPhoneNumberStore<TUser>,
IUserTwoFactorStore<TUser>,
IUserLockoutStore<TUser>
where TKey : IEquatable<TKey>
where TUser : IdentityUser<TKey>
where TRole : IdentityRole<TKey>
where TUserLogin : IdentityUserLogin<TKey>, new()
where TUserRole : IdentityUserRole<TKey>, new()
where TUserClaim : IdentityUserClaim<TKey>, 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; }
/// <summary>
/// If true will call SaveChanges after CreateAsync/UpdateAsync/DeleteAsync
/// </summary>
public bool AutoSaveChanges { get; set; }
private Task SaveChanges(CancellationToken cancellationToken)
{
return AutoSaveChanges ? Context.SaveChangesAsync(cancellationToken) : Task.FromResult(0);
}
protected virtual Task<TUser> GetUserAggregate(Expression<Func<TUser, bool>> 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<string> GetUserIdAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
return Task.FromResult(Convert.ToString(user.Id, CultureInfo.InvariantCulture));
}
public Task<string> GetUserNameAsync(TUser user, CancellationToken cancellationToken = default(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 = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
user.UserName = userName;
return Task.FromResult(0);
}
public Task<string> GetNormalizedUserNameAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
return Task.FromResult(user.NormalizedUserName);
}
public Task SetNormalizedUserNameAsync(TUser user, string userName, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
user.NormalizedUserName = 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");
}
Context.Update(user);
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.Remove(user);
await SaveChanges(cancellationToken);
}
public virtual TKey ConvertUserId(string userId)
{
return (TKey)Convert.ChangeType(userId, typeof(TKey));
}
/// <summary>
/// Find a user by id
/// </summary>
/// <param name="userId"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<TUser> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
var id = ConvertUserId(userId);
return GetUserAggregate(u => u.Id.Equals(id), cancellationToken);
}
/// <summary>
/// Find a user by name
/// </summary>
/// <param name="userName"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<TUser> FindByNameAsync(string userName, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
return GetUserAggregate(u => u.UserName.ToUpper() == userName.ToUpper(), cancellationToken);
}
public IQueryable<TUser> Users
{
get { return Context.Set<TUser>(); }
}
public async virtual Task AddLoginAsync(TUser user, UserLoginInfo login,
CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
var l = new TUserLogin
{
UserId = user.Id,
ProviderKey = login.ProviderKey,
LoginProvider = login.LoginProvider,
ProviderDisplayName = login.ProviderDisplayName
};
await Context.Set<TUserLogin>().AddAsync(l, cancellationToken);
user.Logins.Add(l);
}
public virtual Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey,
CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
var entry = user.Logins.SingleOrDefault(l => l.LoginProvider == loginProvider && l.ProviderKey == providerKey);
if (entry != null)
{
user.Logins.Remove(entry);
Context.Set<IdentityUserLogin<TKey>>().Remove(entry);
}
return Task.FromResult(0);
}
public virtual Task<IList<UserLoginInfo>> GetLoginsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
IList<UserLoginInfo> result = user.Logins.Select(
l => new UserLoginInfo(l.LoginProvider, l.ProviderKey, l.ProviderDisplayName))
.ToList();
return Task.FromResult(result);
}
public async virtual Task<TUser> FindByLoginAsync(string loginProvider, string providerKey, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
var userLogin = await Context.Set<TUserLogin>()
.FirstOrDefaultAsync(l => l.LoginProvider == loginProvider && l.ProviderKey == providerKey);
if (userLogin != null)
{
return await GetUserAggregate(u => u.Id.Equals(userLogin.UserId), cancellationToken);
}
return null;
}
/// <summary>
/// Set the password hash for a user
/// </summary>
/// <param name="user"></param>
/// <param name="passwordHash"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
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);
}
/// <summary>
/// Get the password hash for a user
/// </summary>
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<string> GetPasswordHashAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
return Task.FromResult(user.PasswordHash);
}
/// <summary>
/// Returns true if the user has a password set
/// </summary>
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<bool> HasPasswordAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult(user.PasswordHash != null);
}
/// <summary>
/// Return the claims for a user
/// </summary>
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<IList<Claim>> GetClaimsAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
IList<Claim> result = user.Claims.Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToList();
return Task.FromResult(result);
}
/// <summary>
/// Add claims to a user
/// </summary>
/// <param name="user"></param>
/// <param name="claims"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task AddClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (claims == null)
{
throw new ArgumentNullException("claims");
}
foreach (var claim in claims)
{
user.Claims.Add(new TUserClaim { UserId = user.Id, ClaimType = claim.Type, ClaimValue = claim.Value });
}
return Task.FromResult(0);
}
/// <summary>
/// Updates the give claim with the new one.
/// </summary>
/// <param name="user"></param>
/// <param name="claim"></param>
/// <param name="newClaim"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (claim == null)
{
throw new ArgumentNullException("claim");
}
if (newClaim == null)
{
throw new ArgumentNullException("newClaim");
}
var matchedClaim = user.Claims.FirstOrDefault(uc => uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type);
if(matchedClaim != null)
{
matchedClaim.ClaimValue = newClaim.Value;
matchedClaim.ClaimType = newClaim.Type;
}
return Task.FromResult(0);
}
/// <summary>
/// Remove claims from a user
/// </summary>
/// <param name="user"></param>
/// <param name="claims"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task RemoveClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (claims == null)
{
throw new ArgumentNullException("claims");
}
foreach (var claim in claims)
{
var matchingClaims =
user.Claims.Where(uc => uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToList();
foreach (var c in matchingClaims)
{
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);
}
/// <summary>
/// Returns whether the user email is confirmed
/// </summary>
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<bool> GetEmailConfirmedAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
return Task.FromResult(user.EmailConfirmed);
}
/// <summary>
/// Set IsConfirmed on the user
/// </summary>
/// <param name="user"></param>
/// <param name="confirmed"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
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);
}
/// <summary>
/// Set the user email
/// </summary>
/// <param name="user"></param>
/// <param name="email"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
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);
}
/// <summary>
/// Get the user's email
/// </summary>
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<string> GetEmailAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
return Task.FromResult(user.Email);
}
/// <summary>
/// FindByLoginAsync a user by email
/// </summary>
/// <param name="email"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<TUser> 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);
}
/// <summary>
/// Returns the DateTimeOffset that represents the end of a user's lockout, any time in the past should be considered
/// not locked out.
/// </summary>
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<DateTimeOffset?> GetLockoutEndDateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
return Task.FromResult(user.LockoutEnd);
}
/// <summary>
/// Locks a user out until the specified end date (set to a past date, to unlock a user)
/// </summary>
/// <param name="user"></param>
/// <param name="lockoutEnd"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
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;
return Task.FromResult(0);
}
/// <summary>
/// Used to record when an attempt to access the user has failed
/// </summary>
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<int> IncrementAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
user.AccessFailedCount++;
return Task.FromResult(user.AccessFailedCount);
}
/// <summary>
/// Used to reset the account access count, typically after the account is successfully accessed
/// </summary>
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
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);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<int> GetAccessFailedCountAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
return Task.FromResult(user.AccessFailedCount);
}
/// <summary>
/// Returns whether the user can be locked out.
/// </summary>
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<bool> GetLockoutEnabledAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
return Task.FromResult(user.LockoutEnabled);
}
/// <summary>
/// Sets whether the user can be locked out.
/// </summary>
/// <param name="user"></param>
/// <param name="enabled"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
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);
}
/// <summary>
/// Set the user's phone number
/// </summary>
/// <param name="user"></param>
/// <param name="phoneNumber"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
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);
}
/// <summary>
/// Get a user's phone number
/// </summary>
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<string> GetPhoneNumberAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
return Task.FromResult(user.PhoneNumber);
}
/// <summary>
/// Returns whether the user phoneNumber is confirmed
/// </summary>
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<bool> GetPhoneNumberConfirmedAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
return Task.FromResult(user.PhoneNumberConfirmed);
}
/// <summary>
/// Set PhoneNumberConfirmed on the user
/// </summary>
/// <param name="user"></param>
/// <param name="confirmed"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
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);
}
/// <summary>
/// Add a user to a role
/// </summary>
/// <param name="user"></param>
/// <param name="roleName"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
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<TRole>().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);
}
/// <summary>
/// Remove a user from a role
/// </summary>
/// <param name="user"></param>
/// <param name="roleName"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
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<TRole>().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);
}
/// <summary>
/// Get the names of the roles a user is a member of
/// </summary>
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<IList<string>> 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<TRole>()
on userRoles.RoleId equals roles.Id
select roles.Name;
return Task.FromResult<IList<string>>(query.ToList());
}
/// <summary>
/// Returns true if the user is in the named role
/// </summary>
/// <param name="user"></param>
/// <param name="roleName"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<bool> 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<TRole>().Where(r => r.Name.ToUpper() == roleName.ToUpper())
.Where(r => r.Users.Any(ur => ur.UserId.Equals(user.Id)))
.Count() > 0;
return Task.FromResult(any);
}
/// <summary>
/// Set the security stamp for the user
/// </summary>
/// <param name="user"></param>
/// <param name="stamp"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
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);
}
/// <summary>
/// Get the security stamp for a user
/// </summary>
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<string> GetSecurityStampAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
return Task.FromResult(user.SecurityStamp);
}
/// <summary>
/// Set whether two factor authentication is enabled for the user
/// </summary>
/// <param name="user"></param>
/// <param name="enabled"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
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);
}
/// <summary>
/// Gets whether two factor authentication is enabled for the user
/// </summary>
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual Task<bool> 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);
}
}
/// <summary>
/// Dispose the store
/// </summary>
public void Dispose()
{
_disposed = true;
}
}
}

View File

@ -76,23 +76,10 @@ namespace Microsoft.AspNet.Identity.EntityFramework.InMemory.Test
var role = new IdentityRole("UpdateRoleName");
IdentityResultAssert.IsSuccess(await manager.CreateAsync(role));
Assert.Null(await manager.FindByNameAsync("New"));
role.Name = "New";
IdentityResultAssert.IsSuccess(await manager.SetRoleNameAsync(role, "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"));
}
}
}

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.InMemory.Test
services.AddEntityFramework().AddInMemoryStore();
var serviceProvider = services.BuildServiceProvider();
var db = new InMemoryContext(serviceProvider);
var db = new InMemoryContext();
db.Database.EnsureCreated();
return db;

View File

@ -4,14 +4,13 @@
"Microsoft.AspNet.Http": "1.0.0-*",
"Microsoft.AspNet.Identity": "3.0.0-*",
"Microsoft.AspNet.Identity.EntityFramework": "3.0.0-*",
"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-*"
"xunit.runner.kre": "1.0.0-*"
},
"code": "**\\*.cs;..\\Shared\\*.cs",
"frameworks": {
@ -22,6 +21,6 @@
}
},
"commands": {
"test": "Xunit.KRunner"
"test": "xunit.runner.kre"
}
}

View File

@ -7,10 +7,7 @@ using System.Security.Claims;
using System.Threading.Tasks;
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 Microsoft.Framework.Runtime.Infrastructure;
using Xunit;
@ -156,16 +153,90 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test
IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user));
}
[Fact]
public async Task EnsureRoleClaimNavigationProperty()
private async Task LazyLoadTestSetup(TestDbContext db, TUser user)
{
var context = CreateContext();
var roleManager = CreateRoleManager(context);
var r = CreateRole();
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));
var manager = CreateManager(context);
var role = CreateRoleManager(context);
var admin = CreateRole("Admin");
var local = CreateRole("Local");
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, new UserLoginInfo("provider", user.Id.ToString(), "display")));
IdentityResultAssert.IsSuccess(await role.CreateAsync(admin));
IdentityResultAssert.IsSuccess(await role.CreateAsync(local));
IdentityResultAssert.IsSuccess(await manager.AddToRoleAsync(user, admin.Name));
IdentityResultAssert.IsSuccess(await manager.AddToRoleAsync(user, local.Name));
Claim[] userClaims =
{
new Claim("Whatever", "Value"),
new Claim("Whatever2", "Value2")
};
foreach (var c in userClaims)
{
IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c));
}
}
[Fact]
public async Task LoadFromDbFindByIdTest()
{
var db = CreateContext();
var user = CreateTestUser();
await LazyLoadTestSetup(db, user);
db = CreateContext();
var manager = CreateManager(db);
var userById = await manager.FindByIdAsync(user.Id.ToString());
Assert.Equal(2, (await manager.GetClaimsAsync(userById)).Count);
Assert.Equal(1, (await manager.GetLoginsAsync(userById)).Count);
Assert.Equal(2, (await manager.GetRolesAsync(userById)).Count);
}
[Fact]
public async Task LoadFromDbFindByNameTest()
{
var db = CreateContext();
var user = CreateTestUser();
await LazyLoadTestSetup(db, user);
db = CreateContext();
var manager = CreateManager(db);
var userByName = await manager.FindByNameAsync(user.UserName);
Assert.Equal(2, (await manager.GetClaimsAsync(userByName)).Count);
Assert.Equal(1, (await manager.GetLoginsAsync(userByName)).Count);
Assert.Equal(2, (await manager.GetRolesAsync(userByName)).Count);
}
[Fact]
public async Task LoadFromDbFindByLoginTest()
{
var db = CreateContext();
var user = CreateTestUser();
await LazyLoadTestSetup(db, user);
db = CreateContext();
var manager = CreateManager(db);
var userByLogin = await manager.FindByLoginAsync("provider", user.Id.ToString());
Assert.Equal(2, (await manager.GetClaimsAsync(userByLogin)).Count);
Assert.Equal(1, (await manager.GetLoginsAsync(userByLogin)).Count);
Assert.Equal(2, (await manager.GetRolesAsync(userByLogin)).Count);
}
[Fact]
public async Task LoadFromDbFindByEmailTest()
{
var db = CreateContext();
var user = CreateTestUser();
user.Email = "fooz@fizzy.pop";
await LazyLoadTestSetup(db, user);
db = CreateContext();
var manager = CreateManager(db);
var userByEmail = await manager.FindByEmailAsync(user.Email);
Assert.Equal(2, (await manager.GetClaimsAsync(userByEmail)).Count);
Assert.Equal(1, (await manager.GetLoginsAsync(userByEmail)).Count);
Assert.Equal(2, (await manager.GetRolesAsync(userByEmail)).Count);
}
}
}

View File

@ -265,18 +265,6 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test
IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user));
}
[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 AddUserToUnknownRoleFails()
{
@ -287,6 +275,157 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test
async () => await manager.AddToRoleAsync(u, "bogus"));
}
[Fact]
public async Task ConcurrentUpdatesWillFail()
{
var user = CreateTestUser();
using (var db = CreateContext())
{
var manager = CreateManager(db);
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
}
using (var db = CreateContext())
using (var db2 = CreateContext())
{
var manager1 = CreateManager(db);
var manager2 = CreateManager(db2);
var user1 = await manager1.FindByIdAsync(user.Id);
var user2 = await manager2.FindByIdAsync(user.Id);
Assert.NotNull(user1);
Assert.NotNull(user2);
Assert.NotSame(user1, user2);
user1.UserName = Guid.NewGuid().ToString();
user2.UserName = Guid.NewGuid().ToString();
IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(user1));
IdentityResultAssert.IsFailure(await manager2.UpdateAsync(user2), IdentityErrorDescriber.Default.ConcurrencyFailure());
}
}
[Fact]
public async Task ConcurrentUpdatesWillFailWithDetachedUser()
{
var user = CreateTestUser();
using (var db = CreateContext())
{
var manager = CreateManager(db);
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
}
using (var db = CreateContext())
using (var db2 = CreateContext())
{
var manager1 = CreateManager(db);
var manager2 = CreateManager(db2);
var user2 = await manager2.FindByIdAsync(user.Id);
Assert.NotNull(user2);
Assert.NotSame(user, user2);
user.UserName = Guid.NewGuid().ToString();
user2.UserName = Guid.NewGuid().ToString();
IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(user));
IdentityResultAssert.IsFailure(await manager2.UpdateAsync(user2), IdentityErrorDescriber.Default.ConcurrencyFailure());
}
}
[Fact]
public async Task DeleteAModifiedUserWillFail()
{
var user = CreateTestUser();
using (var db = CreateContext())
{
var manager = CreateManager(db);
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
}
using (var db = CreateContext())
using (var db2 = CreateContext())
{
var manager1 = CreateManager(db);
var manager2 = CreateManager(db2);
var user1 = await manager1.FindByIdAsync(user.Id);
var user2 = await manager2.FindByIdAsync(user.Id);
Assert.NotNull(user1);
Assert.NotNull(user2);
Assert.NotSame(user1, user2);
user1.UserName = Guid.NewGuid().ToString();
IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(user1));
IdentityResultAssert.IsFailure(await manager2.DeleteAsync(user2), IdentityErrorDescriber.Default.ConcurrencyFailure());
}
}
[Fact]
public async Task ConcurrentRoleUpdatesWillFail()
{
var role = new IdentityRole(Guid.NewGuid().ToString());
using (var db = CreateContext())
{
var manager = CreateRoleManager(db);
IdentityResultAssert.IsSuccess(await manager.CreateAsync(role));
}
using (var db = CreateContext())
using (var db2 = CreateContext())
{
var manager1 = CreateRoleManager(db);
var manager2 = CreateRoleManager(db2);
var role1 = await manager1.FindByIdAsync(role.Id);
var role2 = await manager2.FindByIdAsync(role.Id);
Assert.NotNull(role1);
Assert.NotNull(role2);
Assert.NotSame(role1, role2);
role1.Name = Guid.NewGuid().ToString();
role2.Name = Guid.NewGuid().ToString();
IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(role1));
IdentityResultAssert.IsFailure(await manager2.UpdateAsync(role2), IdentityErrorDescriber.Default.ConcurrencyFailure());
}
}
[Fact]
public async Task ConcurrentRoleUpdatesWillFailWithDetachedRole()
{
var role = new IdentityRole(Guid.NewGuid().ToString());
using (var db = CreateContext())
{
var manager = CreateRoleManager(db);
IdentityResultAssert.IsSuccess(await manager.CreateAsync(role));
}
using (var db = CreateContext())
using (var db2 = CreateContext())
{
var manager1 = CreateRoleManager(db);
var manager2 = CreateRoleManager(db2);
var role2 = await manager2.FindByIdAsync(role.Id);
Assert.NotNull(role);
Assert.NotNull(role2);
Assert.NotSame(role, role2);
role.Name = Guid.NewGuid().ToString();
role2.Name = Guid.NewGuid().ToString();
IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(role));
IdentityResultAssert.IsFailure(await manager2.UpdateAsync(role2), IdentityErrorDescriber.Default.ConcurrencyFailure());
}
}
[Fact]
public async Task DeleteAModifiedRoleWillFail()
{
var role = new IdentityRole(Guid.NewGuid().ToString());
using (var db = CreateContext())
{
var manager = CreateRoleManager(db);
IdentityResultAssert.IsSuccess(await manager.CreateAsync(role));
}
using (var db = CreateContext())
using (var db2 = CreateContext())
{
var manager1 = CreateRoleManager(db);
var manager2 = CreateRoleManager(db2);
var role1 = await manager1.FindByIdAsync(role.Id);
var role2 = await manager2.FindByIdAsync(role.Id);
Assert.NotNull(role1);
Assert.NotNull(role2);
Assert.NotSame(role1, role2);
role1.Name = Guid.NewGuid().ToString();
IdentityResultAssert.IsSuccess(await manager1.UpdateAsync(role1));
IdentityResultAssert.IsFailure(await manager2.DeleteAsync(role2), IdentityErrorDescriber.Default.ConcurrencyFailure());
}
}
// TODO: can we move these to UserManagerTestBase?
[Fact]
public async Task DeleteRoleNonEmptySucceedsTest()

View File

@ -4,7 +4,6 @@
"Microsoft.AspNet.Http": "1.0.0-*",
"Microsoft.AspNet.Identity": "3.0.0-*",
"Microsoft.AspNet.Identity.EntityFramework": "1.0.0-*",
"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-*",
@ -12,7 +11,7 @@
"EntityFramework.SqlServer": "7.0.0-*",
"Microsoft.Framework.OptionsModel" : "1.0.0-*",
"System.Security.Claims": "1.0.0-*",
"Xunit.KRunner": "1.0.0-*"
"xunit.runner.kre": "1.0.0-*"
},
"code": "**\\*.cs;..\\Shared\\*.cs",
"frameworks": {
@ -23,6 +22,6 @@
}
},
"commands": {
"test": "Xunit.KRunner"
"test": "xunit.runner.kre"
}
}

View File

@ -4,6 +4,7 @@
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Security;
using Microsoft.AspNet.Identity.Test;
@ -14,7 +15,7 @@ using Xunit;
namespace Microsoft.AspNet.Identity.InMemory.Test
{
public class ApplicationUser : IdentityUser { }
public class ApplicationUser : InMemoryUser { }
public class HttpSignInTest
{
@ -30,7 +31,7 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
var response = new Mock<HttpResponse>();
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
response.Setup(r => r.SignIn(It.Is<AuthenticationProperties>(v => v.IsPersistent == isPersistent), It.IsAny<ClaimsIdentity>())).Verifiable();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
app.UseServices(services =>
{
@ -50,10 +51,10 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
var signInManager = app.ApplicationServices.GetRequiredService<SignInManager<ApplicationUser>>();
IdentityResultAssert.IsSuccess(await userManager.CreateAsync(user, password));
var result = await signInManager.PasswordSignInAsync(user.UserName, password, isPersistent, false);
var result = await signInManager.PasswordSignInAsync(user, password, isPersistent, false);
// Assert
Assert.Equal(SignInStatus.Success, result);
Assert.True(result.Succeeded);
context.VerifyAll();
response.VerifyAll();
contextAccessor.VerifyAll();

View File

@ -15,20 +15,20 @@ namespace Microsoft.AspNet.Identity.InMemory
{
private readonly Dictionary<string, TRole> _roles = new Dictionary<string, TRole>();
public Task CreateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> CreateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
{
_roles[role.Id] = role;
return Task.FromResult(0);
return Task.FromResult(IdentityResult.Success);
}
public Task DeleteAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> 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);
return Task.FromResult(IdentityResult.Success);
}
public Task<string> GetRoleIdAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
@ -47,10 +47,10 @@ namespace Microsoft.AspNet.Identity.InMemory
return Task.FromResult(0);
}
public Task UpdateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> UpdateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
{
_roles[role.Id] = role;
return Task.FromResult(0);
return Task.FromResult(IdentityResult.Success);
}
public Task<TRole> FindByIdAsync(string roleId, CancellationToken cancellationToken = default(CancellationToken))
@ -97,6 +97,17 @@ namespace Microsoft.AspNet.Identity.InMemory
return Task.FromResult(0);
}
public Task<string> GetNormalizedRoleNameAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(role.NormalizedName);
}
public Task SetNormalizedRoleNameAsync(TRole role, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
{
role.NormalizedName = normalizedName;
return Task.FromResult(0);
}
public IQueryable<TRole> Roles
{
get { return _roles.Values.AsQueryable(); }

View File

@ -1,12 +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 Microsoft.AspNet.Identity.Test;
using Microsoft.Framework.DependencyInjection;
namespace Microsoft.AspNet.Identity.InMemory.Test
{
public class InMemoryStoreTest : UserManagerTestBase<IdentityUser, IdentityRole>
public class InMemoryStoreTest : UserManagerTestBase<InMemoryUser, IdentityRole>
{
protected override object CreateTestContext()
{
@ -15,7 +16,7 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
protected override void AddUserStore(IServiceCollection services, object context = null)
{
services.AddSingleton<IUserStore<IdentityUser>, InMemoryUserStore<IdentityUser>>();
services.AddSingleton<IUserStore<InMemoryUser>, InMemoryUserStore<InMemoryUser>>();
}
protected override void AddRoleStore(IServiceCollection services, object context = null)

View File

@ -10,6 +10,30 @@ using System.Threading.Tasks;
namespace Microsoft.AspNet.Identity.InMemory
{
public class InMemoryUser : IdentityUser
{
public InMemoryUser() { }
public InMemoryUser(string userName) : base(userName) { }
/// <summary>
/// Roles for the user
/// </summary>
public virtual ICollection<IdentityUserRole> Roles { get; } = new List<IdentityUserRole>();
/// <summary>
/// Claims for the user
/// </summary>
public virtual ICollection<IdentityUserClaim> Claims { get; } = new List<IdentityUserClaim>();
/// <summary>
/// Associated logins for the user
/// </summary>
public virtual ICollection<IdentityUserLogin> Logins { get; } = new List<IdentityUserLogin>();
}
public class InMemoryUserStore<TUser> :
IUserLoginStore<TUser>,
IUserRoleStore<TUser>,
@ -21,7 +45,7 @@ namespace Microsoft.AspNet.Identity.InMemory
IUserPhoneNumberStore<TUser>,
IQueryableUserStore<TUser>,
IUserTwoFactorStore<TUser>
where TUser : IdentityUser
where TUser : InMemoryUser
{
private readonly Dictionary<string, TUser> _logins = new Dictionary<string, TUser>();
@ -42,7 +66,7 @@ namespace Microsoft.AspNet.Identity.InMemory
{
foreach (var claim in claims)
{
user.Claims.Add(new IdentityUserClaim<string> { ClaimType = claim.Type, ClaimValue = claim.Value, UserId = user.Id });
user.Claims.Add(new IdentityUserClaim { ClaimType = claim.Type, ClaimValue = claim.Value, UserId = user.Id });
}
return Task.FromResult(0);
}
@ -50,7 +74,7 @@ namespace Microsoft.AspNet.Identity.InMemory
public Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken))
{
var matchedClaims = user.Claims.Where(uc => uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToList();
foreach(var matchedClaim in matchedClaims)
foreach (var matchedClaim in matchedClaims)
{
matchedClaim.ClaimValue = newClaim.Value;
matchedClaim.ClaimType = newClaim.Type;
@ -84,6 +108,18 @@ namespace Microsoft.AspNet.Identity.InMemory
return Task.FromResult(user.Email);
}
public Task<string> GetNormalizedEmailAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.NormalizedEmail);
}
public Task SetNormalizedEmailAsync(TUser user, string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
{
user.NormalizedEmail = normalizedEmail;
return Task.FromResult(0);
}
public Task<bool> GetEmailConfirmedAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(user.EmailConfirmed);
@ -99,7 +135,7 @@ namespace Microsoft.AspNet.Identity.InMemory
{
return
Task.FromResult(
Users.FirstOrDefault(u => String.Equals(u.Email, email, StringComparison.OrdinalIgnoreCase)));
Users.FirstOrDefault(u => u.NormalizedEmail == email));
}
public Task<DateTimeOffset?> GetLockoutEndDateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
@ -149,7 +185,7 @@ namespace Microsoft.AspNet.Identity.InMemory
public virtual Task AddLoginAsync(TUser user, UserLoginInfo login,
CancellationToken cancellationToken = default(CancellationToken))
{
user.Logins.Add(new IdentityUserLogin<string>
user.Logins.Add(new IdentityUserLogin
{
UserId = user.Id,
ProviderKey = login.ProviderKey,
@ -209,16 +245,16 @@ namespace Microsoft.AspNet.Identity.InMemory
return Task.FromResult(0);
}
public Task CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
_users[user.Id] = user;
return Task.FromResult(0);
return Task.FromResult(IdentityResult.Success);
}
public Task UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> UpdateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
_users[user.Id] = user;
return Task.FromResult(0);
return Task.FromResult(IdentityResult.Success);
}
public Task<TUser> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
@ -238,17 +274,17 @@ namespace Microsoft.AspNet.Identity.InMemory
{
return
Task.FromResult(
Users.FirstOrDefault(u => String.Equals(u.UserName, userName, StringComparison.OrdinalIgnoreCase)));
Users.FirstOrDefault(u => u.NormalizedUserName == userName));
}
public Task DeleteAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> 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);
return Task.FromResult(IdentityResult.Success);
}
public Task SetPasswordHashAsync(TUser user, string passwordHash, CancellationToken cancellationToken = default(CancellationToken))
@ -292,7 +328,7 @@ namespace Microsoft.AspNet.Identity.InMemory
// RoleId == roleName for InMemory
public Task AddToRoleAsync(TUser user, string role, CancellationToken cancellationToken = default(CancellationToken))
{
user.Roles.Add(new IdentityUserRole<string> { RoleId = role, UserId = user.Id });
user.Roles.Add(new IdentityUserRole { RoleId = role, UserId = user.Id });
return Task.FromResult(0);
}
@ -349,5 +385,30 @@ namespace Microsoft.AspNet.Identity.InMemory
user.NormalizedUserName = userName;
return Task.FromResult(0);
}
// RoleId == rolename for inmemory store tests
public Task<IList<TUser>> GetUsersInRoleAsync(string roleName, CancellationToken cancellationToken = default(CancellationToken))
{
if (String.IsNullOrEmpty(roleName))
{
throw new ArgumentNullException("role");
}
return Task.FromResult<IList<TUser>>(Users.Where(u => (u.Roles.Where(x => x.RoleId == roleName).Count() > 0)).Select(x => x).ToList());
}
public Task<IList<TUser>> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken))
{
if (claim == null)
{
throw new ArgumentNullException("claim");
}
var query = from user in Users
where user.Claims.Where(x => x.ClaimType == claim.Type && x.ClaimValue == claim.Value).FirstOrDefault() != null
select user;
return Task.FromResult<IList<TUser>>(query.ToList());
}
}
}

View File

@ -3,7 +3,6 @@
"Microsoft.AspNet.Hosting": "1.0.0-*",
"Microsoft.AspNet.Http" : "1.0.0-*",
"Microsoft.AspNet.Identity" : "3.0.0-*",
"Microsoft.AspNet.PipelineCore" : "1.0.0-*",
"Microsoft.AspNet.RequestContainer" : "1.0.0-*",
"Microsoft.AspNet.Security" : "1.0.0-*",
"Microsoft.AspNet.Security.Cookies" : "1.0.0-*",
@ -11,7 +10,7 @@
"Microsoft.Framework.ConfigurationModel": "1.0.0-*",
"Microsoft.Framework.DependencyInjection" : "1.0.0-*",
"Microsoft.Framework.OptionsModel" : "1.0.0-*",
"Xunit.KRunner": "1.0.0-*"
"xunit.runner.kre": "1.0.0-*"
},
"code": "**\\*.cs;..\\Shared\\*.cs",
"frameworks": {
@ -22,6 +21,6 @@
}
},
"commands": {
"test": "Xunit.KRunner"
"test": "xunit.runner.kre"
}
}

View File

@ -0,0 +1,6 @@
namespace Microsoft.AspNet.Identity.Test
{
internal interface IHttpContextAccessor<T>
{
}
}

View File

@ -10,7 +10,7 @@ using System;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using Microsoft.AspNet.Security.DataProtection;
using Microsoft.Framework.Logging;
namespace Microsoft.AspNet.Identity.Test
{
@ -62,6 +62,28 @@ namespace Microsoft.AspNet.Identity.Test
Assert.NotNull(thingy);
}
[Fact]
public void CanOverrideUserManager()
{
var services = new ServiceCollection();
services.AddIdentity<TestUser, TestRole>()
.AddUserStore<NoopUserStore>()
.AddUserManager<MyUserManager>();
var myUserManager = services.BuildServiceProvider().GetRequiredService(typeof(UserManager<TestUser>)) as MyUserManager;
Assert.NotNull(myUserManager);
}
[Fact]
public void CanOverrideRoleManager()
{
var services = new ServiceCollection();
services.AddIdentity<TestUser, TestRole>()
.AddRoleStore<NoopRoleStore>()
.AddRoleManager<MyRoleManager>();
var myRoleManager = services.BuildServiceProvider().GetRequiredService<RoleManager<TestRole>>() as MyRoleManager;
Assert.NotNull(myRoleManager);
}
[Fact]
public void EnsureDefaultServices()
{
@ -92,22 +114,22 @@ namespace Microsoft.AspNet.Identity.Test
private class MyUberThingy : IUserValidator<IdentityUser>, IPasswordValidator<IdentityUser>, IRoleValidator<IdentityRole>, IUserStore<IdentityUser>, IRoleStore<IdentityRole>
{
public Task CreateAsync(IdentityRole role, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> CreateAsync(IdentityRole role, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task CreateAsync(IdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> CreateAsync(IdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task DeleteAsync(IdentityRole role, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> DeleteAsync(IdentityRole role, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task DeleteAsync(IdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> DeleteAsync(IdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
@ -127,6 +149,11 @@ namespace Microsoft.AspNet.Identity.Test
throw new NotImplementedException();
}
public Task<string> GetNormalizedRoleNameAsync(IdentityRole role, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task<string> GetNormalizedUserNameAsync(IdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
@ -152,6 +179,11 @@ namespace Microsoft.AspNet.Identity.Test
throw new NotImplementedException();
}
public Task SetNormalizedRoleNameAsync(IdentityRole role, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task SetNormalizedUserNameAsync(IdentityUser user, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
@ -167,12 +199,12 @@ namespace Microsoft.AspNet.Identity.Test
throw new NotImplementedException();
}
public Task UpdateAsync(IdentityRole role, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> UpdateAsync(IdentityRole role, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task UpdateAsync(IdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> UpdateAsync(IdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
@ -203,5 +235,18 @@ namespace Microsoft.AspNet.Identity.Test
}
}
private class MyUserManager : UserManager<TestUser>
{
public MyUserManager(IUserStore<TestUser> store) : base(store) { }
}
private class MyRoleManager : RoleManager<TestRole>
{
public MyRoleManager(IRoleStore<TestRole> store,
IEnumerable<IRoleValidator<TestRole>> roleValidators) : base(store)
{
}
}
}
}

View File

@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Identity.Test
Assert.Equal(ClaimTypes.Role, options.ClaimsIdentity.RoleClaimType);
Assert.Equal(ClaimTypes.Name, options.ClaimsIdentity.UserNameClaimType);
Assert.Equal(ClaimTypes.NameIdentifier, options.ClaimsIdentity.UserIdClaimType);
Assert.Equal(ClaimsIdentityOptions.DefaultSecurityStampClaimType, options.ClaimsIdentity.SecurityStampClaimType);
Assert.Equal("AspNet.Identity.SecurityStamp", options.ClaimsIdentity.SecurityStampClaimType);
}
[Theory]

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using System.Text;
using Xunit;
namespace Microsoft.AspNet.Identity.Test
@ -13,17 +14,39 @@ namespace Microsoft.AspNet.Identity.Test
{
var result = new IdentityResult();
Assert.False(result.Succeeded);
Assert.Equal(1, result.Errors.Count());
Assert.Equal("An unknown failure has occured.", result.Errors.First());
Assert.Equal(0, result.Errors.Count());
}
[Fact]
public void NullErrorListUsesDefaultError()
public void NullFailedUsesEmptyErrors()
{
var result = new IdentityResult(null);
var result = IdentityResult.Failed();
Assert.False(result.Succeeded);
Assert.Equal(1, result.Errors.Count());
Assert.Equal("An unknown failure has occured.", result.Errors.First());
Assert.Equal(0, result.Errors.Count());
}
[Fact]
public void VerifySuccessResultLog()
{
var result = IdentityResult.Success;
var logMessage = new StringBuilder();
var logger = MockHelpers.MockILogger(logMessage);
result.Log(logger.Object, "Operation");
Assert.Equal("Operation : Success", logMessage.ToString());
}
[Fact]
public void VerifyFailureResultLog()
{
var result = IdentityResult.Failed(new IdentityError() { Code = "Foo" }, new IdentityError() { Code = "Bar" });
var logMessage = new StringBuilder();
var logger = MockHelpers.MockILogger(logMessage);
result.Log(logger.Object, "Operation");
Assert.Equal("Operation : Failed : Foo,Bar", logMessage.ToString());
}
}
}

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
@ -14,4 +14,9 @@
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
<ProjectExtensions>
<VisualStudio>
<UserProperties project_1json__JSONSchema="http://www.asp.net/media/4878834/project.json" />
</VisualStudio>
</ProjectExtensions>
</Project>

View File

@ -8,14 +8,14 @@ namespace Microsoft.AspNet.Identity.Test
{
public class NoopRoleStore : IRoleStore<TestRole>
{
public Task CreateAsync(TestRole user, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> CreateAsync(TestRole user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(0);
return Task.FromResult(IdentityResult.Success);
}
public Task UpdateAsync(TestRole user, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> UpdateAsync(TestRole user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(0);
return Task.FromResult(IdentityResult.Success);
}
public Task<string> GetRoleNameAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
@ -42,14 +42,24 @@ namespace Microsoft.AspNet.Identity.Test
{
}
public Task DeleteAsync(TestRole user, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> DeleteAsync(TestRole user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(0);
return Task.FromResult(IdentityResult.Success);
}
public Task<string> GetRoleIdAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult<string>(null);
}
public Task<string> GetNormalizedRoleNameAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult<string>(null);
}
public Task SetNormalizedRoleNameAsync(TestRole role, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(0);
}
}
}

View File

@ -24,14 +24,14 @@ namespace Microsoft.AspNet.Identity.Test
return Task.FromResult(0);
}
public Task CreateAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> CreateAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(0);
return Task.FromResult(IdentityResult.Success);
}
public Task UpdateAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> UpdateAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(0);
return Task.FromResult(IdentityResult.Success);
}
public Task<TestUser> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
@ -48,9 +48,9 @@ namespace Microsoft.AspNet.Identity.Test
{
}
public Task DeleteAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> DeleteAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(0);
return Task.FromResult(IdentityResult.Success);
}
public Task<string> GetNormalizedUserNameAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
@ -133,13 +134,18 @@ namespace Microsoft.AspNet.Identity.Test
{
errors.Add(upperError);
}
var result = await valid.ValidateAsync(manager, null, input);
if (errors.Count == 0)
{
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(manager, null, input));
IdentityResultAssert.IsSuccess(result);
}
else
{
IdentityResultAssert.IsFailure(await valid.ValidateAsync(manager, null, input), string.Join(" ", errors));
IdentityResultAssert.IsFailure(result);
foreach (var error in errors)
{
Assert.True(result.Errors.Any(e => e.Description == error));
}
}
}
}

View File

@ -6,12 +6,51 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Identity.Test
{
public class RoleManagerTest
{
[Fact]
public async Task CreateCallsStore()
{
// Setup
var store = new Mock<IRoleStore<TestRole>>();
var role = new TestRole { Name = "Foo" };
store.Setup(s => s.CreateAsync(role, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable();
store.Setup(s => s.GetRoleNameAsync(role, CancellationToken.None)).Returns(Task.FromResult(role.Name)).Verifiable();
store.Setup(s => s.SetNormalizedRoleNameAsync(role, role.Name.ToUpperInvariant(), CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
var roleManager = MockHelpers.TestRoleManager(store.Object);
// Act
var result = await roleManager.CreateAsync(role);
// Assert
Assert.True(result.Succeeded);
store.VerifyAll();
}
[Fact]
public async Task UpdateCallsStore()
{
// Setup
var store = new Mock<IRoleStore<TestRole>>();
var role = new TestRole { Name = "Foo" };
store.Setup(s => s.UpdateAsync(role, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable();
store.Setup(s => s.GetRoleNameAsync(role, CancellationToken.None)).Returns(Task.FromResult(role.Name)).Verifiable();
store.Setup(s => s.SetNormalizedRoleNameAsync(role, role.Name.ToUpperInvariant(), CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
var roleManager = MockHelpers.TestRoleManager(store.Object);
// Act
var result = await roleManager.UpdateAsync(role);
// Assert
Assert.True(result.Succeeded);
store.VerifyAll();
}
[Fact]
public void RolesQueryableFailWhenStoreNotImplemented()
{
@ -20,6 +59,59 @@ namespace Microsoft.AspNet.Identity.Test
Assert.Throws<NotSupportedException>(() => manager.Roles.Count());
}
[Fact]
public async Task FindByNameCallsStoreWithNormalizedName()
{
// Setup
var store = new Mock<IRoleStore<TestRole>>();
var role = new TestRole { Name = "Foo" };
store.Setup(s => s.FindByNameAsync("FOO", CancellationToken.None)).Returns(Task.FromResult(role)).Verifiable();
var manager = MockHelpers.TestRoleManager(store.Object);
// Act
var result = await manager.FindByNameAsync(role.Name);
// Assert
Assert.Equal(role, result);
store.VerifyAll();
}
[Fact]
public async Task CanFindByNameCallsStoreWithoutNormalizedName()
{
// Setup
var store = new Mock<IRoleStore<TestRole>>();
var role = new TestRole { Name = "Foo" };
store.Setup(s => s.FindByNameAsync(role.Name, CancellationToken.None)).Returns(Task.FromResult(role)).Verifiable();
var manager = MockHelpers.TestRoleManager(store.Object);
manager.KeyNormalizer = null;
// Act
var result = await manager.FindByNameAsync(role.Name);
// Assert
Assert.Equal(role, result);
store.VerifyAll();
}
[Fact]
public async Task RoleExistsCallsStoreWithNormalizedName()
{
// Setup
var store = new Mock<IRoleStore<TestRole>>();
var role = new TestRole { Name = "Foo" };
store.Setup(s => s.FindByNameAsync("FOO", CancellationToken.None)).Returns(Task.FromResult(role)).Verifiable();
var manager = MockHelpers.TestRoleManager(store.Object);
// Act
var result = await manager.RoleExistsAsync(role.Name);
// Assert
Assert.True(result);
store.VerifyAll();
}
[Fact]
public void DisposeAfterDisposeDoesNotThrow()
{
@ -32,7 +124,7 @@ namespace Microsoft.AspNet.Identity.Test
public async Task RoleManagerPublicNullChecks()
{
Assert.Throws<ArgumentNullException>("store",
() => new RoleManager<TestRole>(null, null));
() => new RoleManager<TestRole>(null, null, null));
var manager = CreateRoleManager(new NotImplementedStore());
await Assert.ThrowsAsync<ArgumentNullException>("role", async () => await manager.CreateAsync(null));
await Assert.ThrowsAsync<ArgumentNullException>("role", async () => await manager.UpdateAsync(null));
@ -58,22 +150,22 @@ namespace Microsoft.AspNet.Identity.Test
{
var v = new List<IRoleValidator<TestRole>>();
v.Add(new RoleValidator<TestRole>());
return new RoleManager<TestRole>(roleStore, v);
return new RoleManager<TestRole>(roleStore);
}
private class NotImplementedStore : IRoleStore<TestRole>
{
public Task CreateAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> CreateAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task UpdateAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> UpdateAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task DeleteAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> DeleteAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
@ -107,6 +199,16 @@ namespace Microsoft.AspNet.Identity.Test
{
throw new NotImplementedException();
}
public Task<string> GetNormalizedRoleNameAsync(TestRole role, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task SetNormalizedRoleNameAsync(TestRole role, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
}
}
}

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Identity.Test
{
// Setup
var validator = new RoleValidator<TestRole>();
var manager = new RoleManager<TestRole>(new NoopRoleStore(), null);
var manager = new RoleManager<TestRole>(new NoopRoleStore());
// Act
// Assert
@ -29,14 +29,14 @@ namespace Microsoft.AspNet.Identity.Test
{
// Setup
var validator = new RoleValidator<TestRole>();
var manager = new RoleManager<TestRole>(new NoopRoleStore(), null);
var manager = new RoleManager<TestRole>(new NoopRoleStore());
var user = new TestRole {Name = input};
// Act
var result = await validator.ValidateAsync(manager, user);
// Assert
IdentityResultAssert.IsFailure(result, "Name cannot be null or empty.");
IdentityResultAssert.IsFailure(result, IdentityErrorDescriber.Default.InvalidRoleName(input));
}
}
}

View File

@ -5,6 +5,7 @@ using System;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Security;
using Microsoft.AspNet.Security;
@ -43,10 +44,10 @@ namespace Microsoft.AspNet.Identity.Test
var options = new Mock<IOptions<IdentityOptions>>();
options.Setup(a => a.Options).Returns(identityOptions);
var httpContext = new Mock<HttpContext>();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.Value).Returns(httpContext.Object);
var signInManager = new Mock<SignInManager<IdentityUser>>(userManager.Object,
contextAccessor.Object, claimsManager.Object, options.Object);
contextAccessor.Object, claimsManager.Object, options.Object, null);
signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsIdentity>(), user.Id, CancellationToken.None)).ReturnsAsync(user).Verifiable();
signInManager.Setup(s => s.SignInAsync(user, isPersistent, null, CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
var services = new ServiceCollection();
@ -78,10 +79,10 @@ namespace Microsoft.AspNet.Identity.Test
var options = new Mock<IOptions<IdentityOptions>>();
options.Setup(a => a.Options).Returns(identityOptions);
var httpContext = new Mock<HttpContext>();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.Value).Returns(httpContext.Object);
var signInManager = new Mock<SignInManager<IdentityUser>>(userManager.Object,
contextAccessor.Object, claimsManager.Object, options.Object);
contextAccessor.Object, claimsManager.Object, options.Object, null);
signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsIdentity>(), user.Id, CancellationToken.None)).ReturnsAsync(null).Verifiable();
var services = new ServiceCollection();
services.AddInstance(options.Object);
@ -112,10 +113,10 @@ namespace Microsoft.AspNet.Identity.Test
var identityOptions = new IdentityOptions { SecurityStampValidationInterval = TimeSpan.Zero };
var options = new Mock<IOptions<IdentityOptions>>();
options.Setup(a => a.Options).Returns(identityOptions);
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.Value).Returns(httpContext.Object);
var signInManager = new Mock<SignInManager<IdentityUser>>(userManager.Object,
contextAccessor.Object, claimsManager.Object, options.Object);
contextAccessor.Object, claimsManager.Object, options.Object, null);
signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsIdentity>(), user.Id, CancellationToken.None)).ReturnsAsync(null).Verifiable();
var services = new ServiceCollection();
services.AddInstance(options.Object);
@ -146,10 +147,10 @@ namespace Microsoft.AspNet.Identity.Test
var identityOptions = new IdentityOptions { SecurityStampValidationInterval = TimeSpan.FromDays(1) };
var options = new Mock<IOptions<IdentityOptions>>();
options.Setup(a => a.Options).Returns(identityOptions);
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.Value).Returns(httpContext.Object);
var signInManager = new Mock<SignInManager<IdentityUser>>(userManager.Object,
contextAccessor.Object, claimsManager.Object, options.Object);
contextAccessor.Object, claimsManager.Object, options.Object, null);
signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsIdentity>(), user.Id, CancellationToken.None)).Throws(new Exception("Shouldn't be called"));
signInManager.Setup(s => s.SignInAsync(user, false, null, CancellationToken.None)).Throws(new Exception("Shouldn't be called"));
var services = new ServiceCollection();

View File

@ -1,17 +1,19 @@
// 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.Security.Claims;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Security;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.OptionsModel;
using Moq;
using System;
using System.Collections.Generic;
using System.Security.Claims;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.AspNet.Identity.Test
@ -31,7 +33,7 @@ namespace Microsoft.AspNet.Identity.Test
// TODO: how to functionally test context?
// var context = new DefaultHttpContext(new FeatureCollection());
// var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
// var contextAccessor = new Mock<IHttpContextAccessor>();
// contextAccessor.Setup(a => a.Value).Returns(context);
// app.UseServices(services =>
// {
@ -67,16 +69,14 @@ namespace Microsoft.AspNet.Identity.Test
[Fact]
public void ConstructorNullChecks()
{
Assert.Throws<ArgumentNullException>("userManager", () => new SignInManager<IdentityUser>(null, null, null, null));
Assert.Throws<ArgumentNullException>("userManager", () => new SignInManager<IdentityUser>(null, null, null, null, null));
var userManager = MockHelpers.MockUserManager<IdentityUser>().Object;
Assert.Throws<ArgumentNullException>("contextAccessor", () => new SignInManager<IdentityUser>(userManager, null, null, null));
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
var contextAccessor = new Mock<IHttpContextAccessor>();
Assert.Throws<ArgumentNullException>("contextAccessor", () => new SignInManager<IdentityUser>(userManager, contextAccessor.Object, null, null));
var context = new Mock<HttpContext>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
Assert.Throws<ArgumentNullException>("claimsFactory", () => new SignInManager<IdentityUser>(userManager, contextAccessor.Object, null, null));
var claimsFactory = new Mock<IClaimsIdentityFactory<IdentityUser>>().Object;
Assert.Throws<ArgumentNullException>("optionsAccessor", () => new SignInManager<IdentityUser>(userManager, contextAccessor.Object, claimsFactory, null));
}
//TODO: Mock fails in K (this works fine in net45)
@ -95,7 +95,7 @@ namespace Microsoft.AspNet.Identity.Test
// var response = new Mock<HttpResponse>();
// context.Setup(c => c.Response).Returns(response.Object).Verifiable();
// response.Setup(r => r.SignIn(testIdentity, It.IsAny<AuthenticationProperties>())).Verifiable();
// var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
// var contextAccessor = new Mock<IHttpContextAccessor>();
// contextAccessor.Setup(a => a.Value).Returns(context.Object);
// var helper = new HttpAuthenticationManager(contextAccessor.Object);
@ -118,24 +118,31 @@ namespace Microsoft.AspNet.Identity.Test
manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable();
manager.Setup(m => m.IsLockedOutAsync(user, CancellationToken.None)).ReturnsAsync(true).Verifiable();
manager.Setup(m => m.FindByNameAsync(user.UserName, CancellationToken.None)).ReturnsAsync(user).Verifiable();
manager.Setup(m => m.GetUserIdAsync(user, CancellationToken.None)).ReturnsAsync(user.Id.ToString()).Verifiable();
var context = new Mock<HttpContext>();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var roleManager = MockHelpers.MockRoleManager<TestRole>();
var identityOptions = new IdentityOptions();
var options = new Mock<IOptions<IdentityOptions>>();
options.Setup(a => a.Options).Returns(identityOptions);
var claimsFactory = new Mock<ClaimsIdentityFactory<TestUser, TestRole>>(manager.Object, roleManager.Object, options.Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object);
var logStore = new StringBuilder();
var loggerFactory = MockHelpers.MockILoggerFactory(MockHelpers.MockILogger(logStore).Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object, loggerFactory.Object);
string expected = string.Format("{0} for user: {1} : Result : {2}", "PasswordSignInAsync", user.Id, "Lockedout");
// Act
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false);
// Assert
Assert.Equal(SignInStatus.LockedOut, result);
Assert.False(result.Succeeded);
Assert.True(result.IsLockedOut);
Assert.NotEqual(-1, logStore.ToString().IndexOf(expected));
manager.VerifyAll();
}
[Theory]
[InlineData(true)]
[InlineData(false)]
@ -149,11 +156,13 @@ namespace Microsoft.AspNet.Identity.Test
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.ResetAccessFailedCountAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable();
manager.Setup(m => m.GetUserIdAsync(user, CancellationToken.None)).ReturnsAsync(user.Id.ToString()).Verifiable();
var context = new Mock<HttpContext>();
var response = new Mock<HttpResponse>();
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
response.Setup(r => r.SignIn(It.Is<AuthenticationProperties>(v => v.IsPersistent == isPersistent), It.IsAny<ClaimsIdentity>())).Verifiable();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var roleManager = MockHelpers.MockRoleManager<TestRole>();
var identityOptions = new IdentityOptions();
@ -161,13 +170,17 @@ namespace Microsoft.AspNet.Identity.Test
options.Setup(a => a.Options).Returns(identityOptions);
var claimsFactory = new Mock<ClaimsIdentityFactory<TestUser, TestRole>>(manager.Object, roleManager.Object, options.Object);
claimsFactory.Setup(m => m.CreateAsync(user, CancellationToken.None)).ReturnsAsync(new ClaimsIdentity("Microsoft.AspNet.Identity")).Verifiable();
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object);
var logStore = new StringBuilder();
var loggerFactory = MockHelpers.MockILoggerFactory(MockHelpers.MockILogger(logStore).Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object, loggerFactory.Object);
string expected = string.Format("{0} for user: {1} : Result : {2}", "PasswordSignInAsync", user.Id, "Succeeded");
// Act
var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false);
// Assert
Assert.Equal(SignInStatus.Success, result);
Assert.True(result.Succeeded);
Assert.NotEqual(-1, logStore.ToString().IndexOf(expected));
manager.VerifyAll();
context.VerifyAll();
response.VerifyAll();
@ -186,11 +199,13 @@ namespace Microsoft.AspNet.Identity.Test
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.ResetAccessFailedCountAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable();
manager.Setup(m => m.GetUserIdAsync(user, CancellationToken.None)).ReturnsAsync(user.Id.ToString()).Verifiable();
var context = new Mock<HttpContext>();
var response = new Mock<HttpResponse>();
response.Setup(r => r.SignIn(It.IsAny<AuthenticationProperties>(), It.IsAny<ClaimsIdentity>())).Verifiable();
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var roleManager = MockHelpers.MockRoleManager<TestRole>();
var identityOptions = new IdentityOptions();
@ -203,7 +218,7 @@ namespace Microsoft.AspNet.Identity.Test
var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false);
// Assert
Assert.Equal(SignInStatus.Success, result);
Assert.True(result.Succeeded);
manager.VerifyAll();
context.VerifyAll();
response.VerifyAll();
@ -239,19 +254,24 @@ namespace Microsoft.AspNet.Identity.Test
var response = new Mock<HttpResponse>();
response.Setup(r => r.SignIn(It.Is<ClaimsIdentity>(id => id.Name == user.Id))).Verifiable();
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var roleManager = MockHelpers.MockRoleManager<TestRole>();
var identityOptions = new IdentityOptions();
var options = new Mock<IOptions<IdentityOptions>>();
options.Setup(a => a.Options).Returns(identityOptions);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, new ClaimsIdentityFactory<TestUser, TestRole>(manager.Object, roleManager.Object, options.Object), options.Object);
var logStore = new StringBuilder();
var loggerFactory = MockHelpers.MockILoggerFactory(MockHelpers.MockILogger(logStore).Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, new ClaimsIdentityFactory<TestUser, TestRole>(manager.Object, roleManager.Object, options.Object), options.Object, loggerFactory.Object);
string expected = string.Format("{0} for user: {1} : Result : {2}", "PasswordSignInAsync", user.Id, "RequiresTwoFactor");
// Act
var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false);
// Assert
Assert.Equal(SignInStatus.RequiresVerification, result);
Assert.False(result.Succeeded);
Assert.True(result.RequiresTwoFactor);
Assert.NotEqual(-1, logStore.ToString().IndexOf(expected));
manager.VerifyAll();
context.VerifyAll();
response.VerifyAll();
@ -276,13 +296,15 @@ namespace Microsoft.AspNet.Identity.Test
manager.Setup(m => m.IsLockedOutAsync(user, CancellationToken.None)).ReturnsAsync(false).Verifiable();
}
manager.Setup(m => m.FindByLoginAsync(loginProvider, providerKey, CancellationToken.None)).ReturnsAsync(user).Verifiable();
manager.Setup(m => m.GetUserIdAsync(user, CancellationToken.None)).ReturnsAsync(user.Id.ToString()).Verifiable();
var context = new Mock<HttpContext>();
var response = new Mock<HttpResponse>();
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
response.Setup(r => r.SignIn(
It.Is<AuthenticationProperties>(v => v.IsPersistent == isPersistent),
It.Is<ClaimsIdentity>(i => i.FindFirstValue(ClaimTypes.AuthenticationMethod) == loginProvider))).Verifiable();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var roleManager = MockHelpers.MockRoleManager<TestRole>();
var identityOptions = new IdentityOptions();
@ -291,13 +313,17 @@ namespace Microsoft.AspNet.Identity.Test
options.Setup(a => a.Options).Returns(identityOptions);
var claimsFactory = new Mock<ClaimsIdentityFactory<TestUser, TestRole>>(manager.Object, roleManager.Object, options.Object);
claimsFactory.Setup(m => m.CreateAsync(user, CancellationToken.None)).ReturnsAsync(new ClaimsIdentity("Microsoft.AspNet.Identity")).Verifiable();
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object);
var logStore = new StringBuilder();
var loggerFactory = MockHelpers.MockILoggerFactory(MockHelpers.MockILogger(logStore).Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object, loggerFactory.Object);
string expected = string.Format("{0} for user: {1} : Result : {2}", "ExternalLoginSignInAsync", user.Id.ToString(), "Succeeded");
// Act
var result = await helper.ExternalLoginSignInAsync(loginProvider, providerKey, isPersistent);
// Assert
Assert.Equal(SignInStatus.Success, result);
Assert.True(result.Succeeded);
Assert.NotEqual(-1, logStore.ToString().IndexOf(expected));
manager.VerifyAll();
context.VerifyAll();
response.VerifyAll();
@ -322,7 +348,6 @@ namespace Microsoft.AspNet.Identity.Test
[InlineData(false, true, false, false)]
[InlineData(false, false, true, false)]
[InlineData(false, false, false, false)]
public async Task CanTwoFactorSignIn(bool isPersistent, bool supportsLockout, bool externalLogin, bool rememberClient)
{
// Setup
@ -342,7 +367,7 @@ namespace Microsoft.AspNet.Identity.Test
manager.Setup(m => m.GetUserNameAsync(user, CancellationToken.None)).ReturnsAsync(user.UserName).Verifiable();
var context = new Mock<HttpContext>();
var response = new Mock<HttpResponse>();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
var contextAccessor = new Mock<IHttpContextAccessor>();
var twoFactorInfo = new SignInManager<TestUser>.TwoFactorAuthenticationInfo { UserId = user.Id };
var loginProvider = "loginprovider";
var id = SignInManager<TestUser>.StoreTwoFactorInfo(user.Id, externalLogin ? loginProvider : null);
@ -376,13 +401,17 @@ namespace Microsoft.AspNet.Identity.Test
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
context.Setup(c => c.AuthenticateAsync(IdentityOptions.TwoFactorUserIdCookieAuthenticationType)).ReturnsAsync(authResult).Verifiable();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory, options.Object);
var logStore = new StringBuilder();
var loggerFactory = MockHelpers.MockILoggerFactory(MockHelpers.MockILogger(logStore).Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory, options.Object, loggerFactory.Object);
string expected = string.Format("{0} for user: {1} : Result : {2}", "TwoFactorSignInAsync", user.Id.ToString(), "Succeeded");
// Act
var result = await helper.TwoFactorSignInAsync(provider, code, isPersistent, rememberClient);
// Assert
Assert.Equal(SignInStatus.Success, result);
Assert.True(result.Succeeded);
Assert.NotEqual(-1, logStore.ToString().IndexOf(expected));
manager.VerifyAll();
context.VerifyAll();
response.VerifyAll();
@ -397,7 +426,7 @@ namespace Microsoft.AspNet.Identity.Test
var manager = MockHelpers.MockUserManager<TestUser>();
var context = new Mock<HttpContext>();
var response = new Mock<HttpResponse>();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
var contextAccessor = new Mock<IHttpContextAccessor>();
var roleManager = MockHelpers.MockRoleManager<TestRole>();
var identityOptions = new IdentityOptions();
var options = new Mock<IOptions<IdentityOptions>>();
@ -452,7 +481,7 @@ namespace Microsoft.AspNet.Identity.Test
id.AddClaim(new Claim(ClaimTypes.Name, user.Id));
var authResult = new AuthenticationResult(id, new AuthenticationProperties(), new AuthenticationDescription());
context.Setup(c => c.AuthenticateAsync(IdentityOptions.TwoFactorRememberMeCookieAuthenticationType)).ReturnsAsync(authResult).Verifiable();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var roleManager = MockHelpers.MockRoleManager<TestRole>();
var identityOptions = new IdentityOptions();
@ -466,7 +495,7 @@ namespace Microsoft.AspNet.Identity.Test
var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false);
// Assert
Assert.Equal(SignInStatus.Success, result);
Assert.True(result.Succeeded);
manager.VerifyAll();
context.VerifyAll();
response.VerifyAll();
@ -487,7 +516,7 @@ namespace Microsoft.AspNet.Identity.Test
response.Setup(r => r.SignOut(authenticationType)).Verifiable();
response.Setup(r => r.SignOut(IdentityOptions.TwoFactorUserIdCookieAuthenticationType)).Verifiable();
response.Setup(r => r.SignOut(IdentityOptions.ExternalCookieAuthenticationType)).Verifiable();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var roleManager = MockHelpers.MockRoleManager<TestRole>();
var identityOptions = new IdentityOptions();
@ -495,7 +524,9 @@ namespace Microsoft.AspNet.Identity.Test
options.Setup(a => a.Options).Returns(identityOptions);
IdentityOptions.ApplicationCookieAuthenticationType = authenticationType;
var claimsFactory = new Mock<ClaimsIdentityFactory<TestUser, TestRole>>(manager.Object, roleManager.Object, options.Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object);
var logStore = new StringBuilder();
var loggerFactory = MockHelpers.MockILoggerFactory(MockHelpers.MockILogger(logStore).Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object, loggerFactory.Object);
// Act
helper.SignOut();
@ -517,20 +548,25 @@ namespace Microsoft.AspNet.Identity.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, "bogus", CancellationToken.None)).ReturnsAsync(false).Verifiable();
manager.Setup(m => m.GetUserIdAsync(user, CancellationToken.None)).ReturnsAsync(user.Id.ToString()).Verifiable();
var context = new Mock<HttpContext>();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var roleManager = MockHelpers.MockRoleManager<TestRole>();
var identityOptions = new IdentityOptions();
var options = new Mock<IOptions<IdentityOptions>>();
options.Setup(a => a.Options).Returns(identityOptions);
var claimsFactory = new Mock<ClaimsIdentityFactory<TestUser, TestRole>>(manager.Object, roleManager.Object, options.Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object);
var logStore = new StringBuilder();
var loggerFactory = MockHelpers.MockILoggerFactory(MockHelpers.MockILogger(logStore).Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object, loggerFactory.Object);
string expected = string.Format("{0} for user: {1} : Result : {2}", "PasswordSignInAsync", user.Id.ToString(), "Failed");
// Act
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false);
// Assert
Assert.Equal(SignInStatus.Failure, result);
Assert.False(result.Succeeded);
Assert.NotEqual(-1, logStore.ToString().IndexOf(expected));
manager.VerifyAll();
context.VerifyAll();
contextAccessor.VerifyAll();
@ -543,7 +579,7 @@ namespace Microsoft.AspNet.Identity.Test
var manager = MockHelpers.MockUserManager<TestUser>();
manager.Setup(m => m.FindByNameAsync("bogus", CancellationToken.None)).ReturnsAsync(null).Verifiable();
var context = new Mock<HttpContext>();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var roleManager = MockHelpers.MockRoleManager<TestRole>();
var identityOptions = new IdentityOptions();
@ -556,7 +592,7 @@ namespace Microsoft.AspNet.Identity.Test
var result = await helper.PasswordSignInAsync("bogus", "bogus", false, false);
// Assert
Assert.Equal(SignInStatus.Failure, result);
Assert.False(result.Succeeded);
manager.VerifyAll();
context.VerifyAll();
contextAccessor.VerifyAll();
@ -579,7 +615,7 @@ namespace Microsoft.AspNet.Identity.Test
manager.Setup(m => m.FindByNameAsync(user.UserName, CancellationToken.None)).ReturnsAsync(user).Verifiable();
manager.Setup(m => m.CheckPasswordAsync(user, "bogus", CancellationToken.None)).ReturnsAsync(false).Verifiable();
var context = new Mock<HttpContext>();
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var roleManager = MockHelpers.MockRoleManager<TestRole>();
var identityOptions = new IdentityOptions();
@ -592,7 +628,8 @@ namespace Microsoft.AspNet.Identity.Test
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, true);
// Assert
Assert.Equal(SignInStatus.LockedOut, result);
Assert.False(result.Succeeded);
Assert.True(result.IsLockedOut);
manager.VerifyAll();
}
@ -609,6 +646,7 @@ namespace Microsoft.AspNet.Identity.Test
{
manager.Setup(m => m.CheckPasswordAsync(user, "password", CancellationToken.None)).ReturnsAsync(true).Verifiable();
}
manager.Setup(m => m.GetUserIdAsync(user, CancellationToken.None)).ReturnsAsync(user.Id.ToString()).Verifiable();
var context = new Mock<HttpContext>();
var response = new Mock<HttpResponse>();
if (confirmed)
@ -617,7 +655,7 @@ namespace Microsoft.AspNet.Identity.Test
context.Setup(c => c.Response).Returns(response.Object).Verifiable();
response.Setup(r => r.SignIn(It.Is<AuthenticationProperties>(v => v.IsPersistent == false), It.IsAny<ClaimsIdentity>())).Verifiable();
}
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var roleManager = MockHelpers.MockRoleManager<TestRole>();
var identityOptions = new IdentityOptions();
@ -625,13 +663,19 @@ namespace Microsoft.AspNet.Identity.Test
var options = new Mock<IOptions<IdentityOptions>>();
options.Setup(a => a.Options).Returns(identityOptions);
var claimsFactory = new Mock<ClaimsIdentityFactory<TestUser, TestRole>>(manager.Object, roleManager.Object, options.Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object);
var logStore = new StringBuilder();
var loggerFactory = MockHelpers.MockILoggerFactory(MockHelpers.MockILogger(logStore).Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object, loggerFactory.Object);
string expected = string.Format("{0} for user: {1} : Result : {2}", "CanSignInAsync", user.Id.ToString(), confirmed.ToString());
// Act
var result = await helper.PasswordSignInAsync(user, "password", false, false);
// Assert
Assert.Equal(confirmed ? SignInStatus.Success : SignInStatus.NotAllowed, result);
Assert.Equal(confirmed, result.Succeeded);
Assert.NotEqual(confirmed, result.IsNotAllowed);
Assert.NotEqual(-1, logStore.ToString().IndexOf(expected));
manager.VerifyAll();
context.VerifyAll();
response.VerifyAll();
@ -646,7 +690,8 @@ namespace Microsoft.AspNet.Identity.Test
// Setup
var user = new TestUser { UserName = "Foo" };
var manager = MockHelpers.MockUserManager<TestUser>();
manager.Setup(m => m.IsEmailConfirmedAsync(user, CancellationToken.None)).ReturnsAsync(confirmed).Verifiable();
manager.Setup(m => m.IsPhoneNumberConfirmedAsync(user, CancellationToken.None)).ReturnsAsync(confirmed).Verifiable();
manager.Setup(m => m.GetUserIdAsync(user, CancellationToken.None)).ReturnsAsync(user.Id.ToString()).Verifiable();
var context = new Mock<HttpContext>();
var response = new Mock<HttpResponse>();
if (confirmed)
@ -656,21 +701,26 @@ namespace Microsoft.AspNet.Identity.Test
response.Setup(r => r.SignIn(It.Is<AuthenticationProperties>(v => v.IsPersistent == false), It.IsAny<ClaimsIdentity>())).Verifiable();
}
var contextAccessor = new Mock<IContextAccessor<HttpContext>>();
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.Value).Returns(context.Object);
var roleManager = MockHelpers.MockRoleManager<TestRole>();
var identityOptions = new IdentityOptions();
identityOptions.SignIn.RequireConfirmedEmail = true;
identityOptions.SignIn.RequireConfirmedPhoneNumber = true;
var options = new Mock<IOptions<IdentityOptions>>();
options.Setup(a => a.Options).Returns(identityOptions);
var claimsFactory = new Mock<ClaimsIdentityFactory<TestUser, TestRole>>(manager.Object, roleManager.Object, options.Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object);
var logStore = new StringBuilder();
var loggerFactory = MockHelpers.MockILoggerFactory(MockHelpers.MockILogger(logStore).Object);
var helper = new SignInManager<TestUser>(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object, loggerFactory.Object);
string expected = string.Format("{0} for user: {1} : Result : {2}", "CanSignInAsync", user.Id.ToString(), confirmed.ToString());
// Act
var result = await helper.PasswordSignInAsync(user, "password", false, false);
// Assert
Assert.Equal(confirmed ? SignInStatus.Success : SignInStatus.NotAllowed, result);
Assert.Equal(confirmed, result.Succeeded);
Assert.NotEqual(confirmed, result.IsNotAllowed);
Assert.NotEqual(-1, logStore.ToString().IndexOf(expected));
manager.VerifyAll();
context.VerifyAll();
response.VerifyAll();

View File

@ -0,0 +1,71 @@
// 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.Text;
using Xunit;
namespace Microsoft.AspNet.Identity.Test
{
public class SignInResultTest
{
[Fact]
public void VerifyLogSuccess()
{
var result = SignInResult.Success;
var logMessage = new StringBuilder();
var logger = MockHelpers.MockILogger(logMessage);
result.Log(logger.Object, "Operation");
Assert.Equal("Operation : Result : Succeeded", logMessage.ToString());
}
[Fact]
public void VerifyLogLockedOut()
{
var result = SignInResult.LockedOut;
var logMessage = new StringBuilder();
var logger = MockHelpers.MockILogger(logMessage);
result.Log(logger.Object, "Operation");
Assert.Equal("Operation : Result : Lockedout", logMessage.ToString());
}
[Fact]
public void VerifyLogNotAllowed()
{
var result = SignInResult.NotAllowed;
var logMessage = new StringBuilder();
var logger = MockHelpers.MockILogger(logMessage);
result.Log(logger.Object, "Operation");
Assert.Equal("Operation : Result : NotAllowed", logMessage.ToString());
}
[Fact]
public void VerifyLogRequiresTwoFactor()
{
var result = SignInResult.TwoFactorRequired;
var logMessage = new StringBuilder();
var logger = MockHelpers.MockILogger(logMessage);
result.Log(logger.Object, "Operation");
Assert.Equal("Operation : Result : RequiresTwoFactor", logMessage.ToString());
}
[Fact]
public void VerifyLogRequiresFailed()
{
var result = SignInResult.Failed;
var logMessage = new StringBuilder();
var logger = MockHelpers.MockILogger(logMessage);
result.Log(logger.Object, "Operation");
Assert.Equal("Operation : Result : Failed", logMessage.ToString());
}
}
}

View File

@ -1,16 +1,15 @@
// 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.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
using Microsoft.Framework.OptionsModel;
using Moq;
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;
using Moq;
using Xunit;
namespace Microsoft.AspNet.Identity.Test
@ -21,10 +20,7 @@ namespace Microsoft.AspNet.Identity.Test
{
public IUserStore<TestUser> StorePublic { get { return Store; } }
public TestManager(IUserStore<TestUser> store, IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<TestUser> passwordHasher, IEnumerable<IUserValidator<TestUser>> userValidator,
IEnumerable<IPasswordValidator<TestUser>> passwordValidator)
: base(store, optionsAccessor, passwordHasher, userValidator, passwordValidator, null, null, null) { }
public TestManager(IUserStore<TestUser> store) : base(store) { }
}
[Fact]
@ -36,8 +32,6 @@ namespace Microsoft.AspNet.Identity.Test
services.AddIdentity<TestUser, IdentityRole>();
var manager = services.BuildServiceProvider().GetRequiredService<TestManager>();
Assert.NotNull(manager.PasswordHasher);
Assert.Equal(1, manager.PasswordValidators.Count);
Assert.Equal(1, manager.UserValidators.Count);
Assert.NotNull(manager.StorePublic);
Assert.NotNull(manager.Options);
}
@ -48,7 +42,30 @@ namespace Microsoft.AspNet.Identity.Test
// Setup
var store = new Mock<IUserStore<TestUser>>();
var user = new TestUser { UserName = "Foo" };
store.Setup(s => s.CreateAsync(user, CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
store.Setup(s => s.CreateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable();
store.Setup(s => s.GetUserNameAsync(user, CancellationToken.None)).Returns(Task.FromResult(user.UserName)).Verifiable();
store.Setup(s => s.SetNormalizedUserNameAsync(user, user.UserName.ToUpperInvariant(), CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
// Act
var result = await userManager.CreateAsync(user);
// Assert
Assert.True(result.Succeeded);
store.VerifyAll();
}
[Fact]
public async Task CreateCallsUpdateEmailStore()
{
// Setup
var store = new Mock<IUserEmailStore<TestUser>>();
var user = new TestUser { UserName = "Foo", Email = "Foo@foo.com" };
store.Setup(s => s.CreateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable();
store.Setup(s => s.GetUserNameAsync(user, CancellationToken.None)).Returns(Task.FromResult(user.UserName)).Verifiable();
store.Setup(s => s.GetEmailAsync(user, CancellationToken.None)).Returns(Task.FromResult(user.Email)).Verifiable();
store.Setup(s => s.SetNormalizedEmailAsync(user, user.Email.ToUpperInvariant(), CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
store.Setup(s => s.SetNormalizedUserNameAsync(user, user.UserName.ToUpperInvariant(), CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
// Act
@ -65,8 +82,8 @@ namespace Microsoft.AspNet.Identity.Test
// Setup
var store = new Mock<IUserStore<TestUser>>();
var user = new TestUser { UserName = "Foo" };
store.Setup(s => s.DeleteAsync(user, CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
store.Setup(s => s.DeleteAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable();
var userManager = MockHelpers.TestUserManager(store.Object);
// Act
var result = await userManager.DeleteAsync(user);
@ -82,8 +99,31 @@ namespace Microsoft.AspNet.Identity.Test
// Setup
var store = new Mock<IUserStore<TestUser>>();
var user = new TestUser { UserName = "Foo" };
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
store.Setup(s => s.GetUserNameAsync(user, CancellationToken.None)).Returns(Task.FromResult(user.UserName)).Verifiable();
store.Setup(s => s.SetNormalizedUserNameAsync(user, user.UserName.ToUpperInvariant(), CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable();
var userManager = MockHelpers.TestUserManager(store.Object);
// Act
var result = await userManager.UpdateAsync(user);
// Assert
Assert.True(result.Succeeded);
store.VerifyAll();
}
[Fact]
public async Task UpdateWillUpdateNormalizedEmail()
{
// Setup
var store = new Mock<IUserEmailStore<TestUser>>();
var user = new TestUser { UserName = "Foo", Email = "email" };
store.Setup(s => s.GetUserNameAsync(user, CancellationToken.None)).Returns(Task.FromResult(user.UserName)).Verifiable();
store.Setup(s => s.GetEmailAsync(user, CancellationToken.None)).Returns(Task.FromResult(user.Email)).Verifiable();
store.Setup(s => s.SetNormalizedUserNameAsync(user, user.UserName.ToUpperInvariant(), CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
store.Setup(s => s.SetNormalizedEmailAsync(user, user.Email.ToUpperInvariant(), CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable();
var userManager = MockHelpers.TestUserManager(store.Object);
// Act
var result = await userManager.UpdateAsync(user);
@ -99,8 +139,11 @@ namespace Microsoft.AspNet.Identity.Test
// Setup
var store = new Mock<IUserStore<TestUser>>();
var user = new TestUser();
store.Setup(s => s.SetUserNameAsync(user, It.IsAny<string>(), CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
store.Setup(s => s.SetUserNameAsync(user, "foo", CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
store.Setup(s => s.GetUserNameAsync(user, CancellationToken.None)).Returns(Task.FromResult("foo")).Verifiable();
store.Setup(s => s.SetNormalizedUserNameAsync(user, "FOO", CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).Returns(Task.FromResult(IdentityResult.Success)).Verifiable();
var userManager = MockHelpers.TestUserManager(store.Object);
// Act
var result = await userManager.SetUserNameAsync(user, "foo");
@ -132,7 +175,7 @@ namespace Microsoft.AspNet.Identity.Test
{
// Setup
var store = new Mock<IUserStore<TestUser>>();
var user = new TestUser {UserName="Foo"};
var user = new TestUser { UserName = "Foo" };
store.Setup(s => s.FindByNameAsync(user.UserName.ToUpperInvariant(), CancellationToken.None)).Returns(Task.FromResult(user)).Verifiable();
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
@ -149,10 +192,10 @@ namespace Microsoft.AspNet.Identity.Test
{
// Setup
var store = new Mock<IUserStore<TestUser>>();
var user = new TestUser {UserName="Foo"};
var user = new TestUser { UserName = "Foo" };
store.Setup(s => s.FindByNameAsync(user.UserName, CancellationToken.None)).Returns(Task.FromResult(user)).Verifiable();
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
userManager.UserNameNormalizer = null;
var userManager = MockHelpers.TestUserManager(store.Object);
userManager.KeyNormalizer = null;
// Act
var result = await userManager.FindByNameAsync(user.UserName);
@ -162,13 +205,48 @@ namespace Microsoft.AspNet.Identity.Test
store.VerifyAll();
}
[Fact]
public async Task FindByEmailCallsStoreWithNormalizedEmail()
{
// Setup
var store = new Mock<IUserEmailStore<TestUser>>();
var user = new TestUser { Email = "Foo" };
store.Setup(s => s.FindByEmailAsync(user.Email.ToUpperInvariant(), CancellationToken.None)).Returns(Task.FromResult(user)).Verifiable();
var userManager = MockHelpers.TestUserManager(store.Object);
// Act
var result = await userManager.FindByEmailAsync(user.Email);
// Assert
Assert.Equal(user, result);
store.VerifyAll();
}
[Fact]
public async Task CanFindByEmailCallsStoreWithoutNormalizedEmail()
{
// Setup
var store = new Mock<IUserEmailStore<TestUser>>();
var user = new TestUser { Email = "Foo" };
store.Setup(s => s.FindByEmailAsync(user.Email, CancellationToken.None)).Returns(Task.FromResult(user)).Verifiable();
var userManager = MockHelpers.TestUserManager(store.Object);
userManager.KeyNormalizer = null;
// Act
var result = await userManager.FindByEmailAsync(user.Email);
// Assert
Assert.Equal(user, result);
store.VerifyAll();
}
[Fact]
public async Task AddToRolesCallsStore()
{
// Setup
var store = new Mock<IUserRoleStore<TestUser>>();
var user = new TestUser { UserName = "Foo" };
var roles = new string[] {"A", "B", "C"};
var roles = new string[] { "A", "B", "C" };
store.Setup(s => s.AddToRoleAsync(user, "A", CancellationToken.None))
.Returns(Task.FromResult(0))
.Verifiable();
@ -178,7 +256,7 @@ namespace Microsoft.AspNet.Identity.Test
store.Setup(s => s.AddToRoleAsync(user, "C", CancellationToken.None))
.Returns(Task.FromResult(0))
.Verifiable();
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable();
store.Setup(s => s.GetRolesAsync(user, CancellationToken.None)).ReturnsAsync(new List<string>()).Verifiable();
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
@ -207,7 +285,7 @@ namespace Microsoft.AspNet.Identity.Test
var result = await userManager.AddToRolesAsync(user, roles);
// Assert
IdentityResultAssert.IsFailure(result, "User already in role.");
IdentityResultAssert.IsFailure(result, IdentityErrorDescriber.Default.UserAlreadyInRole("B"));
store.VerifyAll();
}
@ -227,7 +305,7 @@ namespace Microsoft.AspNet.Identity.Test
store.Setup(s => s.RemoveFromRoleAsync(user, "C", CancellationToken.None))
.Returns(Task.FromResult(0))
.Verifiable();
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable();
store.Setup(s => s.IsInRoleAsync(user, "A", CancellationToken.None))
.Returns(Task.FromResult(true))
.Verifiable();
@ -269,7 +347,7 @@ namespace Microsoft.AspNet.Identity.Test
var result = await userManager.RemoveFromRolesAsync(user, roles);
// Assert
IdentityResultAssert.IsFailure(result, "User is not in role.");
IdentityResultAssert.IsFailure(result, IdentityErrorDescriber.Default.UserNotInRole("B"));
store.VerifyAll();
}
@ -283,8 +361,8 @@ namespace Microsoft.AspNet.Identity.Test
store.Setup(s => s.AddClaimsAsync(user, claims, CancellationToken.None))
.Returns(Task.FromResult(0))
.Verifiable();
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable();
var userManager = MockHelpers.TestUserManager(store.Object);
// Act
var result = await userManager.AddClaimsAsync(user, claims);
@ -304,8 +382,8 @@ namespace Microsoft.AspNet.Identity.Test
store.Setup(s => s.AddClaimsAsync(user, It.IsAny<IEnumerable<Claim>>(), CancellationToken.None))
.Returns(Task.FromResult(0))
.Verifiable();
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable();
var userManager = MockHelpers.TestUserManager(store.Object);
// Act
var result = await userManager.AddClaimAsync(user, claim);
@ -326,7 +404,8 @@ namespace Microsoft.AspNet.Identity.Test
store.Setup(s => s.ReplaceClaimAsync(user, It.IsAny<Claim>(), It.IsAny<Claim>(), CancellationToken.None))
.Returns(Task.FromResult(0))
.Verifiable();
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).Returns(Task.FromResult(IdentityResult.Success)).Verifiable();
var userManager = MockHelpers.TestUserManager(store.Object);
// Act
var result = await userManager.ReplaceClaimAsync(user, claim, newClaim);
@ -336,6 +415,34 @@ namespace Microsoft.AspNet.Identity.Test
store.VerifyAll();
}
[Fact]
public async Task CheckPasswordWillRehashPasswordWhenNeeded()
{
// Setup
var store = new Mock<IUserPasswordStore<TestUser>>();
var hasher = new Mock<IPasswordHasher<TestUser>>();
var user = new TestUser { UserName = "Foo" };
var pwd = "password";
var hashed = "hashed";
var rehashed = "rehashed";
store.Setup(s => s.GetPasswordHashAsync(user, CancellationToken.None))
.ReturnsAsync(hashed)
.Verifiable();
hasher.Setup(s => s.VerifyHashedPassword(user, hashed, pwd)).Returns(PasswordVerificationResult.SuccessRehashNeeded).Verifiable();
hasher.Setup(s => s.HashPassword(user, pwd)).Returns(rehashed).Verifiable();
var userManager = MockHelpers.TestUserManager(store.Object);
userManager.PasswordHasher = hasher.Object;
// Act
var result = await userManager.CheckPasswordAsync(user, pwd);
// Assert
Assert.True(result);
store.VerifyAll();
hasher.VerifyAll();
}
[Fact]
public async Task RemoveClaimsCallsStore()
{
@ -346,8 +453,8 @@ namespace Microsoft.AspNet.Identity.Test
store.Setup(s => s.RemoveClaimsAsync(user, claims, CancellationToken.None))
.Returns(Task.FromResult(0))
.Verifiable();
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable();
var userManager = MockHelpers.TestUserManager(store.Object);
// Act
var result = await userManager.RemoveClaimsAsync(user, claims);
@ -367,7 +474,7 @@ namespace Microsoft.AspNet.Identity.Test
store.Setup(s => s.RemoveClaimsAsync(user, It.IsAny<IEnumerable<Claim>>(), CancellationToken.None))
.Returns(Task.FromResult(0))
.Verifiable();
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).ReturnsAsync(IdentityResult.Success).Verifiable();
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
// Act
@ -533,17 +640,11 @@ namespace Microsoft.AspNet.Identity.Test
public async Task ManagerPublicNullChecks()
{
var store = new NotImplementedStore();
var optionsAccessor = new OptionsManager<IdentityOptions>(null);
var passwordHasher = new PasswordHasher<TestUser>(new PasswordHasherOptionsAccessor());
Assert.Throws<ArgumentNullException>("store",
() => new UserManager<TestUser>(null, null, null, null, null, null, null, null));
Assert.Throws<ArgumentNullException>("optionsAccessor",
() => new UserManager<TestUser>(store, null, null, null, null, null, null, null));
Assert.Throws<ArgumentNullException>("passwordHasher",
() => new UserManager<TestUser>(store, optionsAccessor, null, null, null, null, null, null));
() => new UserManager<TestUser>(null, null));
var manager = new UserManager<TestUser>(store, optionsAccessor, passwordHasher, null, null, null, null, null);
var manager = new UserManager<TestUser>(store);
Assert.Throws<ArgumentNullException>("value", () => manager.PasswordHasher = null);
Assert.Throws<ArgumentNullException>("value", () => manager.Options = null);
@ -581,7 +682,7 @@ namespace Microsoft.AspNet.Identity.Test
await Assert.ThrowsAsync<ArgumentNullException>("user",
async () => await manager.AddClaimAsync(null, new Claim("a", "b")));
await Assert.ThrowsAsync<ArgumentNullException>("user",
async () => await manager.AddLoginAsync(null, new UserLoginInfo("","","")));
async () => await manager.AddLoginAsync(null, new UserLoginInfo("", "", "")));
await Assert.ThrowsAsync<ArgumentNullException>("user",
async () => await manager.AddPasswordAsync(null, null));
await Assert.ThrowsAsync<ArgumentNullException>("user",
@ -714,7 +815,7 @@ namespace Microsoft.AspNet.Identity.Test
private class BadPasswordValidator<TUser> : IPasswordValidator<TUser> where TUser : class
{
public const string ErrorMessage = "I'm Bad.";
public static readonly IdentityError ErrorMessage = new IdentityError { Description = "I'm Bad." };
public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user, string password, CancellationToken cancellationToken = default(CancellationToken))
{
@ -842,19 +943,19 @@ namespace Microsoft.AspNet.Identity.Test
return Task.FromResult(0);
}
public Task CreateAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> CreateAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(0);
return Task.FromResult(IdentityResult.Success);
}
public Task UpdateAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> UpdateAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(0);
return Task.FromResult(IdentityResult.Success);
}
public Task DeleteAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
public Task<IdentityResult> DeleteAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(0);
return Task.FromResult(IdentityResult.Success);
}
public Task<TestUser> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
@ -961,6 +1062,26 @@ namespace Microsoft.AspNet.Identity.Test
{
return Task.FromResult(0);
}
public Task<IList<TestUser>> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult<IList<TestUser>>(new List<TestUser>());
}
public Task<IList<TestUser>> GetUsersInRoleAsync(string roleName, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult<IList<TestUser>>(new List<TestUser>());
}
public Task<string> GetNormalizedEmailAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult("");
}
public Task SetNormalizedEmailAsync(TestUser user, string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(0);
}
}
private class NoOpTokenProvider : IUserTokenProvider<TestUser>
@ -1119,21 +1240,6 @@ namespace Microsoft.AspNet.Identity.Test
throw new NotImplementedException();
}
public Task CreateAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task UpdateAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task DeleteAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task<TestUser> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
@ -1218,6 +1324,87 @@ namespace Microsoft.AspNet.Identity.Test
{
throw new NotImplementedException();
}
public Task<IList<TestUser>> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task<IList<TestUser>> GetUsersInRoleAsync(string roleName, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
Task<IdentityResult> IUserStore<TestUser>.CreateAsync(TestUser user, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
Task<IdentityResult> IUserStore<TestUser>.UpdateAsync(TestUser user, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
Task<IdentityResult> IUserStore<TestUser>.DeleteAsync(TestUser user, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<string> GetNormalizedEmailAsync(TestUser user, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task SetNormalizedEmailAsync(TestUser user, string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
}
[Fact]
public async Task CanCustomizeUserValidatorErrors()
{
var services = new ServiceCollection();
var store = new Mock<IUserEmailStore<TestUser>>();
var describer = new TestErrorDescriber();
services.AddInstance<IdentityErrorDescriber>(describer)
.AddInstance<IUserStore<TestUser>>(store.Object)
.AddIdentity<TestUser, IdentityRole>();
var manager = services.BuildServiceProvider().GetRequiredService<UserManager<TestUser>>();
manager.Options.User.RequireUniqueEmail = true;
var user = new TestUser() { UserName = "dupeEmail", Email = "dupe@email.com" };
var user2 = new TestUser() { UserName = "dupeEmail2", Email = "dupe@email.com" };
store.Setup(s => s.FindByEmailAsync("DUPE@EMAIL.COM", CancellationToken.None))
.Returns(Task.FromResult(user2))
.Verifiable();
store.Setup(s => s.GetUserIdAsync(user2, CancellationToken.None))
.Returns(Task.FromResult(user2.Id))
.Verifiable();
store.Setup(s => s.GetUserNameAsync(user, CancellationToken.None))
.Returns(Task.FromResult(user.UserName))
.Verifiable();
store.Setup(s => s.GetEmailAsync(user, CancellationToken.None))
.Returns(Task.FromResult(user.Email))
.Verifiable();
Assert.Same(describer, manager.ErrorDescriber);
IdentityResultAssert.IsFailure(await manager.CreateAsync(user), describer.DuplicateEmail(user.Email));
store.VerifyAll();
}
public class TestErrorDescriber : IdentityErrorDescriber
{
public static string Code = "Error";
public static string FormatError = "FormatError {0}";
public override IdentityError DuplicateEmail(string email)
{
return new IdentityError { Code = Code, Description = string.Format(FormatError, email) };
}
}
}
}

View File

@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Identity.Test
var result = await validator.ValidateAsync(manager, user);
// Assert
IdentityResultAssert.IsFailure(result, "UserName cannot be null or empty.");
IdentityResultAssert.IsFailure(result, IdentityErrorDescriber.Default.InvalidUserName(input));
}
[Theory]

View File

@ -3,13 +3,12 @@
"Microsoft.AspNet.Hosting" : "1.0.0-*",
"Microsoft.AspNet.Http" : "1.0.0-*",
"Microsoft.AspNet.Identity" : "3.0.0-*",
"Microsoft.AspNet.PipelineCore" : "1.0.0-*",
"Microsoft.AspNet.RequestContainer" : "1.0.0-*",
"Microsoft.AspNet.Testing" : "1.0.0-*",
"Microsoft.Framework.ConfigurationModel" : "1.0.0-*",
"Microsoft.Framework.DependencyInjection" : "1.0.0-*",
"Microsoft.Framework.OptionsModel" : "1.0.0-*",
"Xunit.KRunner": "1.0.0-*"
"xunit.runner.kre": "1.0.0-*"
},
"code": "**\\*.cs;..\\Shared\\*.cs",
"frameworks": {
@ -20,6 +19,6 @@
}
},
"commands": {
"test": "Xunit.KRunner"
"test": "xunit.runner.kre"
}
}

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using Microsoft.Framework.Logging;
using Xunit;
namespace Microsoft.AspNet.Identity.Test
@ -24,7 +25,79 @@ namespace Microsoft.AspNet.Identity.Test
{
Assert.NotNull(result);
Assert.False(result.Succeeded);
Assert.Equal(error, result.Errors.First());
Assert.Equal(error, result.Errors.First().Description);
}
public static void IsFailure(IdentityResult result, IdentityError error)
{
Assert.NotNull(result);
Assert.False(result.Succeeded);
Assert.Equal(error.Description, result.Errors.First().Description);
Assert.Equal(error.Code, result.Errors.First().Code);
}
public static void VerifyUserManagerFailureLog(ILogger logger, string methodName, string userId, params IdentityError[] errors)
{
VerifyFailureLog(logger, "UserManager", methodName, userId, "user", errors);
}
public static void VerifyRoleManagerFailureLog(ILogger logger, string methodName, string roleId, params IdentityError[] errors)
{
VerifyFailureLog(logger, "RoleManager", methodName, roleId, "role", errors);
}
public static void VerifyUserManagerSuccessLog(ILogger logger, string methodName, string userId)
{
VerifySuccessLog(logger, "UserManager", methodName, userId, "user");
}
public static void VerifyRoleManagerSuccessLog(ILogger logger, string methodName, string roleId)
{
VerifySuccessLog(logger, "RoleManager", methodName, roleId, "role");
}
private static void VerifySuccessLog(ILogger logger, string className, string methodName, string id, string userOrRole = "user")
{
TestLogger testlogger = logger as TestLogger;
if (testlogger != null)
{
string expected = string.Format("{0} for {1}: {2} : Success", methodName, userOrRole, id);
Assert.True(testlogger.LogMessages.Contains(expected));
}
else
{
Assert.True(true, "No logger registered");
}
}
public static void VerifyLogMessage(ILogger logger, string expectedLog)
{
TestLogger testlogger = logger as TestLogger;
if (testlogger != null)
{
Assert.True(testlogger.LogMessages.Contains(expectedLog));
}
else
{
Assert.True(true, "No logger registered");
}
}
private static void VerifyFailureLog(ILogger logger, string className, string methodName, string userId, string userOrRole = "user", params IdentityError[] errors)
{
TestLogger testlogger = logger as TestLogger;
if (testlogger != null)
{
errors = errors ?? new IdentityError[] { new IdentityError() };
string expected = string.Format("{0} for {1}: {2} : Failed : {3}", methodName, userOrRole, userId, string.Join(",", errors.Select(x => x.Code).ToList()));
Assert.True(testlogger.LogMessages.Contains(expected));
}
else
{
Assert.True(true, "No logger registered");
}
}
}
}

View File

@ -1,59 +1,86 @@
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.Framework.OptionsModel;
using Microsoft.Framework.Logging;
using Moq;
using System.Text;
namespace Microsoft.AspNet.Identity.Test
{
public static class MockHelpers
{
public static StringBuilder LogMessage = new StringBuilder();
public static Mock<UserManager<TUser>> MockUserManager<TUser>() where TUser : class
{
var store = new Mock<IUserStore<TUser>>();
var options = new OptionsManager<IdentityOptions>(null);
var userValidators = new List<IUserValidator<TUser>>();
userValidators.Add(new UserValidator<TUser>());
var pwdValidators = new List<IPasswordValidator<TUser>>();
pwdValidators.Add(new PasswordValidator<TUser>());
return new Mock<UserManager<TUser>>(
store.Object,
options,
new PasswordHasher<TUser>(new PasswordHasherOptionsAccessor()),
userValidators,
pwdValidators,
new UpperInvariantUserNameNormalizer(),
new List<IUserTokenProvider<TUser>>(),
new List<IIdentityMessageProvider>());
var mgr = new Mock<UserManager<TUser>>(store.Object, null, null, null, null, null, null, null, null, null);
mgr.Object.UserValidators.Add(new UserValidator<TUser>());
mgr.Object.PasswordValidators.Add(new PasswordValidator<TUser>());
return mgr;
}
public static Mock<RoleManager<TRole>> MockRoleManager<TRole>() where TRole : class
public static Mock<RoleManager<TRole>> MockRoleManager<TRole>(IRoleStore<TRole> store = null) where TRole : class
{
var store = new Mock<IRoleStore<TRole>>();
store = store ?? new Mock<IRoleStore<TRole>>().Object;
var roles = new List<IRoleValidator<TRole>>();
roles.Add(new RoleValidator<TRole>());
return new Mock<RoleManager<TRole>>(store.Object, roles);
return new Mock<RoleManager<TRole>>(store, roles, null, null,null);
}
public static UserManager<TUser> TestUserManager<TUser>() where TUser : class
public static Mock<ILogger> MockILogger(StringBuilder logStore = null)
{
return TestUserManager(new Mock<IUserStore<TUser>>().Object);
logStore = logStore ?? LogMessage;
var logger = new Mock<ILogger>();
logger.Setup(x => x.Write(It.IsAny<LogLevel>(), It.IsAny<int>(), It.IsAny<object>(),
It.IsAny<Exception>(), It.IsAny<Func<object, Exception, string>>()))
.Callback((LogLevel logLevel, int eventId, object state, Exception exception, Func<object, Exception, string> formatter) =>
{ logStore.Append(state.ToString()); });
logger.Setup(x => x.IsEnabled(LogLevel.Information)).Returns(true);
logger.Setup(x => x.IsEnabled(LogLevel.Warning)).Returns(true);
return logger;
}
public static UserManager<TUser> TestUserManager<TUser>(IUserStore<TUser> store) where TUser : class
public static Mock<ILoggerFactory> MockILoggerFactory(ILogger logger = null)
{
var options = new OptionsManager<IdentityOptions>(null);
var validator = new Mock<UserValidator<TUser>>();
var userManager = new UserManager<TUser>(store, options, new PasswordHasher<TUser>(new PasswordHasherOptionsAccessor()),
null, null, new UpperInvariantUserNameNormalizer(), null, null);
logger = logger ?? MockILogger().Object;
var loggerFactory = new Mock<ILoggerFactory>();
loggerFactory.Setup(x => x.Create(It.IsAny<string>())).Returns(logger);
return loggerFactory;
}
public static UserManager<TUser> UserManagerWithMockLogger<TUser>(ILoggerFactory loggerFactory = null) where TUser : class
{
var userstore = new Mock<IUserStore<TUser>>().Object;
var userManager = new UserManager<TUser>(userstore, loggerFactory: loggerFactory ?? MockILoggerFactory().Object);
return userManager;
}
public static UserManager<TUser> TestUserManager<TUser>(IUserStore<TUser> store = null) where TUser : class
{
store = store ?? new Mock<IUserStore<TUser>>().Object;
var validator = new Mock<IUserValidator<TUser>>();
var userManager = new UserManager<TUser>(store);
userManager.UserValidators.Add(validator.Object);
userManager.PasswordValidators.Add(new PasswordValidator<TUser>());
validator.Setup(v => v.ValidateAsync(userManager, It.IsAny<TUser>(), CancellationToken.None))
.Returns(Task.FromResult(IdentityResult.Success)).Verifiable();
return userManager;
}
public static RoleManager<TRole> TestRoleManager<TRole>(IRoleStore<TRole> store = null) where TRole : class
{
store = store ?? new Mock<IRoleStore<TRole>>().Object;
var roles = new List<IRoleValidator<TRole>>();
roles.Add(new RoleValidator<TRole>());
return new RoleManager<TRole>(store, roles);
}
}
}

View File

@ -30,7 +30,7 @@ namespace Microsoft.AspNet.Identity.Test
{
int priority = 0;
foreach (IAttributeInfo attr in testCase.Method.GetCustomAttributes((typeof(TestPriorityAttribute))))
foreach (IAttributeInfo attr in testCase.TestMethod.Method.GetCustomAttributes((typeof(TestPriorityAttribute))))
priority = attr.GetNamedArgument<int>("Priority");
GetOrCreate(sortedMethods, priority).Add(testCase);
@ -38,7 +38,7 @@ namespace Microsoft.AspNet.Identity.Test
foreach (var list in sortedMethods.Keys.Select(priority => sortedMethods[priority]))
{
list.Sort((x, y) => StringComparer.OrdinalIgnoreCase.Compare(x.Method.Name, y.Method.Name));
list.Sort((x, y) => StringComparer.OrdinalIgnoreCase.Compare(x.TestMethod.Method.Name, y.TestMethod.Method.Name));
foreach (XunitTestCase testCase in list)
yield return testCase;
}

29
test/Shared/TestLogger.cs Normal file
View File

@ -0,0 +1,29 @@
// 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 Microsoft.Framework.Logging;
namespace Microsoft.AspNet.Identity.Test
{
public class TestLogger : ILogger
{
public IList<string> LogMessages { get; private set; } = new List<string>();
public IDisposable BeginScope(object state)
{
throw new NotImplementedException();
}
public bool IsEnabled(LogLevel logLevel)
{
return true;
}
public void Write(LogLevel logLevel, int eventId, object state, Exception exception, Func<object, Exception, string> formatter)
{
LogMessages.Add(state.ToString());
}
}
}

View File

@ -0,0 +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.Framework.Logging;
namespace Microsoft.AspNet.Identity.Test
{
public class TestLoggerFactory : ILoggerFactory
{
public void AddProvider(ILoggerProvider provider)
{
}
public ILogger Create(string name)
{
return new TestLogger();
}
}
}

View File

@ -14,5 +14,6 @@ namespace Microsoft.AspNet.Identity.Test
public string Id { get; private set; }
public string UserName { get; set; }
public string Email { get; set; }
}
}

View File

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using Microsoft.AspNet.Testing;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
using Microsoft.Framework.Logging;
using Xunit;
namespace Microsoft.AspNet.Identity.Test
@ -20,17 +21,25 @@ namespace Microsoft.AspNet.Identity.Test
where TRole : IdentityRole, new()
{ }
public abstract class UserManagerTestBase<TUser, TRole, TKey>
where TUser: IdentityUser<TKey>, new()
where TRole: IdentityRole<TKey>, new()
public abstract class UserManagerTestBase<TUser, TRole, TKey>
where TUser : IdentityUser<TKey>, new()
where TRole : IdentityRole<TKey>, new()
where TKey : IEquatable<TKey>
{
protected TestLoggerFactory loggerFactory;
public UserManagerTestBase()
{
loggerFactory = new TestLoggerFactory();
}
protected virtual void SetupIdentityServices(IServiceCollection services, object context = null)
{
services.AddHosting();
services.AddIdentity<TUser, TRole>().AddDefaultTokenProviders();
AddUserStore(services, context);
AddRoleStore(services, context);
services.AddInstance<ILoggerFactory>(loggerFactory);
services.ConfigureIdentity(options =>
{
options.Password.RequireDigit = false;
@ -74,11 +83,13 @@ namespace Microsoft.AspNet.Identity.Test
protected abstract void AddUserStore(IServiceCollection services, object context = null);
protected abstract void AddRoleStore(IServiceCollection services, object context = null);
protected TUser CreateTestUser(string namePrefix = "") {
protected TUser CreateTestUser(string namePrefix = "")
{
return new TUser() { UserName = namePrefix + Guid.NewGuid().ToString() };
}
protected TRole CreateRole(string namePrefix = "") {
protected TRole CreateRole(string namePrefix = "")
{
return new TRole() { Name = namePrefix + Guid.NewGuid().ToString() };
}
@ -88,7 +99,11 @@ namespace Microsoft.AspNet.Identity.Test
var manager = CreateManager();
var user = CreateTestUser();
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "CreateAsync", user.Id.ToString());
IdentityResultAssert.IsSuccess(await manager.DeleteAsync(user));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "DeleteAsync", user.Id.ToString());
Assert.Null(await manager.FindByIdAsync(user.Id.ToString()));
}
@ -103,6 +118,7 @@ namespace Microsoft.AspNet.Identity.Test
Assert.Null(await manager.FindByNameAsync(newName));
user.UserName = newName;
IdentityResultAssert.IsSuccess(await manager.UpdateAsync(user));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "UpdateAsync", user.Id.ToString());
Assert.NotNull(await manager.FindByNameAsync(newName));
Assert.Null(await manager.FindByNameAsync(name));
}
@ -126,9 +142,14 @@ namespace Microsoft.AspNet.Identity.Test
var user = new TUser() { UserName = "UpdatePassword" };
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password"));
Assert.True(await manager.CheckPasswordAsync(user, "password"));
string expectedLog = string.Format("{0} for user: {1} : {2}", "CheckPasswordAsync", user.Id.ToString(), true.ToString());
IdentityResultAssert.VerifyLogMessage(manager.Logger, expectedLog);
user.PasswordHash = manager.PasswordHasher.HashPassword(user, "New");
IdentityResultAssert.IsSuccess(await manager.UpdateAsync(user));
Assert.False(await manager.CheckPasswordAsync(user, "password"));
expectedLog = string.Format("{0} for user: {1} : {2}", "CheckPasswordAsync", user.Id.ToString(), false.ToString());
IdentityResultAssert.VerifyLogMessage(manager.Logger, expectedLog);
Assert.True(await manager.CheckPasswordAsync(user, "New"));
}
@ -160,6 +181,7 @@ namespace Microsoft.AspNet.Identity.Test
manager.UserValidators.Clear();
manager.UserValidators.Add(new AlwaysBadValidator());
IdentityResultAssert.IsFailure(await manager.UpdateAsync(user), AlwaysBadValidator.ErrorMessage);
IdentityResultAssert.VerifyUserManagerFailureLog(manager.Logger, "UpdateAsync", user.Id.ToString(), AlwaysBadValidator.ErrorMessage);
}
[Fact]
@ -182,7 +204,7 @@ namespace Microsoft.AspNet.Identity.Test
var manager = CreateManager();
var user = CreateTestUser();
manager.Options.User.RequireUniqueEmail = true;
IdentityResultAssert.IsFailure(await manager.CreateAsync(user), "Email cannot be null or empty.");
IdentityResultAssert.IsFailure(await manager.CreateAsync(user), IdentityErrorDescriber.Default.InvalidEmail(email));
}
#if ASPNET50
@ -194,7 +216,7 @@ namespace Microsoft.AspNet.Identity.Test
var manager = CreateManager();
var user = new TUser() { UserName = "UpdateBlocked", Email = email };
manager.Options.User.RequireUniqueEmail = true;
IdentityResultAssert.IsFailure(await manager.CreateAsync(user), "Email '" + email + "' is invalid.");
IdentityResultAssert.IsFailure(await manager.CreateAsync(user), IdentityErrorDescriber.Default.InvalidEmail(email));
}
#endif
@ -208,6 +230,7 @@ namespace Microsoft.AspNet.Identity.Test
manager.PasswordValidators.Add(new AlwaysBadValidator());
IdentityResultAssert.IsFailure(await manager.AddPasswordAsync(user, "password"),
AlwaysBadValidator.ErrorMessage);
IdentityResultAssert.VerifyUserManagerFailureLog(manager.Logger, "AddPasswordAsync", user.Id.ToString(), AlwaysBadValidator.ErrorMessage);
}
[Fact]
@ -234,6 +257,17 @@ namespace Microsoft.AspNet.Identity.Test
manager.PasswordValidators.Add(new AlwaysBadValidator());
IdentityResultAssert.IsFailure(await manager.ChangePasswordAsync(user, "password", "new"),
AlwaysBadValidator.ErrorMessage);
IdentityResultAssert.VerifyUserManagerFailureLog(manager.Logger, "ChangePasswordAsync", user.Id.ToString(), AlwaysBadValidator.ErrorMessage);
}
[Fact]
public async Task PasswordValidatorCanBlockCreateUser()
{
var manager = CreateManager();
var user = CreateTestUser();
manager.PasswordValidators.Clear();
manager.PasswordValidators.Add(new AlwaysBadValidator());
IdentityResultAssert.IsFailure(await manager.CreateAsync(user, "password"), AlwaysBadValidator.ErrorMessage);
}
[Fact]
@ -260,6 +294,7 @@ namespace Microsoft.AspNet.Identity.Test
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
user = await manager.FindByNameAsync(user.UserName);
IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, new UserLoginInfo(provider, providerKey, display)));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "AddLoginAsync", user.Id.ToString());
var logins = await manager.GetLoginsAsync(user);
Assert.NotNull(logins);
Assert.Equal(1, logins.Count());
@ -278,6 +313,7 @@ namespace Microsoft.AspNet.Identity.Test
IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login));
Assert.False(await manager.HasPasswordAsync(user));
IdentityResultAssert.IsSuccess(await manager.AddPasswordAsync(user, "password"));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "AddPasswordAsync", user.Id.ToString());
Assert.True(await manager.HasPasswordAsync(user));
var logins = await manager.GetLoginsAsync(user);
Assert.NotNull(logins);
@ -295,6 +331,7 @@ namespace Microsoft.AspNet.Identity.Test
Assert.True(await manager.HasPasswordAsync(user));
IdentityResultAssert.IsFailure(await manager.AddPasswordAsync(user, "password"),
"User already has a password set.");
IdentityResultAssert.VerifyUserManagerFailureLog(manager.Logger, "AddPasswordAsync", user.Id.ToString(), IdentityErrorDescriber.Default.UserAlreadyHasPassword());
}
[Fact]
@ -316,6 +353,7 @@ namespace Microsoft.AspNet.Identity.Test
Assert.Equal(login.ProviderDisplayName, logins.Last().ProviderDisplayName);
var stamp = user.SecurityStamp;
IdentityResultAssert.IsSuccess(await manager.RemoveLoginAsync(user, login.LoginProvider, login.ProviderKey));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "RemoveLoginAsync", user.Id.ToString());
Assert.Null(await manager.FindByLoginAsync(login.LoginProvider, login.ProviderKey));
logins = await manager.GetLoginsAsync(user);
Assert.NotNull(logins);
@ -332,6 +370,7 @@ namespace Microsoft.AspNet.Identity.Test
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, password));
var stamp = user.SecurityStamp;
IdentityResultAssert.IsSuccess(await manager.RemovePasswordAsync(user));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "RemovePasswordAsync", user.Id.ToString());
var u = await manager.FindByNameAsync(user.UserName);
Assert.NotNull(u);
Assert.Null(u.PasswordHash);
@ -351,6 +390,7 @@ namespace Microsoft.AspNet.Identity.Test
IdentityResultAssert.IsSuccess(await manager.ChangePasswordAsync(user, password, newPassword));
Assert.False(await manager.CheckPasswordAsync(user, password));
Assert.True(await manager.CheckPasswordAsync(user, newPassword));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "ChangePasswordAsync", user.Id.ToString());
Assert.NotEqual(stamp, user.SecurityStamp);
}
@ -365,6 +405,35 @@ namespace Microsoft.AspNet.Identity.Test
{
IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c));
}
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "AddClaimsAsync", user.Id.ToString());
var userClaims = await manager.GetClaimsAsync(user);
Assert.Equal(3, userClaims.Count);
IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[0]));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "RemoveClaimsAsync", user.Id.ToString());
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 RemoveClaimOnlyAffectsUser()
{
var manager = CreateManager();
var user = CreateTestUser();
var user2 = CreateTestUser();
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2));
Claim[] claims = { new Claim("c", "v"), new Claim("c2", "v2"), new Claim("c2", "v3") };
foreach (Claim c in claims)
{
IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, c));
IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user2, c));
}
var userClaims = await manager.GetClaimsAsync(user);
Assert.Equal(3, userClaims.Count);
IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[0]));
@ -376,6 +445,8 @@ namespace Microsoft.AspNet.Identity.Test
IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(user, claims[2]));
userClaims = await manager.GetClaimsAsync(user);
Assert.Equal(0, userClaims.Count);
var userClaims2 = await manager.GetClaimsAsync(user2);
Assert.Equal(3, userClaims2.Count);
}
[Fact]
@ -390,6 +461,7 @@ namespace Microsoft.AspNet.Identity.Test
Claim claim = new Claim("c", "b");
Claim oldClaim = userClaims.FirstOrDefault();
IdentityResultAssert.IsSuccess(await manager.ReplaceClaimAsync(user, oldClaim, claim));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "ReplaceClaimAsync", user.Id.ToString());
var newUserClaims = await manager.GetClaimsAsync(user);
Assert.Equal(1, newUserClaims.Count);
Claim newClaim = newUserClaims.FirstOrDefault();
@ -397,6 +469,35 @@ namespace Microsoft.AspNet.Identity.Test
Assert.Equal(claim.Value, newClaim.Value);
}
[Fact]
public async Task ReplaceUserClaimOnlyAffectsUser()
{
var manager = CreateManager();
var user = CreateTestUser();
var user2 = CreateTestUser();
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2));
IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, new Claim("c", "a")));
IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user2, new Claim("c", "a")));
var userClaims = await manager.GetClaimsAsync(user);
Assert.Equal(1, userClaims.Count);
var userClaims2 = await manager.GetClaimsAsync(user);
Assert.Equal(1, userClaims2.Count);
Claim claim = new Claim("c", "b");
Claim oldClaim = userClaims.FirstOrDefault();
IdentityResultAssert.IsSuccess(await manager.ReplaceClaimAsync(user, oldClaim, claim));
var newUserClaims = await manager.GetClaimsAsync(user);
Assert.Equal(1, newUserClaims.Count);
Claim newClaim = newUserClaims.FirstOrDefault();
Assert.Equal(claim.Type, newClaim.Type);
Assert.Equal(claim.Value, newClaim.Value);
userClaims2 = await manager.GetClaimsAsync(user2);
Assert.Equal(1, userClaims2.Count);
Claim oldClaim2 = userClaims2.FirstOrDefault();
Assert.Equal("c", oldClaim2.Type);
Assert.Equal("a", oldClaim2.Value);
}
[Fact]
public async Task ChangePasswordFallsIfPasswordWrong()
{
@ -405,6 +506,7 @@ namespace Microsoft.AspNet.Identity.Test
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password"));
var result = await manager.ChangePasswordAsync(user, "bogus", "newpassword");
IdentityResultAssert.IsFailure(result, "Incorrect password.");
IdentityResultAssert.VerifyUserManagerFailureLog(manager.Logger, "ChangePasswordAsync", user.Id.ToString(), IdentityErrorDescriber.Default.PasswordMismatch());
}
[Fact]
@ -414,7 +516,7 @@ namespace Microsoft.AspNet.Identity.Test
var user = CreateTestUser();
var user2 = new TUser() { UserName = user.UserName };
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
IdentityResultAssert.IsFailure(await manager.CreateAsync(user2), "Name "+user.UserName+" is already taken.");
IdentityResultAssert.IsFailure(await manager.CreateAsync(user2), IdentityErrorDescriber.Default.DuplicateUserName(user.UserName));
}
[Fact]
@ -440,7 +542,7 @@ namespace Microsoft.AspNet.Identity.Test
user.Email = email;
user2.Email = email;
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
IdentityResultAssert.IsFailure(await manager.CreateAsync(user2), "Email '"+email+"' is already taken.");
IdentityResultAssert.IsFailure(await manager.CreateAsync(user2), IdentityErrorDescriber.Default.DuplicateEmail(user.Email));
}
[Fact]
@ -453,6 +555,7 @@ namespace Microsoft.AspNet.Identity.Test
var stamp = user.SecurityStamp;
Assert.NotNull(stamp);
IdentityResultAssert.IsSuccess(await manager.UpdateSecurityStampAsync(user));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "UpdateSecurityStampAsync", user.Id.ToString());
Assert.NotEqual(stamp, user.SecurityStamp);
}
@ -465,7 +568,8 @@ namespace Microsoft.AspNet.Identity.Test
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.");
IdentityResultAssert.IsFailure(result, IdentityErrorDescriber.Default.LoginAlreadyAssociated());
IdentityResultAssert.VerifyUserManagerFailureLog(manager.Logger, "AddLoginAsync", user.Id.ToString(), IdentityErrorDescriber.Default.LoginAlreadyAssociated());
}
// Email tests
@ -549,7 +653,9 @@ namespace Microsoft.AspNet.Identity.Test
Assert.NotNull(stamp);
var token = await manager.GeneratePasswordResetTokenAsync(user);
Assert.NotNull(token);
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "GeneratePasswordResetTokenAsync", user.Id.ToString());
IdentityResultAssert.IsSuccess(await manager.ResetPasswordAsync(user, token, newPassword));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "ResetPasswordAsync", user.Id.ToString());
Assert.False(await manager.CheckPasswordAsync(user, password));
Assert.True(await manager.CheckPasswordAsync(user, newPassword));
Assert.NotEqual(stamp, user.SecurityStamp);
@ -572,6 +678,7 @@ namespace Microsoft.AspNet.Identity.Test
manager.PasswordValidators.Add(new AlwaysBadValidator());
IdentityResultAssert.IsFailure(await manager.ResetPasswordAsync(user, token, newPassword),
AlwaysBadValidator.ErrorMessage);
IdentityResultAssert.VerifyUserManagerFailureLog(manager.Logger, "ResetPasswordAsync", user.Id.ToString(), AlwaysBadValidator.ErrorMessage);
Assert.True(await manager.CheckPasswordAsync(user, password));
Assert.Equal(stamp, user.SecurityStamp);
}
@ -589,6 +696,7 @@ namespace Microsoft.AspNet.Identity.Test
var stamp = user.SecurityStamp;
Assert.NotNull(stamp);
IdentityResultAssert.IsFailure(await manager.ResetPasswordAsync(user, "bogus", newPassword), "Invalid token.");
IdentityResultAssert.VerifyUserManagerFailureLog(manager.Logger, "ResetPasswordAsync", user.Id.ToString(), IdentityErrorDescriber.Default.InvalidToken());
Assert.True(await manager.CheckPasswordAsync(user, password));
Assert.Equal(stamp, user.SecurityStamp);
}
@ -603,8 +711,14 @@ namespace Microsoft.AspNet.Identity.Test
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2));
var token = await manager.GenerateUserTokenAsync(user, "Static", "test");
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "GenerateUserTokenAsync", user.Id.ToString());
Assert.True(await manager.VerifyUserTokenAsync(user, "Static", "test", token));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "VerifyUserTokenAsync", user.Id.ToString());
Assert.False(await manager.VerifyUserTokenAsync(user, "Static", "test2", token));
IdentityResultAssert.VerifyUserManagerFailureLog(manager.Logger, "VerifyUserTokenAsync", user.Id.ToString(), IdentityErrorDescriber.Default.InvalidToken());
Assert.False(await manager.VerifyUserTokenAsync(user, "Static", "test", token + "a"));
Assert.False(await manager.VerifyUserTokenAsync(user2, "Static", "test", token));
}
@ -620,7 +734,9 @@ namespace Microsoft.AspNet.Identity.Test
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
var token = await manager.GenerateEmailConfirmationTokenAsync(user);
Assert.NotNull(token);
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "GenerateEmailConfirmationTokenAsync", user.Id.ToString());
IdentityResultAssert.IsSuccess(await manager.ConfirmEmailAsync(user, token));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "ConfirmEmailAsync", user.Id.ToString());
Assert.True(await manager.IsEmailConfirmedAsync(user));
IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, null));
Assert.False(await manager.IsEmailConfirmedAsync(user));
@ -637,6 +753,7 @@ namespace Microsoft.AspNet.Identity.Test
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
IdentityResultAssert.IsFailure(await manager.ConfirmEmailAsync(user, "bogus"), "Invalid token.");
Assert.False(await manager.IsEmailConfirmedAsync(user));
IdentityResultAssert.VerifyUserManagerFailureLog(manager.Logger, "ConfirmEmailAsync", user.Id.ToString(), IdentityErrorDescriber.Default.InvalidToken());
}
[Fact]
@ -712,6 +829,7 @@ namespace Microsoft.AspNet.Identity.Test
Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55));
Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user));
IdentityResultAssert.IsSuccess(await mgr.ResetAccessFailedCountAsync(user));
IdentityResultAssert.VerifyUserManagerSuccessLog(mgr.Logger, "ResetAccessFailedCountAsync", user.Id.ToString());
Assert.Equal(0, await mgr.GetAccessFailedCountAsync(user));
Assert.False(await mgr.IsLockedOutAsync(user));
Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55));
@ -734,8 +852,10 @@ namespace Microsoft.AspNet.Identity.Test
IdentityResultAssert.IsSuccess(await mgr.SetLockoutEnabledAsync(user, true));
Assert.True(await mgr.GetLockoutEnabledAsync(user));
Assert.True(user.LockoutEnabled);
IdentityResultAssert.VerifyUserManagerSuccessLog(mgr.Logger, "SetLockoutEnabledAsync", user.Id.ToString());
Assert.False(await mgr.IsLockedOutAsync(user));
IdentityResultAssert.IsSuccess(await mgr.AccessFailedAsync(user));
IdentityResultAssert.VerifyUserManagerSuccessLog(mgr.Logger, "AccessFailedAsync", user.Id.ToString());
Assert.False(await mgr.IsLockedOutAsync(user));
Assert.False(await mgr.GetLockoutEndDateAsync(user) > DateTimeOffset.UtcNow.AddMinutes(55));
Assert.Equal(1, await mgr.GetAccessFailedCountAsync(user));
@ -756,6 +876,7 @@ namespace Microsoft.AspNet.Identity.Test
Assert.True(user.LockoutEnabled);
IdentityResultAssert.IsSuccess(await mgr.SetLockoutEndDateAsync(user, new DateTimeOffset()));
Assert.False(await mgr.IsLockedOutAsync(user));
IdentityResultAssert.VerifyUserManagerSuccessLog(mgr.Logger, "SetLockoutEndDateAsync", user.Id.ToString());
Assert.Equal(new DateTimeOffset(), await mgr.GetLockoutEndDateAsync(user));
Assert.Equal(new DateTimeOffset(), user.LockoutEnd);
}
@ -842,7 +963,7 @@ namespace Microsoft.AspNet.Identity.Test
private class AlwaysBadValidator : IUserValidator<TUser>, IRoleValidator<TRole>,
IPasswordValidator<TUser>
{
public const string ErrorMessage = "I'm Bad.";
public static readonly IdentityError ErrorMessage = new IdentityError { Description = "I'm Bad.", Code = "BadValidator" };
public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user, string password, CancellationToken cancellationToken = default(CancellationToken))
{
@ -892,6 +1013,7 @@ namespace Microsoft.AspNet.Identity.Test
manager.RoleValidators.Clear();
manager.RoleValidators.Add(new AlwaysBadValidator());
IdentityResultAssert.IsFailure(await manager.UpdateAsync(role), error);
IdentityResultAssert.VerifyRoleManagerFailureLog(manager.Logger, "UpdateAsync", role.Id.ToString(), AlwaysBadValidator.ErrorMessage);
}
[Fact]
@ -903,6 +1025,7 @@ namespace Microsoft.AspNet.Identity.Test
IdentityResultAssert.IsSuccess(await manager.CreateAsync(role));
IdentityResultAssert.IsSuccess(await manager.DeleteAsync(role));
Assert.False(await manager.RoleExistsAsync(role.Name));
IdentityResultAssert.VerifyRoleManagerSuccessLog(manager.Logger, "DeleteAsync", role.Id.ToString());
}
[Fact]
@ -916,6 +1039,7 @@ namespace Microsoft.AspNet.Identity.Test
{
IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(role, c));
}
IdentityResultAssert.VerifyRoleManagerSuccessLog(manager.Logger, "AddClaimAsync", role.Id.ToString());
var roleClaims = await manager.GetClaimsAsync(role);
Assert.Equal(3, roleClaims.Count);
IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(role, claims[0]));
@ -927,6 +1051,8 @@ namespace Microsoft.AspNet.Identity.Test
IdentityResultAssert.IsSuccess(await manager.RemoveClaimAsync(role, claims[2]));
roleClaims = await manager.GetClaimsAsync(role);
Assert.Equal(0, roleClaims.Count);
IdentityResultAssert.VerifyRoleManagerSuccessLog(manager.Logger, "RemoveClaimAsync", role.Id.ToString());
}
[Fact]
@ -958,10 +1084,11 @@ namespace Microsoft.AspNet.Identity.Test
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.SetRoleNameAsync(role, "Changed"));
IdentityResultAssert.IsSuccess(await manager.UpdateAsync(role));
IdentityResultAssert.VerifyRoleManagerSuccessLog(manager.Logger, "UpdateAsync", role.Id.ToString());
Assert.False(await manager.RoleExistsAsync("update"));
Assert.Equal(role, await manager.FindByNameAsync(role.Name));
Assert.Equal(role, await manager.FindByNameAsync("Changed"));
}
[Fact]
@ -1027,6 +1154,7 @@ namespace Microsoft.AspNet.Identity.Test
var role = CreateRole("dupeRole");
Assert.False(await manager.RoleExistsAsync(role.Name));
IdentityResultAssert.IsSuccess(await manager.CreateAsync(role));
IdentityResultAssert.VerifyRoleManagerSuccessLog(manager.Logger, "CreateAsync", role.Id.ToString());
Assert.True(await manager.RoleExistsAsync(role.Name));
var role2 = CreateRole();
role2.Name = role.Name;
@ -1051,6 +1179,8 @@ namespace Microsoft.AspNet.Identity.Test
IdentityResultAssert.IsSuccess(await manager.CreateAsync(u));
IdentityResultAssert.IsSuccess(await manager.AddToRoleAsync(u, role.Name));
Assert.True(await manager.IsInRoleAsync(u, role.Name));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "AddToRoleAsync", u.Id.ToString());
}
}
@ -1103,6 +1233,7 @@ namespace Microsoft.AspNet.Identity.Test
Assert.True(await userManager.IsInRoleAsync(user, r.Name));
}
IdentityResultAssert.IsSuccess(await userManager.RemoveFromRoleAsync(user, roles[2].Name));
IdentityResultAssert.VerifyUserManagerSuccessLog(userManager.Logger, "RemoveFromRoleAsync", user.Id.ToString());
Assert.False(await userManager.IsInRoleAsync(user, roles[2].Name));
}
@ -1142,7 +1273,8 @@ namespace Microsoft.AspNet.Identity.Test
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.");
IdentityResultAssert.IsFailure(result, IdentityErrorDescriber.Default.UserNotInRole(role.Name));
IdentityResultAssert.VerifyUserManagerFailureLog(userMgr.Logger, "RemoveFromRoleAsync", user.Id.ToString(), IdentityErrorDescriber.Default.UserNotInRole(role.Name));
}
[Fact]
@ -1157,7 +1289,8 @@ namespace Microsoft.AspNet.Identity.Test
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.");
IdentityResultAssert.IsFailure(await userMgr.AddToRoleAsync(user, role.Name), IdentityErrorDescriber.Default.UserAlreadyInRole(role.Name));
IdentityResultAssert.VerifyUserManagerFailureLog(userMgr.Logger, "AddToRoleAsync", user.Id.ToString(), IdentityErrorDescriber.Default.UserAlreadyInRole(role.Name));
}
[Fact]
@ -1203,6 +1336,7 @@ namespace Microsoft.AspNet.Identity.Test
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));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "ChangePhoneNumberAsync", user.Id.ToString());
Assert.True(await manager.IsPhoneNumberConfirmedAsync(user));
Assert.Equal(await manager.GetPhoneNumberAsync(user), "111-111-1111");
Assert.NotEqual(stamp, user.SecurityStamp);
@ -1219,6 +1353,7 @@ namespace Microsoft.AspNet.Identity.Test
var stamp = await manager.GetSecurityStampAsync(user);
IdentityResultAssert.IsFailure(await manager.ChangePhoneNumberAsync(user, "111-111-1111", "bogus"),
"Invalid token.");
IdentityResultAssert.VerifyUserManagerFailureLog(manager.Logger, "ChangePhoneNumberAsync", user.Id.ToString(), IdentityErrorDescriber.Default.InvalidToken());
Assert.False(await manager.IsPhoneNumberConfirmedAsync(user));
Assert.Equal(await manager.GetPhoneNumberAsync(user), "123-456-7890");
Assert.Equal(stamp, user.SecurityStamp);
@ -1250,12 +1385,16 @@ namespace Microsoft.AspNet.Identity.Test
const string num1 = "111-123-4567";
const string num2 = "111-111-1111";
var token1 = await manager.GenerateChangePhoneNumberTokenAsync(user, num1);
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "GenerateChangePhoneNumberTokenAsync", user.Id.ToString());
var token2 = await manager.GenerateChangePhoneNumberTokenAsync(user, num2);
Assert.NotEqual(token1, token2);
Assert.True(await manager.VerifyChangePhoneNumberTokenAsync(user, token1, num1));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "VerifyChangePhoneNumberTokenAsync", user.Id.ToString());
Assert.True(await manager.VerifyChangePhoneNumberTokenAsync(user, token2, num2));
Assert.False(await manager.VerifyChangePhoneNumberTokenAsync(user, token2, num1));
Assert.False(await manager.VerifyChangePhoneNumberTokenAsync(user, token1, num2));
IdentityResultAssert.VerifyUserManagerFailureLog(manager.Logger, "VerifyChangePhoneNumberTokenAsync", user.Id.ToString(), IdentityErrorDescriber.Default.InvalidToken());
}
[Fact]
@ -1270,6 +1409,7 @@ namespace Microsoft.AspNet.Identity.Test
string newEmail = user.UserName + "@en.vec";
var token1 = await manager.GenerateChangeEmailTokenAsync(user, newEmail);
IdentityResultAssert.IsSuccess(await manager.ChangeEmailAsync(user, newEmail, token1));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "ChangeEmailAsync", user.Id.ToString());
Assert.True(await manager.IsEmailConfirmedAsync(user));
Assert.Equal(await manager.GetEmailAsync(user), newEmail);
Assert.NotEqual(stamp, user.SecurityStamp);
@ -1287,6 +1427,7 @@ namespace Microsoft.AspNet.Identity.Test
var stamp = await manager.GetSecurityStampAsync(user);
IdentityResultAssert.IsFailure(await manager.ChangeEmailAsync(user, "whatevah@foo.barf", "bogus"),
"Invalid token.");
IdentityResultAssert.VerifyUserManagerFailureLog(manager.Logger, "ChangeEmailAsync", user.Id.ToString(), IdentityErrorDescriber.Default.InvalidToken());
Assert.False(await manager.IsEmailConfirmedAsync(user));
Assert.Equal(await manager.GetEmailAsync(user), oldEmail);
Assert.Equal(stamp, user.SecurityStamp);
@ -1328,8 +1469,9 @@ namespace Microsoft.AspNet.Identity.Test
Assert.Null(messageService.Message);
IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token));
Assert.NotNull(messageService.Message);
Assert.Equal("Your security code is: "+token, messageService.Message.Body);
Assert.Equal("Your security code is: " + token, messageService.Message.Body);
Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "VerifyTwoFactorTokenAsync", user.Id.ToString());
}
[Fact]
@ -1369,11 +1511,13 @@ namespace Microsoft.AspNet.Identity.Test
Assert.NotNull(stamp);
var token = await manager.GenerateTwoFactorTokenAsync(user, factorId);
Assert.NotNull(token);
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "GenerateTwoFactorTokenAsync", user.Id.ToString());
Assert.Null(messageService.Message);
IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token));
Assert.NotNull(messageService.Message);
Assert.Equal(subject, messageService.Message.Subject);
Assert.Equal(string.Format(body, token), messageService.Message.Body);
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "NotifyTwoFactorTokenAsync", user.Id.ToString());
Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token));
}
@ -1403,6 +1547,7 @@ namespace Microsoft.AspNet.Identity.Test
var stamp = user.SecurityStamp;
Assert.NotNull(stamp);
IdentityResultAssert.IsSuccess(await manager.SetTwoFactorEnabledAsync(user, true));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "SetTwoFactorEnabledAsync", user.Id.ToString());
Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
Assert.True(await manager.GetTwoFactorEnabledAsync(user));
}
@ -1439,7 +1584,7 @@ namespace Microsoft.AspNet.Identity.Test
Assert.Null(messageService.Message);
IdentityResultAssert.IsSuccess(await manager.NotifyTwoFactorTokenAsync(user, factorId, token));
Assert.NotNull(messageService.Message);
Assert.Equal("Your security code is: "+token, messageService.Message.Body);
Assert.Equal("Your security code is: " + token, messageService.Message.Body);
Assert.True(await manager.VerifyTwoFactorTokenAsync(user, factorId, token));
}
@ -1505,6 +1650,7 @@ namespace Microsoft.AspNet.Identity.Test
Assert.NotNull(factors);
Assert.False(factors.Any());
IdentityResultAssert.IsSuccess(await manager.SetPhoneNumberAsync(user, "111-111-1111"));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "SetPhoneNumberAsync", user.Id.ToString());
user.PhoneNumberConfirmed = true;
await manager.UpdateAsync(user);
factors = await manager.GetValidTwoFactorProvidersAsync(user);
@ -1512,6 +1658,7 @@ namespace Microsoft.AspNet.Identity.Test
Assert.Equal(1, factors.Count());
Assert.Equal("Phone", factors[0]);
IdentityResultAssert.IsSuccess(await manager.SetEmailAsync(user, "test@test.com"));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "SetEmailAsync", user.Id.ToString());
user.EmailConfirmed = true;
await manager.UpdateAsync(user);
factors = await manager.GetValidTwoFactorProvidersAsync(user);
@ -1560,6 +1707,7 @@ namespace Microsoft.AspNet.Identity.Test
user.PhoneNumber = "4251234567";
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
Assert.False(await manager.VerifyTwoFactorTokenAsync(user, "Phone", "bogus"));
IdentityResultAssert.VerifyUserManagerFailureLog(manager.Logger, "VerifyTwoFactorTokenAsync", user.Id.ToString(), IdentityErrorDescriber.Default.InvalidToken());
}
[Fact]
@ -1581,6 +1729,60 @@ namespace Microsoft.AspNet.Identity.Test
Assert.Equal(DateTimeOffset.Parse("01/01/2014"), await userMgr.GetLockoutEndDateAsync(user));
}
[Fact]
public async Task CanGetUsersWithClaims()
{
var manager = CreateManager();
for (int i = 0; i < 6; i++)
{
var user = CreateTestUser();
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
if ((i % 2) == 0)
{
IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, new Claim("foo", "bar")));
}
}
Assert.Equal(3, (await manager.GetUsersForClaimAsync(new Claim("foo", "bar"))).Count);
Assert.Equal(0, (await manager.GetUsersForClaimAsync(new Claim("123", "456"))).Count);
}
[Fact]
public async Task CanGetUsersInRole()
{
var context = CreateTestContext();
var manager = CreateManager(context);
var roleManager = CreateRoleManager(context);
var roles = GenerateRoles("UsersInRole", 4);
foreach (var role in roles)
{
IdentityResultAssert.IsSuccess(await roleManager.CreateAsync(role));
}
for (int i = 0; i < 6; i++)
{
var user = CreateTestUser();
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
if ((i % 2) == 0)
{
IdentityResultAssert.IsSuccess(await manager.AddToRolesAsync(user, roles.Select(x => x.Name).AsEnumerable()));
IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "AddToRolesAsync", user.Id.ToString());
}
}
foreach (var role in roles)
{
Assert.Equal(3, (await manager.GetUsersInRoleAsync(role.Name)).Count);
}
Assert.Equal(0, (await manager.GetUsersInRoleAsync("123456")).Count);
}
public List<TUser> GenerateUsers(string userNamePrefix, int count)
{
var users = new List<TUser>(count);
@ -1600,6 +1802,5 @@ namespace Microsoft.AspNet.Identity.Test
}
return roles;
}
}
}