Introduce SignInResult/IdentityError/Describer
Follows Resource pattern (IdentityErrorDescriber.StringName, or FormatStringName(arg1, arg2) Also cleaned up optional services, by allowing null in constructor SignInStatus -> SignInFailure and introduced SignInResult to make SignInManager APIs consistent with IdentityResult (but no strings needed for SignIn) Fixes: #86, #176, #287 and #177
This commit is contained in:
parent
625b270924
commit
c9d27e27e6
|
|
@ -1,11 +1,10 @@
|
||||||
using Microsoft.AspNet.Http;
|
using System.Linq;
|
||||||
using Microsoft.AspNet.Identity;
|
|
||||||
using Microsoft.AspNet.Mvc;
|
|
||||||
using Microsoft.AspNet.Mvc.Rendering;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Identity;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
using Microsoft.AspNet.Mvc.Rendering;
|
||||||
|
|
||||||
namespace IdentitySample.Models
|
namespace IdentitySample.Models
|
||||||
{
|
{
|
||||||
|
|
@ -43,20 +42,23 @@ namespace IdentitySample.Models
|
||||||
ViewBag.ReturnUrl = returnUrl;
|
ViewBag.ReturnUrl = returnUrl;
|
||||||
if (ModelState.IsValid)
|
if (ModelState.IsValid)
|
||||||
{
|
{
|
||||||
var signInStatus = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);
|
var result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);
|
||||||
switch (signInStatus)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
case SignInStatus.Success:
|
return RedirectToLocal(returnUrl);
|
||||||
return RedirectToLocal(returnUrl);
|
}
|
||||||
case SignInStatus.LockedOut:
|
if (result.RequiresTwoFactor)
|
||||||
ModelState.AddModelError("", "User is locked out, try again later.");
|
{
|
||||||
return View(model);
|
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
|
||||||
case SignInStatus.RequiresVerification:
|
}
|
||||||
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
|
if (result.IsLockedOut)
|
||||||
case SignInStatus.Failure:
|
{
|
||||||
default:
|
return View("Lockout");
|
||||||
ModelState.AddModelError("", "Invalid username or password.");
|
}
|
||||||
return View(model);
|
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
|
// 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,
|
var result = await SignInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey,
|
||||||
isPersistent: false);
|
isPersistent: false);
|
||||||
switch (result)
|
if (result.Succeeded)
|
||||||
{
|
{
|
||||||
case SignInStatus.Success:
|
return RedirectToLocal(returnUrl);
|
||||||
return RedirectToLocal(returnUrl);
|
}
|
||||||
case SignInStatus.LockedOut:
|
if (result.RequiresTwoFactor)
|
||||||
return View("Lockout");
|
{
|
||||||
case SignInStatus.RequiresVerification:
|
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl });
|
||||||
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl });
|
}
|
||||||
case SignInStatus.Failure:
|
if (result.IsLockedOut)
|
||||||
default:
|
{
|
||||||
// If the user does not have an account, then prompt the user to create an account
|
return View("Lockout");
|
||||||
ViewBag.ReturnUrl = returnUrl;
|
}
|
||||||
ViewBag.LoginProvider = info.LoginProvider;
|
else
|
||||||
// REVIEW: handle case where email not in claims?
|
{
|
||||||
var email = info.ExternalIdentity.FindFirstValue(ClaimTypes.Email);
|
// If the user does not have an account, then prompt the user to create an account
|
||||||
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email });
|
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);
|
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);
|
||||||
return RedirectToLocal(model.ReturnUrl);
|
}
|
||||||
case SignInStatus.LockedOut:
|
if (result.IsLockedOut)
|
||||||
return View("Lockout");
|
{
|
||||||
default:
|
return View("Lockout");
|
||||||
ModelState.AddModelError("", "Invalid code.");
|
}
|
||||||
return View(model);
|
else
|
||||||
|
{
|
||||||
|
ModelState.AddModelError("", "Invalid code.");
|
||||||
|
return View(model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -398,7 +407,7 @@ namespace IdentitySample.Models
|
||||||
{
|
{
|
||||||
foreach (var error in result.Errors)
|
foreach (var error in result.Errors)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError("", error);
|
ModelState.AddModelError("", error.Description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -346,7 +346,7 @@ namespace IdentitySample
|
||||||
{
|
{
|
||||||
foreach (var error in result.Errors)
|
foreach (var error in result.Errors)
|
||||||
{
|
{
|
||||||
ModelState.AddModelError("", error);
|
ModelState.AddModelError("", error.Description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,12 @@ namespace Microsoft.AspNet.Identity
|
||||||
return AddScoped(typeof(IRoleValidator<>).MakeGenericType(RoleType), typeof(T));
|
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
|
public IdentityBuilder AddPasswordValidator<T>() where T : class
|
||||||
{
|
{
|
||||||
return AddScoped(typeof(IPasswordValidator<>).MakeGenericType(UserType), typeof(T));
|
return AddScoped(typeof(IPasswordValidator<>).MakeGenericType(UserType), typeof(T));
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,9 @@
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Identity
|
namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
public enum SignInStatus
|
public class IdentityError
|
||||||
{
|
{
|
||||||
Success,
|
public string Code { get; set; }
|
||||||
LockedOut,
|
public string Description { get; set; }
|
||||||
RequiresVerification,
|
|
||||||
NotAllowed,
|
|
||||||
Failure
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,181 @@
|
||||||
|
// 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 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,45 +11,19 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class IdentityResult
|
public class IdentityResult
|
||||||
{
|
{
|
||||||
private static readonly IdentityResult _success = new IdentityResult(true);
|
private static readonly IdentityResult _success = new IdentityResult { Succeeded = true };
|
||||||
|
|
||||||
/// <summary>
|
private List<IdentityError> _errors = new List<IdentityError>();
|
||||||
/// 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];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// True if the operation was successful
|
/// True if the operation was successful
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Succeeded { get; private set; }
|
public bool Succeeded { get; protected set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// List of errors
|
/// List of errors
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<string> Errors { get; private set; }
|
public IEnumerable<IdentityError> Errors { get { return _errors; } }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Static success result
|
/// Static success result
|
||||||
|
|
@ -65,9 +39,14 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="errors"></param>
|
/// <param name="errors"></param>
|
||||||
/// <returns></returns>
|
/// <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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -59,6 +59,8 @@ namespace Microsoft.Framework.DependencyInjection
|
||||||
services.TryAdd(describe.Transient<IPasswordHasher<TUser>, PasswordHasher<TUser>>());
|
services.TryAdd(describe.Transient<IPasswordHasher<TUser>, PasswordHasher<TUser>>());
|
||||||
services.TryAdd(describe.Transient<IUserNameNormalizer, UpperInvariantUserNameNormalizer>());
|
services.TryAdd(describe.Transient<IUserNameNormalizer, UpperInvariantUserNameNormalizer>());
|
||||||
services.TryAdd(describe.Transient<IRoleValidator<TRole>, RoleValidator<TRole>>());
|
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<ISecurityStampValidator, SecurityStampValidator<TUser>>());
|
||||||
services.TryAdd(describe.Scoped<IClaimsIdentityFactory<TUser>, ClaimsIdentityFactory<TUser, TRole>>());
|
services.TryAdd(describe.Scoped<IClaimsIdentityFactory<TUser>, ClaimsIdentityFactory<TUser, TRole>>());
|
||||||
services.TryAdd(describe.Scoped<UserManager<TUser>, UserManager<TUser>>());
|
services.TryAdd(describe.Scoped<UserManager<TUser>, UserManager<TUser>>());
|
||||||
|
|
|
||||||
|
|
@ -37,14 +37,11 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// Constructs a PasswordHasher using the specified options
|
/// Constructs a PasswordHasher using the specified options
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="options"></param>
|
/// <param name="options"></param>
|
||||||
public PasswordHasher(IOptions<PasswordHasherOptions> options)
|
public PasswordHasher(IOptions<PasswordHasherOptions> optionsAccessor = null)
|
||||||
{
|
{
|
||||||
if (options == null)
|
var options = optionsAccessor?.Options ?? new PasswordHasherOptions();
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(options));
|
|
||||||
}
|
|
||||||
|
|
||||||
_compatibilityMode = options.Options.CompatibilityMode;
|
_compatibilityMode = options.CompatibilityMode;
|
||||||
switch (_compatibilityMode)
|
switch (_compatibilityMode)
|
||||||
{
|
{
|
||||||
case PasswordHasherCompatibilityMode.IdentityV2:
|
case PasswordHasherCompatibilityMode.IdentityV2:
|
||||||
|
|
@ -52,7 +49,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PasswordHasherCompatibilityMode.IdentityV3:
|
case PasswordHasherCompatibilityMode.IdentityV3:
|
||||||
_iterCount = options.Options.IterationCount;
|
_iterCount = options.IterationCount;
|
||||||
if (_iterCount < 1)
|
if (_iterCount < 1)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(Resources.InvalidPasswordHasherIterationCount);
|
throw new InvalidOperationException(Resources.InvalidPasswordHasherIterationCount);
|
||||||
|
|
@ -63,7 +60,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
throw new InvalidOperationException(Resources.InvalidPasswordHasherCompatibilityMode);
|
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.
|
// Compares two byte arrays for equality. The method is specifically written so that the loop is not optimized.
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -15,6 +14,13 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class PasswordValidator<TUser> : IPasswordValidator<TUser> where TUser : class
|
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>
|
/// <summary>
|
||||||
/// Ensures that the password is of the required length and meets the configured requirements
|
/// Ensures that the password is of the required length and meets the configured requirements
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -33,33 +39,32 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("manager");
|
throw new ArgumentNullException("manager");
|
||||||
}
|
}
|
||||||
var errors = new List<string>();
|
var errors = new List<IdentityError>();
|
||||||
var options = manager.Options.Password;
|
var options = manager.Options.Password;
|
||||||
if (string.IsNullOrWhiteSpace(password) || password.Length < options.RequiredLength)
|
if (string.IsNullOrWhiteSpace(password) || password.Length < options.RequiredLength)
|
||||||
{
|
{
|
||||||
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.PasswordTooShort,
|
errors.Add(Describer.PasswordTooShort(options.RequiredLength));
|
||||||
options.RequiredLength));
|
|
||||||
}
|
}
|
||||||
if (options.RequireNonLetterOrDigit && password.All(IsLetterOrDigit))
|
if (options.RequireNonLetterOrDigit && password.All(IsLetterOrDigit))
|
||||||
{
|
{
|
||||||
errors.Add(Resources.PasswordRequireNonLetterOrDigit);
|
errors.Add(Describer.PasswordRequiresNonLetterAndDigit());
|
||||||
}
|
}
|
||||||
if (options.RequireDigit && !password.Any(IsDigit))
|
if (options.RequireDigit && !password.Any(IsDigit))
|
||||||
{
|
{
|
||||||
errors.Add(Resources.PasswordRequireDigit);
|
errors.Add(Describer.PasswordRequiresDigit());
|
||||||
}
|
}
|
||||||
if (options.RequireLowercase && !password.Any(IsLower))
|
if (options.RequireLowercase && !password.Any(IsLower))
|
||||||
{
|
{
|
||||||
errors.Add(Resources.PasswordRequireLower);
|
errors.Add(Describer.PasswordRequiresLower());
|
||||||
}
|
}
|
||||||
if (options.RequireUppercase && !password.Any(IsUpper))
|
if (options.RequireUppercase && !password.Any(IsUpper))
|
||||||
{
|
{
|
||||||
errors.Add(Resources.PasswordRequireUpper);
|
errors.Add(Describer.PasswordRequiresUpper());
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
Task.FromResult(errors.Count == 0
|
Task.FromResult(errors.Count == 0
|
||||||
? IdentityResult.Success
|
? IdentityResult.Success
|
||||||
: IdentityResult.Failed(String.Join(" ", errors)));
|
: IdentityResult.Failed(errors.ToArray()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -139,35 +139,35 @@ namespace Microsoft.AspNet.Identity
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Name {0} is already taken.
|
/// Role Name '{0}' is already taken.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string DuplicateName
|
internal static string DuplicateRoleName
|
||||||
{
|
{
|
||||||
get { return GetString("DuplicateName"); }
|
get { return GetString("DuplicateRoleName"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Name {0} is already taken.
|
/// Role Name '{0}' is already taken.
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// A user with that external login already exists.
|
/// UserName '{0}' is already taken.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string ExternalLoginExists
|
internal static string DuplicateUserName
|
||||||
{
|
{
|
||||||
get { return GetString("ExternalLoginExists"); }
|
get { return GetString("DuplicateUserName"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A user with that external login already exists.
|
/// UserName '{0}' is already taken.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatExternalLoginExists()
|
internal static string FormatDuplicateUserName(object p0)
|
||||||
{
|
{
|
||||||
return GetString("ExternalLoginExists");
|
return string.Format(CultureInfo.CurrentCulture, GetString("DuplicateUserName"), p0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -218,6 +218,22 @@ namespace Microsoft.AspNet.Identity
|
||||||
return GetString("InvalidPasswordHasherIterationCount");
|
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>
|
/// <summary>
|
||||||
/// Invalid token.
|
/// Invalid token.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -235,7 +251,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// User name {0} is invalid, can only contain letters or digits.
|
/// User name '{0}' is invalid, can only contain letters or digits.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string InvalidUserName
|
internal static string InvalidUserName
|
||||||
{
|
{
|
||||||
|
|
@ -243,7 +259,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// User name {0} is invalid, can only contain letters or digits.
|
/// User name '{0}' is invalid, can only contain letters or digits.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatInvalidUserName(object p0)
|
internal static string FormatInvalidUserName(object p0)
|
||||||
{
|
{
|
||||||
|
|
@ -251,19 +267,19 @@ namespace Microsoft.AspNet.Identity
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lockout is not enabled for this user.
|
/// A user with this login already exists.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string LockoutNotEnabled
|
internal static string LoginAlreadyAssociated
|
||||||
{
|
{
|
||||||
get { return GetString("LockoutNotEnabled"); }
|
get { return GetString("LoginAlreadyAssociated"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Lockout is not enabled for this user.
|
/// A user with this login already exists.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatLockoutNotEnabled()
|
internal static string FormatLoginAlreadyAssociated()
|
||||||
{
|
{
|
||||||
return GetString("LockoutNotEnabled");
|
return GetString("LoginAlreadyAssociated");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -317,65 +333,65 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Passwords must have at least one digit ('0'-'9').
|
/// Passwords must have at least one digit ('0'-'9').
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string PasswordRequireDigit
|
internal static string PasswordRequiresDigit
|
||||||
{
|
{
|
||||||
get { return GetString("PasswordRequireDigit"); }
|
get { return GetString("PasswordRequiresDigit"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Passwords must have at least one digit ('0'-'9').
|
/// Passwords must have at least one digit ('0'-'9').
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatPasswordRequireDigit()
|
internal static string FormatPasswordRequiresDigit()
|
||||||
{
|
{
|
||||||
return GetString("PasswordRequireDigit");
|
return GetString("PasswordRequiresDigit");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Passwords must have at least one lowercase ('a'-'z').
|
/// Passwords must have at least one lowercase ('a'-'z').
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string PasswordRequireLower
|
internal static string PasswordRequiresLower
|
||||||
{
|
{
|
||||||
get { return GetString("PasswordRequireLower"); }
|
get { return GetString("PasswordRequiresLower"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Passwords must have at least one lowercase ('a'-'z').
|
/// Passwords must have at least one lowercase ('a'-'z').
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatPasswordRequireLower()
|
internal static string FormatPasswordRequiresLower()
|
||||||
{
|
{
|
||||||
return GetString("PasswordRequireLower");
|
return GetString("PasswordRequiresLower");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Passwords must have at least one non letter and non digit character.
|
/// Passwords must have at least one non letter and non digit character.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string PasswordRequireNonLetterOrDigit
|
internal static string PasswordRequiresNonLetterAndDigit
|
||||||
{
|
{
|
||||||
get { return GetString("PasswordRequireNonLetterOrDigit"); }
|
get { return GetString("PasswordRequiresNonLetterAndDigit"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Passwords must have at least one non letter and non digit character.
|
/// Passwords must have at least one non letter and non digit character.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatPasswordRequireNonLetterOrDigit()
|
internal static string FormatPasswordRequiresNonLetterAndDigit()
|
||||||
{
|
{
|
||||||
return GetString("PasswordRequireNonLetterOrDigit");
|
return GetString("PasswordRequiresNonLetterAndDigit");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Passwords must have at least one uppercase ('A'-'Z').
|
/// Passwords must have at least one uppercase ('A'-'Z').
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string PasswordRequireUpper
|
internal static string PasswordRequiresUpper
|
||||||
{
|
{
|
||||||
get { return GetString("PasswordRequireUpper"); }
|
get { return GetString("PasswordRequiresUpper"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Passwords must have at least one uppercase ('A'-'Z').
|
/// Passwords must have at least one uppercase ('A'-'Z').
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatPasswordRequireUpper()
|
internal static string FormatPasswordRequiresUpper()
|
||||||
{
|
{
|
||||||
return GetString("PasswordRequireUpper");
|
return GetString("PasswordRequiresUpper");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -651,7 +667,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// User already in role.
|
/// User already in role '{0}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string UserAlreadyInRole
|
internal static string UserAlreadyInRole
|
||||||
{
|
{
|
||||||
|
|
@ -659,11 +675,43 @@ namespace Microsoft.AspNet.Identity
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// User already in role.
|
/// User already in role '{0}'.
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
|
|
@ -683,7 +731,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// User is not in role.
|
/// User is not in role '{0}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string UserNotInRole
|
internal static string UserNotInRole
|
||||||
{
|
{
|
||||||
|
|
@ -691,11 +739,11 @@ namespace Microsoft.AspNet.Identity
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// User is not in role.
|
/// User is not in role '{0}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatUserNotInRole()
|
internal static string FormatUserNotInRole(object p0)
|
||||||
{
|
{
|
||||||
return GetString("UserNotInRole");
|
return string.Format(CultureInfo.CurrentCulture, GetString("UserNotInRole"), p0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
|
|
|
||||||
|
|
@ -149,13 +149,13 @@
|
||||||
<value>Email '{0}' is already taken.</value>
|
<value>Email '{0}' is already taken.</value>
|
||||||
<comment>error for duplicate emails</comment>
|
<comment>error for duplicate emails</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="DuplicateName" xml:space="preserve">
|
<data name="DuplicateRoleName" xml:space="preserve">
|
||||||
<value>Name {0} is already taken.</value>
|
<value>Role Name '{0}' is already taken.</value>
|
||||||
<comment>error for duplicate usernames</comment>
|
<comment>error for duplicate usernames</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="ExternalLoginExists" xml:space="preserve">
|
<data name="DuplicateUserName" xml:space="preserve">
|
||||||
<value>A user with that external login already exists.</value>
|
<value>UserName '{0}' is already taken.</value>
|
||||||
<comment>Error when a login already linked</comment>
|
<comment>error for duplicate usernames</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="InvalidEmail" xml:space="preserve">
|
<data name="InvalidEmail" xml:space="preserve">
|
||||||
<value>Email '{0}' is invalid.</value>
|
<value>Email '{0}' is invalid.</value>
|
||||||
|
|
@ -169,17 +169,21 @@
|
||||||
<value>The iteration count must be a positive integer.</value>
|
<value>The iteration count must be a positive integer.</value>
|
||||||
<comment>Error when the iteration count is < 1.</comment>
|
<comment>Error when the iteration count is < 1.</comment>
|
||||||
</data>
|
</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">
|
<data name="InvalidToken" xml:space="preserve">
|
||||||
<value>Invalid token.</value>
|
<value>Invalid token.</value>
|
||||||
<comment>Error when a token is not recognized</comment>
|
<comment>Error when a token is not recognized</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="InvalidUserName" xml:space="preserve">
|
<data name="InvalidUserName" xml:space="preserve">
|
||||||
<value>User name {0} is invalid, can only contain letters or digits.</value>
|
<value>User name '{0}' is invalid, can only contain letters or digits.</value>
|
||||||
<comment>usernames can only contain letters or digits</comment>
|
<comment>usernames can only contain letters or digits</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="LockoutNotEnabled" xml:space="preserve">
|
<data name="LoginAlreadyAssociated" xml:space="preserve">
|
||||||
<value>Lockout is not enabled for this user.</value>
|
<value>A user with this login already exists.</value>
|
||||||
<comment>error when lockout is not enabled</comment>
|
<comment>Error when a login already linked</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="NoMessageProvider" xml:space="preserve">
|
<data name="NoMessageProvider" xml:space="preserve">
|
||||||
<value>No IUserMessageProvider named '{0}' is registered.</value>
|
<value>No IUserMessageProvider named '{0}' is registered.</value>
|
||||||
|
|
@ -193,19 +197,19 @@
|
||||||
<value>Incorrect password.</value>
|
<value>Incorrect password.</value>
|
||||||
<comment>Error when a password doesn't match</comment>
|
<comment>Error when a password doesn't match</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="PasswordRequireDigit" xml:space="preserve">
|
<data name="PasswordRequiresDigit" xml:space="preserve">
|
||||||
<value>Passwords must have at least one digit ('0'-'9').</value>
|
<value>Passwords must have at least one digit ('0'-'9').</value>
|
||||||
<comment>Error when passwords do not have a digit</comment>
|
<comment>Error when passwords do not have a digit</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="PasswordRequireLower" xml:space="preserve">
|
<data name="PasswordRequiresLower" xml:space="preserve">
|
||||||
<value>Passwords must have at least one lowercase ('a'-'z').</value>
|
<value>Passwords must have at least one lowercase ('a'-'z').</value>
|
||||||
<comment>Error when passwords do not have a lowercase letter</comment>
|
<comment>Error when passwords do not have a lowercase letter</comment>
|
||||||
</data>
|
</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>
|
<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>
|
<comment>Error when password does not have enough letter or digit characters</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="PasswordRequireUpper" xml:space="preserve">
|
<data name="PasswordRequiresUpper" xml:space="preserve">
|
||||||
<value>Passwords must have at least one uppercase ('A'-'Z').</value>
|
<value>Passwords must have at least one uppercase ('A'-'Z').</value>
|
||||||
<comment>Error when passwords do not have an uppercase letter</comment>
|
<comment>Error when passwords do not have an uppercase letter</comment>
|
||||||
</data>
|
</data>
|
||||||
|
|
@ -213,10 +217,6 @@
|
||||||
<value>Passwords must be at least {0} characters.</value>
|
<value>Passwords must be at least {0} characters.</value>
|
||||||
<comment>Error message for passwords that are too short</comment>
|
<comment>Error message for passwords that are too short</comment>
|
||||||
</data>
|
</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">
|
<data name="RoleNotFound" xml:space="preserve">
|
||||||
<value>Role {0} does not exist.</value>
|
<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>
|
||||||
|
|
@ -278,15 +278,23 @@
|
||||||
<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>
|
||||||
<data name="UserAlreadyInRole" xml:space="preserve">
|
<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>
|
<comment>Error when a user is already in a role</comment>
|
||||||
</data>
|
</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">
|
<data name="UserNameNotFound" xml:space="preserve">
|
||||||
<value>User {0} does not exist.</value>
|
<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>
|
||||||
<data name="UserNotInRole" xml:space="preserve">
|
<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>
|
<comment>Error when a user is not in the role</comment>
|
||||||
</data>
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -24,13 +24,16 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="store">The IRoleStore commits changes via the UpdateAsync/CreateAsync methods</param>
|
/// <param name="store">The IRoleStore commits changes via the UpdateAsync/CreateAsync methods</param>
|
||||||
/// <param name="roleValidator"></param>
|
/// <param name="roleValidator"></param>
|
||||||
public RoleManager(IRoleStore<TRole> store, IEnumerable<IRoleValidator<TRole>> roleValidators)
|
public RoleManager(IRoleStore<TRole> store,
|
||||||
|
IEnumerable<IRoleValidator<TRole>> roleValidators = null,
|
||||||
|
IdentityErrorDescriber errors = null)
|
||||||
{
|
{
|
||||||
if (store == null)
|
if (store == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("store");
|
throw new ArgumentNullException("store");
|
||||||
}
|
}
|
||||||
Store = store;
|
Store = store;
|
||||||
|
ErrorDescriber = errors ?? new IdentityErrorDescriber();
|
||||||
|
|
||||||
if (roleValidators != null)
|
if (roleValidators != null)
|
||||||
{
|
{
|
||||||
|
|
@ -51,6 +54,11 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IList<IRoleValidator<TRole>> RoleValidators { get; } = new List<IRoleValidator<TRole>>();
|
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>
|
/// <summary>
|
||||||
/// Returns an IQueryable of roles if the store is an IQueryableRoleStore
|
/// Returns an IQueryable of roles if the store is an IQueryableRoleStore
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -102,7 +110,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
|
|
||||||
private async Task<IdentityResult> ValidateRoleInternal(TRole role, CancellationToken cancellationToken)
|
private async Task<IdentityResult> ValidateRoleInternal(TRole role, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var errors = new List<string>();
|
var errors = new List<IdentityError>();
|
||||||
foreach (var v in RoleValidators)
|
foreach (var v in RoleValidators)
|
||||||
{
|
{
|
||||||
var result = await v.ValidateAsync(this, role, cancellationToken);
|
var result = await v.ValidateAsync(this, role, cancellationToken);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
|
@ -15,6 +14,13 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// <typeparam name="TRole"></typeparam>
|
/// <typeparam name="TRole"></typeparam>
|
||||||
public class RoleValidator<TRole> : IRoleValidator<TRole> where TRole : class
|
public class RoleValidator<TRole> : IRoleValidator<TRole> where TRole : class
|
||||||
{
|
{
|
||||||
|
public RoleValidator(IdentityErrorDescriber errors = null)
|
||||||
|
{
|
||||||
|
Describer = errors ?? new IdentityErrorDescriber();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IdentityErrorDescriber Describer { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validates a role before saving
|
/// Validates a role before saving
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -33,7 +39,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("role");
|
throw new ArgumentNullException("role");
|
||||||
}
|
}
|
||||||
var errors = new List<string>();
|
var errors = new List<IdentityError>();
|
||||||
await ValidateRoleName(manager, role, errors);
|
await ValidateRoleName(manager, role, errors);
|
||||||
if (errors.Count > 0)
|
if (errors.Count > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -42,13 +48,13 @@ namespace Microsoft.AspNet.Identity
|
||||||
return IdentityResult.Success;
|
return IdentityResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task ValidateRoleName(RoleManager<TRole> manager, TRole role,
|
private async Task ValidateRoleName(RoleManager<TRole> manager, TRole role,
|
||||||
ICollection<string> errors)
|
ICollection<IdentityError> errors)
|
||||||
{
|
{
|
||||||
var roleName = await manager.GetRoleNameAsync(role);
|
var roleName = await manager.GetRoleNameAsync(role);
|
||||||
if (string.IsNullOrWhiteSpace(roleName))
|
if (string.IsNullOrWhiteSpace(roleName))
|
||||||
{
|
{
|
||||||
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.PropertyTooShort, "Name"));
|
errors.Add(Describer.InvalidRoleName(roleName));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -56,7 +62,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
if (owner != null &&
|
if (owner != null &&
|
||||||
!string.Equals(await manager.GetRoleIdAsync(owner), await manager.GetRoleIdAsync(role)))
|
!string.Equals(await manager.GetRoleIdAsync(owner), await manager.GetRoleIdAsync(role)))
|
||||||
{
|
{
|
||||||
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.DuplicateName, roleName));
|
errors.Add(Describer.DuplicateRoleName(roleName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,10 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// <typeparam name="TUser"></typeparam>
|
/// <typeparam name="TUser"></typeparam>
|
||||||
public class SignInManager<TUser> where TUser : class
|
public class SignInManager<TUser> where TUser : class
|
||||||
{
|
{
|
||||||
public SignInManager(UserManager<TUser> userManager, IContextAccessor<HttpContext> contextAccessor,
|
public SignInManager(UserManager<TUser> userManager,
|
||||||
IClaimsIdentityFactory<TUser> claimsFactory, IOptions<IdentityOptions> optionsAccessor)
|
IContextAccessor<HttpContext> contextAccessor,
|
||||||
|
IClaimsIdentityFactory<TUser> claimsFactory,
|
||||||
|
IOptions<IdentityOptions> optionsAccessor = null)
|
||||||
{
|
{
|
||||||
if (userManager == null)
|
if (userManager == null)
|
||||||
{
|
{
|
||||||
|
|
@ -36,14 +38,10 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(claimsFactory));
|
throw new ArgumentNullException(nameof(claimsFactory));
|
||||||
}
|
}
|
||||||
if (optionsAccessor == null || optionsAccessor.Options == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(optionsAccessor));
|
|
||||||
}
|
|
||||||
UserManager = userManager;
|
UserManager = userManager;
|
||||||
Context = contextAccessor.Value;
|
Context = contextAccessor.Value;
|
||||||
ClaimsFactory = claimsFactory;
|
ClaimsFactory = claimsFactory;
|
||||||
Options = optionsAccessor.Options;
|
Options = optionsAccessor?.Options ?? new IdentityOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserManager<TUser> UserManager { get; private set; }
|
public UserManager<TUser> UserManager { get; private set; }
|
||||||
|
|
@ -96,15 +94,15 @@ namespace Microsoft.AspNet.Identity
|
||||||
return UserManager.SupportsUserLockout && await UserManager.IsLockedOutAsync(user, token);
|
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))
|
if (!await CanSignInAsync(user, token))
|
||||||
{
|
{
|
||||||
return SignInStatus.NotAllowed;
|
return SignInResult.NotAllowed;
|
||||||
}
|
}
|
||||||
if (await IsLockedOut(user, token))
|
if (await IsLockedOut(user, token))
|
||||||
{
|
{
|
||||||
return SignInStatus.LockedOut;
|
return SignInResult.LockedOut;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -141,13 +139,21 @@ namespace Microsoft.AspNet.Identity
|
||||||
return null;
|
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))
|
bool isPersistent, bool shouldLockout, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(user));
|
||||||
|
}
|
||||||
var error = await PreSignInCheck(user, cancellationToken);
|
var error = await PreSignInCheck(user, cancellationToken);
|
||||||
if (error != null)
|
if (error != null)
|
||||||
{
|
{
|
||||||
return error.Value;
|
return error;
|
||||||
|
}
|
||||||
|
if (await IsLockedOut(user, cancellationToken))
|
||||||
|
{
|
||||||
|
return SignInResult.LockedOut;
|
||||||
}
|
}
|
||||||
if (await UserManager.CheckPasswordAsync(user, password, cancellationToken))
|
if (await UserManager.CheckPasswordAsync(user, password, cancellationToken))
|
||||||
{
|
{
|
||||||
|
|
@ -160,19 +166,19 @@ namespace Microsoft.AspNet.Identity
|
||||||
await UserManager.AccessFailedAsync(user, cancellationToken);
|
await UserManager.AccessFailedAsync(user, cancellationToken);
|
||||||
if (await UserManager.IsLockedOutAsync(user, cancellationToken))
|
if (await UserManager.IsLockedOutAsync(user, cancellationToken))
|
||||||
{
|
{
|
||||||
return SignInStatus.LockedOut;
|
return SignInResult.LockedOut;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return SignInStatus.Failure;
|
return SignInResult.Failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
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))
|
bool isPersistent, bool shouldLockout, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
var user = await UserManager.FindByNameAsync(userName, cancellationToken);
|
var user = await UserManager.FindByNameAsync(userName, cancellationToken);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
return SignInStatus.Failure;
|
return SignInResult.Failed;
|
||||||
}
|
}
|
||||||
return await PasswordSignInAsync(user, password, isPersistent, shouldLockout, cancellationToken);
|
return await PasswordSignInAsync(user, password, isPersistent, shouldLockout, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
@ -207,7 +213,6 @@ namespace Microsoft.AspNet.Identity
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
var token = await UserManager.GenerateTwoFactorTokenAsync(user, provider, cancellationToken);
|
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);
|
await UserManager.NotifyTwoFactorTokenAsync(user, provider, token, cancellationToken);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -236,23 +241,23 @@ namespace Microsoft.AspNet.Identity
|
||||||
return Task.FromResult(0);
|
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))
|
bool rememberClient, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
var twoFactorInfo = await RetrieveTwoFactorInfoAsync(cancellationToken);
|
var twoFactorInfo = await RetrieveTwoFactorInfoAsync(cancellationToken);
|
||||||
if (twoFactorInfo == null || twoFactorInfo.UserId == null)
|
if (twoFactorInfo == null || twoFactorInfo.UserId == null)
|
||||||
{
|
{
|
||||||
return SignInStatus.Failure;
|
return SignInResult.Failed;
|
||||||
}
|
}
|
||||||
var user = await UserManager.FindByIdAsync(twoFactorInfo.UserId, cancellationToken);
|
var user = await UserManager.FindByIdAsync(twoFactorInfo.UserId, cancellationToken);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
return SignInStatus.Failure;
|
return SignInResult.Failed;
|
||||||
}
|
}
|
||||||
var error = await PreSignInCheck(user, cancellationToken);
|
var error = await PreSignInCheck(user, cancellationToken);
|
||||||
if (error != null)
|
if (error != null)
|
||||||
{
|
{
|
||||||
return error.Value;
|
return error;
|
||||||
}
|
}
|
||||||
if (await UserManager.VerifyTwoFactorTokenAsync(user, provider, code, cancellationToken))
|
if (await UserManager.VerifyTwoFactorTokenAsync(user, provider, code, cancellationToken))
|
||||||
{
|
{
|
||||||
|
|
@ -268,11 +273,13 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
await RememberTwoFactorClientAsync(user, cancellationToken);
|
await RememberTwoFactorClientAsync(user, cancellationToken);
|
||||||
}
|
}
|
||||||
return SignInStatus.Success;
|
await UserManager.ResetAccessFailedCountAsync(user, cancellationToken);
|
||||||
|
await SignInAsync(user, isPersistent);
|
||||||
|
return SignInResult.Success;
|
||||||
}
|
}
|
||||||
// If the token is incorrect, record the failure which also may cause the user to be locked out
|
// If the token is incorrect, record the failure which also may cause the user to be locked out
|
||||||
await UserManager.AccessFailedAsync(user, cancellationToken);
|
await UserManager.AccessFailedAsync(user, cancellationToken);
|
||||||
return SignInStatus.Failure;
|
return SignInResult.Failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -292,18 +299,18 @@ namespace Microsoft.AspNet.Identity
|
||||||
return await UserManager.FindByIdAsync(info.UserId, cancellationToken);
|
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))
|
CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
var user = await UserManager.FindByLoginAsync(loginProvider, providerKey, cancellationToken);
|
var user = await UserManager.FindByLoginAsync(loginProvider, providerKey, cancellationToken);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
return SignInStatus.Failure;
|
return SignInResult.Failed;
|
||||||
}
|
}
|
||||||
var error = await PreSignInCheck(user, cancellationToken);
|
var error = await PreSignInCheck(user, cancellationToken);
|
||||||
if (error != null)
|
if (error != null)
|
||||||
{
|
{
|
||||||
return error.Value;
|
return error;
|
||||||
}
|
}
|
||||||
return await SignInOrTwoFactorAsync(user, isPersistent, cancellationToken, loginProvider);
|
return await SignInOrTwoFactorAsync(user, isPersistent, cancellationToken, loginProvider);
|
||||||
}
|
}
|
||||||
|
|
@ -358,7 +365,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
return properties;
|
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)
|
CancellationToken cancellationToken, string loginProvider = null)
|
||||||
{
|
{
|
||||||
if (UserManager.SupportsUserTwoFactor &&
|
if (UserManager.SupportsUserTwoFactor &&
|
||||||
|
|
@ -370,7 +377,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
// Store the userId for use after two factor check
|
// Store the userId for use after two factor check
|
||||||
var userId = await UserManager.GetUserIdAsync(user, cancellationToken);
|
var userId = await UserManager.GetUserIdAsync(user, cancellationToken);
|
||||||
Context.Response.SignIn(StoreTwoFactorInfo(userId, loginProvider));
|
Context.Response.SignIn(StoreTwoFactorInfo(userId, loginProvider));
|
||||||
return SignInStatus.RequiresVerification;
|
return SignInResult.TwoFactorRequired;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Cleanup external cookie
|
// Cleanup external cookie
|
||||||
|
|
@ -379,7 +386,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
Context.Response.SignOut(IdentityOptions.ExternalCookieAuthenticationType);
|
Context.Response.SignOut(IdentityOptions.ExternalCookieAuthenticationType);
|
||||||
}
|
}
|
||||||
await SignInAsync(user, isPersistent, loginProvider, cancellationToken);
|
await SignInAsync(user, isPersistent, loginProvider, cancellationToken);
|
||||||
return SignInStatus.Success;
|
return SignInResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<TwoFactorAuthenticationInfo> RetrieveTwoFactorInfoAsync(CancellationToken cancellationToken)
|
private async Task<TwoFactorAuthenticationInfo> RetrieveTwoFactorInfoAsync(CancellationToken cancellationToken)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
// 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
|
||||||
|
{
|
||||||
|
/// <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; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -39,30 +39,24 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// <param name="passwordValidator"></param>
|
/// <param name="passwordValidator"></param>
|
||||||
/// <param name="claimsIdentityFactory"></param>
|
/// <param name="claimsIdentityFactory"></param>
|
||||||
public UserManager(IUserStore<TUser> store,
|
public UserManager(IUserStore<TUser> store,
|
||||||
IOptions<IdentityOptions> optionsAccessor,
|
IOptions<IdentityOptions> optionsAccessor = null,
|
||||||
IPasswordHasher<TUser> passwordHasher,
|
IPasswordHasher<TUser> passwordHasher = null,
|
||||||
IEnumerable<IUserValidator<TUser>> userValidators,
|
IEnumerable<IUserValidator<TUser>> userValidators = null,
|
||||||
IEnumerable<IPasswordValidator<TUser>> passwordValidators,
|
IEnumerable<IPasswordValidator<TUser>> passwordValidators = null,
|
||||||
IUserNameNormalizer userNameNormalizer,
|
IUserNameNormalizer userNameNormalizer = null,
|
||||||
IEnumerable<IUserTokenProvider<TUser>> tokenProviders,
|
IdentityErrorDescriber errors = null,
|
||||||
IEnumerable<IIdentityMessageProvider> msgProviders)
|
IEnumerable<IUserTokenProvider<TUser>> tokenProviders = null,
|
||||||
|
IEnumerable<IIdentityMessageProvider> msgProviders = null)
|
||||||
{
|
{
|
||||||
if (store == null)
|
if (store == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(store));
|
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;
|
Store = store;
|
||||||
Options = optionsAccessor.Options;
|
Options = optionsAccessor?.Options ?? new IdentityOptions();
|
||||||
PasswordHasher = passwordHasher;
|
PasswordHasher = passwordHasher ?? new PasswordHasher<TUser>();
|
||||||
UserNameNormalizer = userNameNormalizer;
|
UserNameNormalizer = userNameNormalizer ?? new UpperInvariantUserNameNormalizer();
|
||||||
|
ErrorDescriber = errors ?? new IdentityErrorDescriber();
|
||||||
if (userValidators != null)
|
if (userValidators != null)
|
||||||
{
|
{
|
||||||
foreach (var v in userValidators)
|
foreach (var v in userValidators)
|
||||||
|
|
@ -136,6 +130,11 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IUserNameNormalizer UserNameNormalizer { get; set; }
|
public IUserNameNormalizer UserNameNormalizer { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to generate public API error messages
|
||||||
|
/// </summary>
|
||||||
|
public IdentityErrorDescriber ErrorDescriber { get; set; }
|
||||||
|
|
||||||
public IdentityOptions Options
|
public IdentityOptions Options
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|
@ -301,7 +300,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
|
|
||||||
private async Task<IdentityResult> ValidateUserInternal(TUser user, CancellationToken cancellationToken)
|
private async Task<IdentityResult> ValidateUserInternal(TUser user, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var errors = new List<string>();
|
var errors = new List<IdentityError>();
|
||||||
foreach (var v in UserValidators)
|
foreach (var v in UserValidators)
|
||||||
{
|
{
|
||||||
var result = await v.ValidateAsync(this, user, cancellationToken);
|
var result = await v.ValidateAsync(this, user, cancellationToken);
|
||||||
|
|
@ -315,7 +314,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
|
|
||||||
private async Task<IdentityResult> ValidatePasswordInternal(TUser user, string password, CancellationToken cancellationToken)
|
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)
|
foreach (var v in PasswordValidators)
|
||||||
{
|
{
|
||||||
var result = await v.ValidateAsync(this, user, password, cancellationToken);
|
var result = await v.ValidateAsync(this, user, password, cancellationToken);
|
||||||
|
|
@ -598,7 +597,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
var hash = await passwordStore.GetPasswordHashAsync(user, cancellationToken);
|
var hash = await passwordStore.GetPasswordHashAsync(user, cancellationToken);
|
||||||
if (hash != null)
|
if (hash != null)
|
||||||
{
|
{
|
||||||
return new IdentityResult(Resources.UserAlreadyHasPassword);
|
return IdentityResult.Failed(ErrorDescriber.UserAlreadyHasPassword());
|
||||||
}
|
}
|
||||||
var result = await UpdatePasswordInternal(passwordStore, user, password, cancellationToken);
|
var result = await UpdatePasswordInternal(passwordStore, user, password, cancellationToken);
|
||||||
if (!result.Succeeded)
|
if (!result.Succeeded)
|
||||||
|
|
@ -634,7 +633,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
}
|
}
|
||||||
return await UpdateAsync(user, cancellationToken);
|
return await UpdateAsync(user, cancellationToken);
|
||||||
}
|
}
|
||||||
return IdentityResult.Failed(Resources.PasswordMismatch);
|
return IdentityResult.Failed(ErrorDescriber.PasswordMismatch());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -766,7 +765,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
// Make sure the token is valid and the stamp matches
|
// Make sure the token is valid and the stamp matches
|
||||||
if (!await VerifyUserTokenAsync(user, Options.PasswordResetTokenProvider, "ResetPassword", token, cancellationToken))
|
if (!await VerifyUserTokenAsync(user, Options.PasswordResetTokenProvider, "ResetPassword", token, cancellationToken))
|
||||||
{
|
{
|
||||||
return IdentityResult.Failed(Resources.InvalidToken);
|
return IdentityResult.Failed(ErrorDescriber.InvalidToken());
|
||||||
}
|
}
|
||||||
var passwordStore = GetPasswordStore();
|
var passwordStore = GetPasswordStore();
|
||||||
var result = await UpdatePasswordInternal(passwordStore, user, newPassword, cancellationToken);
|
var result = await UpdatePasswordInternal(passwordStore, user, newPassword, cancellationToken);
|
||||||
|
|
@ -877,7 +876,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
var existingUser = await FindByLoginAsync(login.LoginProvider, login.ProviderKey, cancellationToken);
|
var existingUser = await FindByLoginAsync(login.LoginProvider, login.ProviderKey, cancellationToken);
|
||||||
if (existingUser != null)
|
if (existingUser != null)
|
||||||
{
|
{
|
||||||
return IdentityResult.Failed(Resources.ExternalLoginExists);
|
return IdentityResult.Failed(ErrorDescriber.LoginAlreadyAssociated());
|
||||||
}
|
}
|
||||||
await loginStore.AddLoginAsync(user, login, cancellationToken);
|
await loginStore.AddLoginAsync(user, login, cancellationToken);
|
||||||
return await UpdateAsync(user, cancellationToken);
|
return await UpdateAsync(user, cancellationToken);
|
||||||
|
|
@ -1082,7 +1081,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
var userRoles = await userRoleStore.GetRolesAsync(user, cancellationToken);
|
var userRoles = await userRoleStore.GetRolesAsync(user, cancellationToken);
|
||||||
if (userRoles.Contains(role))
|
if (userRoles.Contains(role))
|
||||||
{
|
{
|
||||||
return new IdentityResult(Resources.UserAlreadyInRole);
|
return IdentityResult.Failed(ErrorDescriber.UserAlreadyInRole(role));
|
||||||
}
|
}
|
||||||
await userRoleStore.AddToRoleAsync(user, role, cancellationToken);
|
await userRoleStore.AddToRoleAsync(user, role, cancellationToken);
|
||||||
return await UpdateAsync(user, cancellationToken);
|
return await UpdateAsync(user, cancellationToken);
|
||||||
|
|
@ -1113,7 +1112,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
if (userRoles.Contains(role))
|
if (userRoles.Contains(role))
|
||||||
{
|
{
|
||||||
return new IdentityResult(Resources.UserAlreadyInRole);
|
return IdentityResult.Failed(ErrorDescriber.UserAlreadyInRole(role));
|
||||||
}
|
}
|
||||||
await userRoleStore.AddToRoleAsync(user, role, cancellationToken);
|
await userRoleStore.AddToRoleAsync(user, role, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
@ -1138,7 +1137,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
}
|
}
|
||||||
if (!await userRoleStore.IsInRoleAsync(user, role, cancellationToken))
|
if (!await userRoleStore.IsInRoleAsync(user, role, cancellationToken))
|
||||||
{
|
{
|
||||||
return new IdentityResult(Resources.UserNotInRole);
|
return IdentityResult.Failed(ErrorDescriber.UserNotInRole(role));
|
||||||
}
|
}
|
||||||
await userRoleStore.RemoveFromRoleAsync(user, role, cancellationToken);
|
await userRoleStore.RemoveFromRoleAsync(user, role, cancellationToken);
|
||||||
return await UpdateAsync(user, cancellationToken);
|
return await UpdateAsync(user, cancellationToken);
|
||||||
|
|
@ -1168,7 +1167,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
if (!await userRoleStore.IsInRoleAsync(user, role, cancellationToken))
|
if (!await userRoleStore.IsInRoleAsync(user, role, cancellationToken))
|
||||||
{
|
{
|
||||||
return new IdentityResult(Resources.UserNotInRole);
|
return IdentityResult.Failed(ErrorDescriber.UserNotInRole(role));
|
||||||
}
|
}
|
||||||
await userRoleStore.RemoveFromRoleAsync(user, role, cancellationToken);
|
await userRoleStore.RemoveFromRoleAsync(user, role, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
@ -1313,7 +1312,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
}
|
}
|
||||||
if (!await VerifyUserTokenAsync(user, Options.EmailConfirmationTokenProvider, "Confirmation", token, cancellationToken))
|
if (!await VerifyUserTokenAsync(user, Options.EmailConfirmationTokenProvider, "Confirmation", token, cancellationToken))
|
||||||
{
|
{
|
||||||
return IdentityResult.Failed(Resources.InvalidToken);
|
return IdentityResult.Failed(ErrorDescriber.InvalidToken());
|
||||||
}
|
}
|
||||||
await store.SetEmailConfirmedAsync(user, true, cancellationToken);
|
await store.SetEmailConfirmedAsync(user, true, cancellationToken);
|
||||||
return await UpdateAsync(user, cancellationToken);
|
return await UpdateAsync(user, cancellationToken);
|
||||||
|
|
@ -1374,7 +1373,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
// Make sure the token is valid and the stamp matches
|
// Make sure the token is valid and the stamp matches
|
||||||
if (!await VerifyUserTokenAsync(user, Options.ChangeEmailTokenProvider, GetChangeEmailPurpose(newEmail), token, cancellationToken))
|
if (!await VerifyUserTokenAsync(user, Options.ChangeEmailTokenProvider, GetChangeEmailPurpose(newEmail), token, cancellationToken))
|
||||||
{
|
{
|
||||||
return IdentityResult.Failed(Resources.InvalidToken);
|
return IdentityResult.Failed(ErrorDescriber.InvalidToken());
|
||||||
}
|
}
|
||||||
var store = GetEmailStore();
|
var store = GetEmailStore();
|
||||||
await store.SetEmailAsync(user, newEmail, cancellationToken);
|
await store.SetEmailAsync(user, newEmail, cancellationToken);
|
||||||
|
|
@ -1453,7 +1452,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
}
|
}
|
||||||
if (!await VerifyChangePhoneNumberTokenAsync(user, token, phoneNumber, cancellationToken))
|
if (!await VerifyChangePhoneNumberTokenAsync(user, token, phoneNumber, cancellationToken))
|
||||||
{
|
{
|
||||||
return IdentityResult.Failed(Resources.InvalidToken);
|
return IdentityResult.Failed(ErrorDescriber.InvalidToken());
|
||||||
}
|
}
|
||||||
await store.SetPhoneNumberAsync(user, phoneNumber, cancellationToken);
|
await store.SetPhoneNumberAsync(user, phoneNumber, cancellationToken);
|
||||||
await store.SetPhoneNumberConfirmedAsync(user, true, cancellationToken);
|
await store.SetPhoneNumberConfirmedAsync(user, true, cancellationToken);
|
||||||
|
|
@ -1890,7 +1889,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
}
|
}
|
||||||
if (!await store.GetLockoutEnabledAsync(user, cancellationToken).ConfigureAwait((false)))
|
if (!await store.GetLockoutEnabledAsync(user, cancellationToken).ConfigureAwait((false)))
|
||||||
{
|
{
|
||||||
return IdentityResult.Failed(Resources.LockoutNotEnabled);
|
return IdentityResult.Failed(ErrorDescriber.UserLockoutNotEnabled());
|
||||||
}
|
}
|
||||||
await store.SetLockoutEndDateAsync(user, lockoutEnd, cancellationToken);
|
await store.SetLockoutEndDateAsync(user, lockoutEnd, cancellationToken);
|
||||||
return await UpdateAsync(user, cancellationToken);
|
return await UpdateAsync(user, cancellationToken);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
|
||||||
#if ASPNET50
|
#if ASPNET50
|
||||||
using System.Net.Mail;
|
using System.Net.Mail;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -19,6 +18,13 @@ namespace Microsoft.AspNet.Identity
|
||||||
/// <typeparam name="TUser"></typeparam>
|
/// <typeparam name="TUser"></typeparam>
|
||||||
public class UserValidator<TUser> : IUserValidator<TUser> where TUser : class
|
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>
|
/// <summary>
|
||||||
/// Validates a user before saving
|
/// Validates a user before saving
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -37,7 +43,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("user");
|
throw new ArgumentNullException("user");
|
||||||
}
|
}
|
||||||
var errors = new List<string>();
|
var errors = new List<IdentityError>();
|
||||||
await ValidateUserName(manager, user, errors);
|
await ValidateUserName(manager, user, errors);
|
||||||
if (manager.Options.User.RequireUniqueEmail)
|
if (manager.Options.User.RequireUniqueEmail)
|
||||||
{
|
{
|
||||||
|
|
@ -46,16 +52,16 @@ namespace Microsoft.AspNet.Identity
|
||||||
return errors.Count > 0 ? IdentityResult.Failed(errors.ToArray()) : IdentityResult.Success;
|
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);
|
var userName = await manager.GetUserNameAsync(user);
|
||||||
if (string.IsNullOrWhiteSpace(userName))
|
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))
|
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
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -63,18 +69,18 @@ namespace Microsoft.AspNet.Identity
|
||||||
if (owner != null &&
|
if (owner != null &&
|
||||||
!string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user)))
|
!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
|
// 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);
|
var email = await manager.GetEmailAsync(user);
|
||||||
if (string.IsNullOrWhiteSpace(email))
|
if (string.IsNullOrWhiteSpace(email))
|
||||||
{
|
{
|
||||||
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.PropertyTooShort, "Email"));
|
errors.Add(Describer.InvalidEmail(email));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#if ASPNET50
|
#if ASPNET50
|
||||||
|
|
@ -84,7 +90,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
}
|
}
|
||||||
catch (FormatException)
|
catch (FormatException)
|
||||||
{
|
{
|
||||||
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.InvalidEmail, email));
|
errors.Add(Describer.InvalidEmail(email));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -92,7 +98,7 @@ namespace Microsoft.AspNet.Identity
|
||||||
if (owner != null &&
|
if (owner != null &&
|
||||||
!string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user)))
|
!string.Equals(await manager.GetUserIdAsync(owner), await manager.GetUserIdAsync(user)))
|
||||||
{
|
{
|
||||||
errors.Add(String.Format(CultureInfo.CurrentCulture, Resources.DuplicateEmail, email));
|
errors.Add(Describer.DuplicateEmail(email));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,10 +50,10 @@ namespace Microsoft.AspNet.Identity.InMemory.Test
|
||||||
var signInManager = app.ApplicationServices.GetRequiredService<SignInManager<ApplicationUser>>();
|
var signInManager = app.ApplicationServices.GetRequiredService<SignInManager<ApplicationUser>>();
|
||||||
|
|
||||||
IdentityResultAssert.IsSuccess(await userManager.CreateAsync(user, password));
|
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
|
||||||
Assert.Equal(SignInStatus.Success, result);
|
Assert.True(result.Succeeded);
|
||||||
context.VerifyAll();
|
context.VerifyAll();
|
||||||
response.VerifyAll();
|
response.VerifyAll();
|
||||||
contextAccessor.VerifyAll();
|
contextAccessor.VerifyAll();
|
||||||
|
|
|
||||||
|
|
@ -226,18 +226,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
|
|
||||||
private class MyUserManager : UserManager<TestUser>
|
private class MyUserManager : UserManager<TestUser>
|
||||||
{
|
{
|
||||||
public MyUserManager(IUserStore<TestUser> store,
|
public MyUserManager(IUserStore<TestUser> store) : base(store) { }
|
||||||
IOptions<IdentityOptions> optionsAccessor,
|
|
||||||
IPasswordHasher<TestUser> passwordHasher,
|
|
||||||
IEnumerable<IUserValidator<TestUser>> userValidators,
|
|
||||||
IEnumerable<IPasswordValidator<TestUser>> passwordValidators,
|
|
||||||
IUserNameNormalizer userNameNormalizer,
|
|
||||||
IEnumerable<IUserTokenProvider<TestUser>> tokenProviders,
|
|
||||||
IEnumerable<IIdentityMessageProvider> msgProviders) :
|
|
||||||
base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, userNameNormalizer, tokenProviders, msgProviders)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MyRoleManager : RoleManager<TestRole>
|
private class MyRoleManager : RoleManager<TestRole>
|
||||||
|
|
|
||||||
|
|
@ -13,17 +13,15 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
{
|
{
|
||||||
var result = new IdentityResult();
|
var result = new IdentityResult();
|
||||||
Assert.False(result.Succeeded);
|
Assert.False(result.Succeeded);
|
||||||
Assert.Equal(1, result.Errors.Count());
|
Assert.Equal(0, result.Errors.Count());
|
||||||
Assert.Equal("An unknown failure has occured.", result.Errors.First());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void NullErrorListUsesDefaultError()
|
public void NullFailedUsesEmptyErrors()
|
||||||
{
|
{
|
||||||
var result = new IdentityResult(null);
|
var result = IdentityResult.Failed();
|
||||||
Assert.False(result.Succeeded);
|
Assert.False(result.Succeeded);
|
||||||
Assert.Equal(1, result.Errors.Count());
|
Assert.Equal(0, result.Errors.Count());
|
||||||
Assert.Equal("An unknown failure has occured.", result.Errors.First());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -133,13 +134,18 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
{
|
{
|
||||||
errors.Add(upperError);
|
errors.Add(upperError);
|
||||||
}
|
}
|
||||||
|
var result = await valid.ValidateAsync(manager, null, input);
|
||||||
if (errors.Count == 0)
|
if (errors.Count == 0)
|
||||||
{
|
{
|
||||||
IdentityResultAssert.IsSuccess(await valid.ValidateAsync(manager, null, input));
|
IdentityResultAssert.IsSuccess(result);
|
||||||
}
|
}
|
||||||
else
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var result = await validator.ValidateAsync(manager, user);
|
var result = await validator.ValidateAsync(manager, user);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
IdentityResultAssert.IsFailure(result, "Name cannot be null or empty.");
|
IdentityResultAssert.IsFailure(result, IdentityErrorDescriber.Default.InvalidRoleName(input));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNet.Http;
|
|
||||||
using Microsoft.AspNet.Http.Security;
|
|
||||||
using Microsoft.Framework.DependencyInjection;
|
|
||||||
using Microsoft.Framework.OptionsModel;
|
|
||||||
using Moq;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Http.Security;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.OptionsModel;
|
||||||
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Identity.Test
|
namespace Microsoft.AspNet.Identity.Test
|
||||||
|
|
@ -75,8 +75,6 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var context = new Mock<HttpContext>();
|
var context = new Mock<HttpContext>();
|
||||||
contextAccessor.Setup(a => a.Value).Returns(context.Object);
|
contextAccessor.Setup(a => a.Value).Returns(context.Object);
|
||||||
Assert.Throws<ArgumentNullException>("claimsFactory", () => new SignInManager<IdentityUser>(userManager, contextAccessor.Object, null, null));
|
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)
|
//TODO: Mock fails in K (this works fine in net45)
|
||||||
|
|
@ -132,7 +130,8 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false);
|
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(SignInStatus.LockedOut, result);
|
Assert.False(result.Succeeded);
|
||||||
|
Assert.True(result.IsLockedOut);
|
||||||
manager.VerifyAll();
|
manager.VerifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -167,7 +166,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false);
|
var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(SignInStatus.Success, result);
|
Assert.True(result.Succeeded);
|
||||||
manager.VerifyAll();
|
manager.VerifyAll();
|
||||||
context.VerifyAll();
|
context.VerifyAll();
|
||||||
response.VerifyAll();
|
response.VerifyAll();
|
||||||
|
|
@ -203,7 +202,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false);
|
var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(SignInStatus.Success, result);
|
Assert.True(result.Succeeded);
|
||||||
manager.VerifyAll();
|
manager.VerifyAll();
|
||||||
context.VerifyAll();
|
context.VerifyAll();
|
||||||
response.VerifyAll();
|
response.VerifyAll();
|
||||||
|
|
@ -251,7 +250,8 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false);
|
var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(SignInStatus.RequiresVerification, result);
|
Assert.False(result.Succeeded);
|
||||||
|
Assert.True(result.RequiresTwoFactor);
|
||||||
manager.VerifyAll();
|
manager.VerifyAll();
|
||||||
context.VerifyAll();
|
context.VerifyAll();
|
||||||
response.VerifyAll();
|
response.VerifyAll();
|
||||||
|
|
@ -297,7 +297,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var result = await helper.ExternalLoginSignInAsync(loginProvider, providerKey, isPersistent);
|
var result = await helper.ExternalLoginSignInAsync(loginProvider, providerKey, isPersistent);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(SignInStatus.Success, result);
|
Assert.True(result.Succeeded);
|
||||||
manager.VerifyAll();
|
manager.VerifyAll();
|
||||||
context.VerifyAll();
|
context.VerifyAll();
|
||||||
response.VerifyAll();
|
response.VerifyAll();
|
||||||
|
|
@ -382,7 +382,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var result = await helper.TwoFactorSignInAsync(provider, code, isPersistent, rememberClient);
|
var result = await helper.TwoFactorSignInAsync(provider, code, isPersistent, rememberClient);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(SignInStatus.Success, result);
|
Assert.True(result.Succeeded);
|
||||||
manager.VerifyAll();
|
manager.VerifyAll();
|
||||||
context.VerifyAll();
|
context.VerifyAll();
|
||||||
response.VerifyAll();
|
response.VerifyAll();
|
||||||
|
|
@ -466,7 +466,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false);
|
var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(SignInStatus.Success, result);
|
Assert.True(result.Succeeded);
|
||||||
manager.VerifyAll();
|
manager.VerifyAll();
|
||||||
context.VerifyAll();
|
context.VerifyAll();
|
||||||
response.VerifyAll();
|
response.VerifyAll();
|
||||||
|
|
@ -530,7 +530,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false);
|
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(SignInStatus.Failure, result);
|
Assert.False(result.Succeeded);
|
||||||
manager.VerifyAll();
|
manager.VerifyAll();
|
||||||
context.VerifyAll();
|
context.VerifyAll();
|
||||||
contextAccessor.VerifyAll();
|
contextAccessor.VerifyAll();
|
||||||
|
|
@ -556,7 +556,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var result = await helper.PasswordSignInAsync("bogus", "bogus", false, false);
|
var result = await helper.PasswordSignInAsync("bogus", "bogus", false, false);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(SignInStatus.Failure, result);
|
Assert.False(result.Succeeded);
|
||||||
manager.VerifyAll();
|
manager.VerifyAll();
|
||||||
context.VerifyAll();
|
context.VerifyAll();
|
||||||
contextAccessor.VerifyAll();
|
contextAccessor.VerifyAll();
|
||||||
|
|
@ -592,7 +592,8 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, true);
|
var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, true);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(SignInStatus.LockedOut, result);
|
Assert.False(result.Succeeded);
|
||||||
|
Assert.True(result.IsLockedOut);
|
||||||
manager.VerifyAll();
|
manager.VerifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -631,7 +632,9 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var result = await helper.PasswordSignInAsync(user, "password", false, false);
|
var result = await helper.PasswordSignInAsync(user, "password", false, false);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(confirmed ? SignInStatus.Success : SignInStatus.NotAllowed, result);
|
|
||||||
|
Assert.Equal(confirmed, result.Succeeded);
|
||||||
|
Assert.NotEqual(confirmed, result.IsNotAllowed);
|
||||||
manager.VerifyAll();
|
manager.VerifyAll();
|
||||||
context.VerifyAll();
|
context.VerifyAll();
|
||||||
response.VerifyAll();
|
response.VerifyAll();
|
||||||
|
|
@ -670,7 +673,8 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var result = await helper.PasswordSignInAsync(user, "password", false, false);
|
var result = await helper.PasswordSignInAsync(user, "password", false, false);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(confirmed ? SignInStatus.Success : SignInStatus.NotAllowed, result);
|
Assert.Equal(confirmed, result.Succeeded);
|
||||||
|
Assert.NotEqual(confirmed, result.IsNotAllowed);
|
||||||
manager.VerifyAll();
|
manager.VerifyAll();
|
||||||
context.VerifyAll();
|
context.VerifyAll();
|
||||||
response.VerifyAll();
|
response.VerifyAll();
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,15 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
// 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.
|
// 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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||||
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Identity.Test
|
namespace Microsoft.AspNet.Identity.Test
|
||||||
|
|
@ -21,10 +20,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
{
|
{
|
||||||
public IUserStore<TestUser> StorePublic { get { return Store; } }
|
public IUserStore<TestUser> StorePublic { get { return Store; } }
|
||||||
|
|
||||||
public TestManager(IUserStore<TestUser> store, IOptions<IdentityOptions> optionsAccessor,
|
public TestManager(IUserStore<TestUser> store) : base(store) { }
|
||||||
IPasswordHasher<TestUser> passwordHasher, IEnumerable<IUserValidator<TestUser>> userValidator,
|
|
||||||
IEnumerable<IPasswordValidator<TestUser>> passwordValidator)
|
|
||||||
: base(store, optionsAccessor, passwordHasher, userValidator, passwordValidator, null, null, null) { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -36,8 +32,6 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
services.AddIdentity<TestUser, IdentityRole>();
|
services.AddIdentity<TestUser, IdentityRole>();
|
||||||
var manager = services.BuildServiceProvider().GetRequiredService<TestManager>();
|
var manager = services.BuildServiceProvider().GetRequiredService<TestManager>();
|
||||||
Assert.NotNull(manager.PasswordHasher);
|
Assert.NotNull(manager.PasswordHasher);
|
||||||
Assert.Equal(1, manager.PasswordValidators.Count);
|
|
||||||
Assert.Equal(1, manager.UserValidators.Count);
|
|
||||||
Assert.NotNull(manager.StorePublic);
|
Assert.NotNull(manager.StorePublic);
|
||||||
Assert.NotNull(manager.Options);
|
Assert.NotNull(manager.Options);
|
||||||
}
|
}
|
||||||
|
|
@ -66,7 +60,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var store = new Mock<IUserStore<TestUser>>();
|
var store = new Mock<IUserStore<TestUser>>();
|
||||||
var user = new TestUser { UserName = "Foo" };
|
var user = new TestUser { UserName = "Foo" };
|
||||||
store.Setup(s => s.DeleteAsync(user, CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
|
store.Setup(s => s.DeleteAsync(user, CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
|
||||||
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
|
var userManager = MockHelpers.TestUserManager(store.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await userManager.DeleteAsync(user);
|
var result = await userManager.DeleteAsync(user);
|
||||||
|
|
@ -100,7 +94,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var store = new Mock<IUserStore<TestUser>>();
|
var store = new Mock<IUserStore<TestUser>>();
|
||||||
var user = new TestUser();
|
var user = new TestUser();
|
||||||
store.Setup(s => s.SetUserNameAsync(user, It.IsAny<string>(), CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
|
store.Setup(s => s.SetUserNameAsync(user, It.IsAny<string>(), CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable();
|
||||||
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
|
var userManager = MockHelpers.TestUserManager(store.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await userManager.SetUserNameAsync(user, "foo");
|
var result = await userManager.SetUserNameAsync(user, "foo");
|
||||||
|
|
@ -207,7 +201,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var result = await userManager.AddToRolesAsync(user, roles);
|
var result = await userManager.AddToRolesAsync(user, roles);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
IdentityResultAssert.IsFailure(result, "User already in role.");
|
IdentityResultAssert.IsFailure(result, IdentityErrorDescriber.Default.UserAlreadyInRole("B"));
|
||||||
store.VerifyAll();
|
store.VerifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -269,7 +263,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var result = await userManager.RemoveFromRolesAsync(user, roles);
|
var result = await userManager.RemoveFromRolesAsync(user, roles);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
IdentityResultAssert.IsFailure(result, "User is not in role.");
|
IdentityResultAssert.IsFailure(result, IdentityErrorDescriber.Default.UserNotInRole("B"));
|
||||||
store.VerifyAll();
|
store.VerifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -284,7 +278,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
.Returns(Task.FromResult(0))
|
.Returns(Task.FromResult(0))
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
store.Setup(s => s.UpdateAsync(user, 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);
|
var userManager = MockHelpers.TestUserManager(store.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await userManager.AddClaimsAsync(user, claims);
|
var result = await userManager.AddClaimsAsync(user, claims);
|
||||||
|
|
@ -305,7 +299,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
.Returns(Task.FromResult(0))
|
.Returns(Task.FromResult(0))
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
store.Setup(s => s.UpdateAsync(user, 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);
|
var userManager = MockHelpers.TestUserManager(store.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await userManager.AddClaimAsync(user, claim);
|
var result = await userManager.AddClaimAsync(user, claim);
|
||||||
|
|
@ -326,7 +320,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
store.Setup(s => s.ReplaceClaimAsync(user, It.IsAny<Claim>(), It.IsAny<Claim>(), CancellationToken.None))
|
store.Setup(s => s.ReplaceClaimAsync(user, It.IsAny<Claim>(), It.IsAny<Claim>(), CancellationToken.None))
|
||||||
.Returns(Task.FromResult(0))
|
.Returns(Task.FromResult(0))
|
||||||
.Verifiable();
|
.Verifiable();
|
||||||
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
|
var userManager = MockHelpers.TestUserManager(store.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await userManager.ReplaceClaimAsync(user, claim, newClaim);
|
var result = await userManager.ReplaceClaimAsync(user, claim, newClaim);
|
||||||
|
|
@ -533,17 +527,11 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
public async Task ManagerPublicNullChecks()
|
public async Task ManagerPublicNullChecks()
|
||||||
{
|
{
|
||||||
var store = new NotImplementedStore();
|
var store = new NotImplementedStore();
|
||||||
var optionsAccessor = new OptionsManager<IdentityOptions>(null);
|
|
||||||
var passwordHasher = new PasswordHasher<TestUser>(new PasswordHasherOptionsAccessor());
|
|
||||||
|
|
||||||
Assert.Throws<ArgumentNullException>("store",
|
Assert.Throws<ArgumentNullException>("store",
|
||||||
() => new UserManager<TestUser>(null, null, null, null, null, null, null, null));
|
() => new UserManager<TestUser>(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));
|
|
||||||
|
|
||||||
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.PasswordHasher = null);
|
||||||
Assert.Throws<ArgumentNullException>("value", () => manager.Options = null);
|
Assert.Throws<ArgumentNullException>("value", () => manager.Options = null);
|
||||||
|
|
@ -714,7 +702,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
|
|
||||||
private class BadPasswordValidator<TUser> : IPasswordValidator<TUser> where TUser : class
|
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))
|
public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user, string password, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
|
|
@ -1239,5 +1227,51 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
throw new NotImplementedException();
|
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(user.Email, 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) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var result = await validator.ValidateAsync(manager, user);
|
var result = await validator.ValidateAsync(manager, user);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
IdentityResultAssert.IsFailure(result, "UserName cannot be null or empty.");
|
IdentityResultAssert.IsFailure(result, IdentityErrorDescriber.Default.InvalidUserName(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,16 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
{
|
{
|
||||||
Assert.NotNull(result);
|
Assert.NotNull(result);
|
||||||
Assert.False(result.Succeeded);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -14,20 +14,10 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
public static Mock<UserManager<TUser>> MockUserManager<TUser>() where TUser : class
|
public static Mock<UserManager<TUser>> MockUserManager<TUser>() where TUser : class
|
||||||
{
|
{
|
||||||
var store = new Mock<IUserStore<TUser>>();
|
var store = new Mock<IUserStore<TUser>>();
|
||||||
var options = new OptionsManager<IdentityOptions>(null);
|
var mgr = new Mock<UserManager<TUser>>(store.Object, null, null, null, null, null, null, null, null);
|
||||||
var userValidators = new List<IUserValidator<TUser>>();
|
mgr.Object.UserValidators.Add(new UserValidator<TUser>());
|
||||||
userValidators.Add(new UserValidator<TUser>());
|
mgr.Object.PasswordValidators.Add(new PasswordValidator<TUser>());
|
||||||
var pwdValidators = new List<IPasswordValidator<TUser>>();
|
return mgr;
|
||||||
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>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Mock<RoleManager<TRole>> MockRoleManager<TRole>() where TRole : class
|
public static Mock<RoleManager<TRole>> MockRoleManager<TRole>() where TRole : class
|
||||||
|
|
@ -35,7 +25,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var store = new Mock<IRoleStore<TRole>>();
|
var store = new Mock<IRoleStore<TRole>>();
|
||||||
var roles = new List<IRoleValidator<TRole>>();
|
var roles = new List<IRoleValidator<TRole>>();
|
||||||
roles.Add(new RoleValidator<TRole>());
|
roles.Add(new RoleValidator<TRole>());
|
||||||
return new Mock<RoleManager<TRole>>(store.Object, roles);
|
return new Mock<RoleManager<TRole>>(store.Object, roles, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UserManager<TUser> TestUserManager<TUser>() where TUser : class
|
public static UserManager<TUser> TestUserManager<TUser>() where TUser : class
|
||||||
|
|
@ -45,10 +35,8 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
|
|
||||||
public static UserManager<TUser> TestUserManager<TUser>(IUserStore<TUser> store) where TUser : class
|
public static UserManager<TUser> TestUserManager<TUser>(IUserStore<TUser> store) where TUser : class
|
||||||
{
|
{
|
||||||
var options = new OptionsManager<IdentityOptions>(null);
|
var validator = new Mock<IUserValidator<TUser>>();
|
||||||
var validator = new Mock<UserValidator<TUser>>();
|
var userManager = new UserManager<TUser>(store);
|
||||||
var userManager = new UserManager<TUser>(store, options, new PasswordHasher<TUser>(new PasswordHasherOptionsAccessor()),
|
|
||||||
null, null, new UpperInvariantUserNameNormalizer(), null, null);
|
|
||||||
userManager.UserValidators.Add(validator.Object);
|
userManager.UserValidators.Add(validator.Object);
|
||||||
userManager.PasswordValidators.Add(new PasswordValidator<TUser>());
|
userManager.PasswordValidators.Add(new PasswordValidator<TUser>());
|
||||||
validator.Setup(v => v.ValidateAsync(userManager, It.IsAny<TUser>(), CancellationToken.None))
|
validator.Setup(v => v.ValidateAsync(userManager, It.IsAny<TUser>(), CancellationToken.None))
|
||||||
|
|
|
||||||
|
|
@ -14,5 +14,6 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
|
|
||||||
public string Id { get; private set; }
|
public string Id { get; private set; }
|
||||||
public string UserName { get; set; }
|
public string UserName { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -182,7 +182,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var manager = CreateManager();
|
var manager = CreateManager();
|
||||||
var user = CreateTestUser();
|
var user = CreateTestUser();
|
||||||
manager.Options.User.RequireUniqueEmail = true;
|
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
|
#if ASPNET50
|
||||||
|
|
@ -194,7 +194,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var manager = CreateManager();
|
var manager = CreateManager();
|
||||||
var user = new TUser() { UserName = "UpdateBlocked", Email = email };
|
var user = new TUser() { UserName = "UpdateBlocked", Email = email };
|
||||||
manager.Options.User.RequireUniqueEmail = true;
|
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
|
#endif
|
||||||
|
|
||||||
|
|
@ -414,7 +414,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
var user = CreateTestUser();
|
var user = CreateTestUser();
|
||||||
var user2 = new TUser() { UserName = user.UserName };
|
var user2 = new TUser() { UserName = user.UserName };
|
||||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
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]
|
[Fact]
|
||||||
|
|
@ -440,7 +440,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
user.Email = email;
|
user.Email = email;
|
||||||
user2.Email = email;
|
user2.Email = email;
|
||||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
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]
|
[Fact]
|
||||||
|
|
@ -465,7 +465,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
|
||||||
IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login));
|
IdentityResultAssert.IsSuccess(await manager.AddLoginAsync(user, login));
|
||||||
var result = 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());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Email tests
|
// Email tests
|
||||||
|
|
@ -842,7 +842,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
private class AlwaysBadValidator : IUserValidator<TUser>, IRoleValidator<TRole>,
|
private class AlwaysBadValidator : IUserValidator<TUser>, IRoleValidator<TRole>,
|
||||||
IPasswordValidator<TUser>
|
IPasswordValidator<TUser>
|
||||||
{
|
{
|
||||||
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))
|
public Task<IdentityResult> ValidateAsync(UserManager<TUser> manager, TUser user, string password, CancellationToken cancellationToken = default(CancellationToken))
|
||||||
{
|
{
|
||||||
|
|
@ -1142,7 +1142,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user));
|
IdentityResultAssert.IsSuccess(await userMgr.CreateAsync(user));
|
||||||
IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role));
|
IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role));
|
||||||
var result = await userMgr.RemoveFromRoleAsync(user, role.Name);
|
var result = await userMgr.RemoveFromRoleAsync(user, role.Name);
|
||||||
IdentityResultAssert.IsFailure(result, "User is not in role.");
|
IdentityResultAssert.IsFailure(result, IdentityErrorDescriber.Default.UserNotInRole(role.Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -1157,7 +1157,7 @@ namespace Microsoft.AspNet.Identity.Test
|
||||||
IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role));
|
IdentityResultAssert.IsSuccess(await roleMgr.CreateAsync(role));
|
||||||
IdentityResultAssert.IsSuccess(await userMgr.AddToRoleAsync(user, role.Name));
|
IdentityResultAssert.IsSuccess(await userMgr.AddToRoleAsync(user, role.Name));
|
||||||
Assert.True(await userMgr.IsInRoleAsync(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));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue