Replace IIdentityMessageService with IUserMessageProvider

This commit is contained in:
Hao Kung 2014-11-12 17:10:15 -08:00
parent 85530742ef
commit 66f9803d06
23 changed files with 414 additions and 266 deletions

View File

@ -87,7 +87,13 @@ namespace IdentitySample.Models
{
var code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Context.Request.Scheme);
await UserManager.SendEmailAsync(user, "Confirm your account", "Please confirm your account by clicking this link: <a href=\"" + callbackUrl + "\">link</a>");
var email = new IdentityMessage
{
Destination = model.Email,
Subject = "Confirm your account",
Body = "Please confirm your account by clicking this link: <a href=\"" + callbackUrl + "\">link</a>"
};
await UserManager.SendMessageAsync("Email", email);
ViewBag.Link = callbackUrl;
return View("DisplayEmail");
}
@ -240,7 +246,13 @@ namespace IdentitySample.Models
var code = await UserManager.GeneratePasswordResetTokenAsync(user);
var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Context.Request.Scheme);
await UserManager.SendEmailAsync(user, "Reset Password", "Please reset your password by clicking here: <a href=\"" + callbackUrl + "\">link</a>");
var email = new IdentityMessage
{
Destination = model.Email,
Subject = "Reset Password",
Body = "Please reset your password by clicking here: <a href=\"" + callbackUrl + "\">link</a>"
};
await UserManager.SendMessageAsync("Email", email);
ViewBag.Link = callbackUrl;
return View("ForgotPasswordConfirmation");
}

View File

@ -95,16 +95,14 @@ namespace IdentitySample
return View(model);
}
// Generate the token and send it
var code = await UserManager.GenerateChangePhoneNumberTokenAsync(await GetCurrentUserAsync(), model.Number);
if (UserManager.SmsService != null)
var user = await GetCurrentUserAsync();
var code = await UserManager.GenerateChangePhoneNumberTokenAsync(user, model.Number);
var message = new IdentityMessage
{
var message = new IdentityMessage
{
Destination = model.Number,
Body = "Your security code is: " + code
};
await UserManager.SmsService.SendAsync(message);
}
Destination = model.Number,
Body = "Your security code is: " + code
};
await UserManager.SendMessageAsync("SMS", message);
return RedirectToAction("VerifyPhoneNumber", new { PhoneNumber = model.Number });
}

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<DevelopmentServerPort>63045</DevelopmentServerPort>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
@ -11,4 +12,4 @@
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>
</Project>

View File

@ -2,9 +2,6 @@
"DefaultAdminUsername": "Administrator",
"DefaultAdminPassword": "YouShouldChangeThisPassword1!",
"Data": {
"DefaultConnection": {
"Connectionstring": "Server=(localdb)\\mssqllocaldb;Database=IdentitySample-8-12-14;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"IdentityConnection": {
"Connectionstring": "Server=(localdb)\\mssqllocaldb;Database=IdentityMvc-8-12-14;Trusted_Connection=True;MultipleActiveResultSets=true"
}

View File

@ -7,6 +7,8 @@ using Microsoft.AspNet.Routing;
using Microsoft.Data.Entity;
using Microsoft.Framework.ConfigurationModel;
using Microsoft.Framework.DependencyInjection;
using System.Threading.Tasks;
using System.Threading;
namespace IdentitySamples
{
@ -36,12 +38,11 @@ namespace IdentitySamples
options.DefaultAdminPassword = Configuration.Get("DefaultAdminPassword");
});
services.AddIdentity<ApplicationUser, IdentityRole>(Configuration, options =>
{
options.SecurityStampValidationInterval = TimeSpan.FromMinutes(20);
})
services.AddIdentity<ApplicationUser, IdentityRole>(Configuration)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
.AddDefaultTokenProviders()
.AddMessageProvider<EmailMessageProvider>()
.AddMessageProvider<SmsMessageProvider>();
services.ConfigureFacebookAuthentication(options =>
{
@ -80,5 +81,40 @@ namespace IdentitySamples
//Populates the Admin user and role
SampleData.InitializeIdentityDatabaseAsync(app.ApplicationServices).Wait();
}
public class EmailMessageProvider : IIdentityMessageProvider
{
public string Name
{
get
{
return "Email";
}
}
public Task SendAsync(IdentityMessage message, CancellationToken cancellationToken = default(CancellationToken))
{
// Plug in your service
return Task.FromResult(0);
}
}
public class SmsMessageProvider : IIdentityMessageProvider
{
public string Name
{
get
{
return "SMS";
}
}
public Task SendAsync(IdentityMessage message, CancellationToken cancellationToken = default(CancellationToken))
{
// Plug in your service
return Task.FromResult(0);
}
}
}
}

View File

@ -10,6 +10,8 @@ namespace Microsoft.AspNet.Identity
{
public string Name { get; set; } = Resources.DefaultEmailTokenProviderName;
public string MessageProvider { get; set; } = "Email";
public string Subject { get; set; } = "Security Code";
/// <summary>
@ -48,7 +50,7 @@ namespace Microsoft.AspNet.Identity
CancellationToken cancellationToken = default(CancellationToken))
{
var email = await manager.GetEmailAsync(user, cancellationToken);
return !String.IsNullOrWhiteSpace(email) && await manager.IsEmailConfirmedAsync(user, cancellationToken);
return !string.IsNullOrWhiteSpace(email) && await manager.IsEmailConfirmedAsync(user, cancellationToken);
}
/// <summary>
@ -72,14 +74,20 @@ namespace Microsoft.AspNet.Identity
/// <param name="manager"></param>
/// <param name="user"></param>
/// <returns></returns>
public override Task NotifyAsync(string token, UserManager<TUser> manager, TUser user,
public override async Task NotifyAsync(string token, UserManager<TUser> manager, TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
if (manager == null)
{
throw new ArgumentNullException("manager");
throw new ArgumentNullException(nameof(manager));
}
return manager.SendEmailAsync(user, Options.Subject, String.Format(CultureInfo.CurrentCulture, Options.BodyFormat, token), cancellationToken);
var msg = new IdentityMessage
{
Destination = await manager.GetEmailAsync(user, cancellationToken),
Subject = Options.Subject,
Body = string.Format(CultureInfo.CurrentCulture, Options.BodyFormat, token)
};
await manager.SendMessageAsync(Options.MessageProvider, msg, cancellationToken);
}
}
}

View File

@ -6,17 +6,10 @@ using System.Threading.Tasks;
namespace Microsoft.AspNet.Identity
{
/// <summary>
/// Expose a way to send messages (email/txt)
/// </summary>
public interface IIdentityMessageService
public interface IIdentityMessageProvider
{
/// <summary>
/// This method should send the message
/// </summary>
/// <param name="message"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
string Name { get; }
Task SendAsync(IdentityMessage message, CancellationToken cancellationToken = default(CancellationToken));
}
}

View File

@ -19,10 +19,50 @@ namespace Microsoft.AspNet.Identity
public Type RoleType { get; private set; }
public IServiceCollection Services { get; private set; }
private IdentityBuilder AddScoped(Type serviceType, Type concreteType)
{
Services.AddScoped(serviceType, concreteType);
return this;
}
public IdentityBuilder AddUserValidator<T>() where T : class
{
return AddScoped(typeof(IUserValidator<>).MakeGenericType(UserType), typeof(T));
}
public IdentityBuilder AddRoleValidator<T>() where T : class
{
return AddScoped(typeof(IRoleValidator<>).MakeGenericType(RoleType), typeof(T));
}
public IdentityBuilder AddPasswordValidator<T>() where T : class
{
return AddScoped(typeof(IPasswordValidator<>).MakeGenericType(UserType), typeof(T));
}
public IdentityBuilder AddUserStore<T>() where T : class
{
return AddScoped(typeof(IUserStore<>).MakeGenericType(UserType), typeof(T));
}
public IdentityBuilder AddRoleStore<T>() where T : class
{
return AddScoped(typeof(IRoleStore<>).MakeGenericType(RoleType), typeof(T));
}
public IdentityBuilder AddTokenProvider<TProvider>() where TProvider : class
{
return AddTokenProvider(typeof(TProvider));
}
public IdentityBuilder AddTokenProvider(Type provider)
{
Services.AddScoped(typeof(IUserTokenProvider<>).MakeGenericType(UserType), provider);
return this;
return AddScoped(typeof(IUserTokenProvider<>).MakeGenericType(UserType), provider);
}
public IdentityBuilder AddMessageProvider<TProvider>() where TProvider : IIdentityMessageProvider
{
return AddScoped(typeof(IIdentityMessageProvider), typeof(TProvider));
}
public IdentityBuilder AddDefaultTokenProviders()
@ -36,62 +76,5 @@ namespace Microsoft.AspNet.Identity
.AddTokenProvider(typeof(PhoneNumberTokenProvider<>).MakeGenericType(UserType))
.AddTokenProvider(typeof(EmailTokenProvider<>).MakeGenericType(UserType));
}
}
public class IdentityBuilder<TUser, TRole> : IdentityBuilder where TUser : class where TRole : class
{
public IdentityBuilder(IServiceCollection services) : base(typeof(TUser), typeof(TRole), services) { }
public IdentityBuilder<TUser, TRole> AddInstance<TService>(TService instance)
where TService : class
{
Services.AddInstance(instance);
return this;
}
public IdentityBuilder<TUser, TRole> AddUserStore(IUserStore<TUser> store)
{
return AddInstance(store);
}
public IdentityBuilder<TUser, TRole> AddRoleStore(IRoleStore<TRole> store)
{
return AddInstance(store);
}
public IdentityBuilder<TUser, TRole> AddPasswordValidator(IPasswordValidator<TUser> validator)
{
return AddInstance(validator);
}
public IdentityBuilder<TUser, TRole> AddUserValidator(IUserValidator<TUser> validator)
{
return AddInstance(validator);
}
public IdentityBuilder<TUser, TRole> AddTokenProvider<TTokenProvider>() where TTokenProvider : IUserTokenProvider<TUser>
{
Services.AddScoped<IUserTokenProvider<TUser>, TTokenProvider>();
return this;
}
public IdentityBuilder<TUser, TRole> ConfigureIdentity(Action<IdentityOptions> action, int order = 0)
{
Services.Configure(action, order);
return this;
}
public IdentityBuilder<TUser, TRole> AddUserManager<TManager>() where TManager : UserManager<TUser>
{
Services.AddScoped<TManager>();
return this;
}
public IdentityBuilder<TUser, TRole> AddRoleManager<TManager>() where TManager : RoleManager<TRole>
{
Services.AddScoped<TManager>();
return this;
}
}
}

View File

@ -9,7 +9,7 @@ namespace Microsoft.AspNet.Identity
public class IdentityMessage
{
/// <summary>
/// Destination, i.e. To email, or SMS phone number
/// Target for the message, i.e. email or phone number
/// </summary>
public virtual string Destination { get; set; }

View File

@ -17,12 +17,12 @@ namespace Microsoft.Framework.DependencyInjection
return services.Configure(configure);
}
public static IdentityBuilder<IdentityUser, IdentityRole> AddIdentity(this IServiceCollection services)
public static IdentityBuilder AddIdentity(this IServiceCollection services)
{
return services.AddIdentity<IdentityUser, IdentityRole>();
}
public static IdentityBuilder<IdentityUser, IdentityRole> AddIdentity(
public static IdentityBuilder AddIdentity(
this IServiceCollection services,
IConfiguration identityConfig = null,
Action<IdentityOptions> configureOptions = null,
@ -31,7 +31,7 @@ namespace Microsoft.Framework.DependencyInjection
return services.AddIdentity<IdentityUser, IdentityRole>(identityConfig, configureOptions, useDefaultSubKey);
}
public static IdentityBuilder<TUser, TRole> AddIdentity<TUser, TRole>(
public static IdentityBuilder AddIdentity<TUser, TRole>(
this IServiceCollection services,
IConfiguration identityConfig = null,
Action<IdentityOptions> configureOptions = null,
@ -39,8 +39,6 @@ namespace Microsoft.Framework.DependencyInjection
where TUser : class
where TRole : class
{
services.Add(IdentityServices.GetDefaultServices<TUser, TRole>());
if (identityConfig != null)
{
if (useDefaultSubKey)
@ -49,16 +47,34 @@ namespace Microsoft.Framework.DependencyInjection
}
services.Configure<IdentityOptions>(identityConfig);
}
var describe = new ServiceDescriber(identityConfig);
// Services used by identity
services.AddOptions(identityConfig);
services.AddDataProtection(identityConfig);
// Identity services
services.TryAdd(describe.Transient<IUserValidator<TUser>, UserValidator<TUser>>());
services.TryAdd(describe.Transient<IPasswordValidator<TUser>, PasswordValidator<TUser>>());
services.TryAdd(describe.Transient<IPasswordHasher<TUser>, PasswordHasher<TUser>>());
services.TryAdd(describe.Transient<IUserNameNormalizer, UpperInvariantUserNameNormalizer>());
services.TryAdd(describe.Transient<IRoleValidator<TRole>, RoleValidator<TRole>>());
services.TryAdd(describe.Scoped<ISecurityStampValidator, SecurityStampValidator<TUser>>());
services.TryAdd(describe.Scoped<IClaimsIdentityFactory<TUser>, ClaimsIdentityFactory<TUser, TRole>>());
services.TryAdd(describe.Scoped<UserManager<TUser>, UserManager<TUser>>());
services.TryAdd(describe.Scoped<SignInManager<TUser>, SignInManager<TUser>>());
services.TryAdd(describe.Scoped<RoleManager<TRole>, RoleManager<TRole>>());
if (configureOptions != null)
{
services.ConfigureIdentity(configureOptions);
}
services.Configure<ExternalAuthenticationOptions>(options =>
{
options.SignInAsAuthenticationType = IdentityOptions.ExternalCookieAuthenticationType;
});
// Configure all of the cookie middlewares
services.Configure<CookieAuthenticationOptions>(options =>
{
options.AuthenticationType = IdentityOptions.ApplicationCookieAuthenticationType;
@ -68,7 +84,6 @@ namespace Microsoft.Framework.DependencyInjection
OnValidateIdentity = SecurityStampValidator.ValidateIdentityAsync
};
}, IdentityOptions.ApplicationCookieAuthenticationType);
services.Configure<CookieAuthenticationOptions>(options =>
{
options.AuthenticationType = IdentityOptions.ExternalCookieAuthenticationType;
@ -76,14 +91,12 @@ namespace Microsoft.Framework.DependencyInjection
options.CookieName = IdentityOptions.ExternalCookieAuthenticationType;
options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
}, IdentityOptions.ExternalCookieAuthenticationType);
services.Configure<CookieAuthenticationOptions>(options =>
{
options.AuthenticationType = IdentityOptions.TwoFactorRememberMeCookieAuthenticationType;
options.AuthenticationMode = AuthenticationMode.Passive;
options.CookieName = IdentityOptions.TwoFactorRememberMeCookieAuthenticationType;
}, IdentityOptions.TwoFactorRememberMeCookieAuthenticationType);
services.Configure<CookieAuthenticationOptions>(options =>
{
options.AuthenticationType = IdentityOptions.TwoFactorUserIdCookieAuthenticationType;
@ -92,7 +105,7 @@ namespace Microsoft.Framework.DependencyInjection
options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
}, IdentityOptions.TwoFactorUserIdCookieAuthenticationType);
return new IdentityBuilder<TUser, TRole>(services);
return new IdentityBuilder(typeof(TUser), typeof(TRole), services);
}
}
}

View File

@ -1,39 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.Framework.ConfigurationModel;
using Microsoft.Framework.DependencyInjection;
namespace Microsoft.AspNet.Identity
{
/// <summary>
/// Default services used by UserManager and RoleManager
/// </summary>
public class IdentityServices
{
public static IEnumerable<IServiceDescriptor> GetDefaultServices<TUser, TRole>(IConfiguration config = null)
where TUser : class where TRole : class
{
ServiceDescriber describe;
if (config == null)
{
describe = new ServiceDescriber();
}
else
{
describe = new ServiceDescriber(config);
}
yield return describe.Transient<IUserValidator<TUser>, UserValidator<TUser>>();
yield return describe.Transient<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
yield return describe.Transient<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
yield return describe.Transient<IUserNameNormalizer, UpperInvariantUserNameNormalizer>();
yield return describe.Transient<IRoleValidator<TRole>, RoleValidator<TRole>>();
yield return describe.Scoped<ISecurityStampValidator, SecurityStampValidator<TUser>>();
yield return describe.Scoped<IClaimsIdentityFactory<TUser>, ClaimsIdentityFactory<TUser, TRole>>();
yield return describe.Scoped<UserManager<TUser>, UserManager<TUser>>();
yield return describe.Scoped<SignInManager<TUser>, SignInManager<TUser>>();
yield return describe.Scoped<RoleManager<TRole>, RoleManager<TRole>>();
}
}
}

View File

@ -10,6 +10,8 @@ namespace Microsoft.AspNet.Identity
{
public string Name { get; set; } = Resources.DefaultPhoneNumberTokenProviderName;
public string MessageProvider { get; set; } = "SMS";
/// <summary>
/// Message contents which should contain a format string which the token will be the only argument
/// </summary>
@ -50,7 +52,7 @@ namespace Microsoft.AspNet.Identity
throw new ArgumentNullException("manager");
}
var phoneNumber = await manager.GetPhoneNumberAsync(user, cancellationToken);
return !String.IsNullOrWhiteSpace(phoneNumber) && await manager.IsPhoneNumberConfirmedAsync(user, cancellationToken);
return !string.IsNullOrWhiteSpace(phoneNumber) && await manager.IsPhoneNumberConfirmedAsync(user, cancellationToken);
}
/// <summary>
@ -78,14 +80,19 @@ namespace Microsoft.AspNet.Identity
/// <param name="manager"></param>
/// <param name="user"></param>
/// <returns></returns>
public override Task NotifyAsync(string token, UserManager<TUser> manager, TUser user,
public override async Task NotifyAsync(string token, UserManager<TUser> manager, TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
if (manager == null)
{
throw new ArgumentNullException("manager");
}
return manager.SendSmsAsync(user, String.Format(CultureInfo.CurrentCulture, Options.MessageFormat, token), cancellationToken);
var msg = new IdentityMessage
{
Destination = await manager.GetPhoneNumberAsync(user, cancellationToken),
Body = string.Format(CultureInfo.CurrentCulture, Options.MessageFormat, token)
};
await manager.SendMessageAsync(Options.MessageProvider, msg, cancellationToken);
}
}
}

View File

@ -266,6 +266,22 @@ namespace Microsoft.AspNet.Identity
return GetString("LockoutNotEnabled");
}
/// <summary>
/// No IUserMessageProvider named '{0}' is registered.
/// </summary>
internal static string NoMessageProvider
{
get { return GetString("NoMessageProvider"); }
}
/// <summary>
/// No IUserMessageProvider named '{0}' is registered.
/// </summary>
internal static string FormatNoMessageProvider(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("NoMessageProvider"), p0);
}
/// <summary>
/// No IUserTokenProvider named '{0}' is registered.
/// </summary>

View File

@ -181,6 +181,10 @@
<value>Lockout is not enabled for this user.</value>
<comment>error when lockout is not enabled</comment>
</data>
<data name="NoMessageProvider" xml:space="preserve">
<value>No IUserMessageProvider named '{0}' is registered.</value>
<comment>Error when there is no IUserMessageProvider</comment>
</data>
<data name="NoTokenProvider" xml:space="preserve">
<value>No IUserTokenProvider named '{0}' is registered.</value>
<comment>Error when there is no IUserTokenProvider</comment>

View File

@ -21,6 +21,8 @@ namespace Microsoft.AspNet.Identity
{
private readonly Dictionary<string, IUserTokenProvider<TUser>> _tokenProviders =
new Dictionary<string, IUserTokenProvider<TUser>>();
private readonly Dictionary<string, IIdentityMessageProvider> _msgProviders =
new Dictionary<string, IIdentityMessageProvider>();
private TimeSpan _defaultLockout = TimeSpan.Zero;
private bool _disposed;
@ -36,10 +38,14 @@ namespace Microsoft.AspNet.Identity
/// <param name="userValidator"></param>
/// <param name="passwordValidator"></param>
/// <param name="claimsIdentityFactory"></param>
public UserManager(IUserStore<TUser> store, IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<TUser> passwordHasher, IUserValidator<TUser> userValidator,
IPasswordValidator<TUser> passwordValidator, IUserNameNormalizer userNameNormalizer,
IEnumerable<IUserTokenProvider<TUser>> tokenProviders)
public UserManager(IUserStore<TUser> store,
IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<TUser> passwordHasher,
IUserValidator<TUser> userValidator,
IPasswordValidator<TUser> passwordValidator,
IUserNameNormalizer userNameNormalizer,
IEnumerable<IUserTokenProvider<TUser>> tokenProviders,
IEnumerable<IIdentityMessageProvider> msgProviders)
{
if (store == null)
{
@ -68,6 +74,15 @@ namespace Microsoft.AspNet.Identity
RegisterTokenProvider(tokenProvider);
}
}
if (msgProviders != null)
{
foreach (var msgProvider in msgProviders)
{
RegisterMessageProvider(msgProvider);
}
}
}
/// <summary>
@ -111,16 +126,6 @@ namespace Microsoft.AspNet.Identity
/// </summary>
public IUserNameNormalizer UserNameNormalizer { get; set; }
/// <summary>
/// Used to send email
/// </summary>
public IIdentityMessageService EmailService { get; set; }
/// <summary>
/// Used to send a sms message
/// </summary>
public IIdentityMessageService SmsService { get; set; }
public IdentityOptions Options
{
get
@ -1500,7 +1505,6 @@ namespace Microsoft.AspNet.Identity
/// <summary>
/// Register a user token provider
/// </summary>
/// <param name="twoFactorProvider"></param>
/// <param name="provider"></param>
public virtual void RegisterTokenProvider(IUserTokenProvider<TUser> provider)
{
@ -1512,6 +1516,20 @@ namespace Microsoft.AspNet.Identity
_tokenProviders[provider.Name] = provider;
}
/// <summary>
/// Register a user message provider
/// </summary>
/// <param name="provider"></param>
public virtual void RegisterMessageProvider(IIdentityMessageProvider provider)
{
ThrowIfDisposed();
if (provider == null)
{
throw new ArgumentNullException("provider");
}
_msgProviders[provider.Name] = provider;
}
/// <summary>
/// Returns a list of valid two factor providers for a user
/// </summary>
@ -1664,60 +1682,30 @@ namespace Microsoft.AspNet.Identity
return await UpdateAsync(user, cancellationToken);
}
// SMS/Email methods
// Messaging methods
/// <summary>
/// Send an email to the user
/// Send a message to the user using the specified provider
/// </summary>
/// <param name="user"></param>
/// <param name="subject"></param>
/// <param name="body"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task SendEmailAsync(TUser user, string subject, string body,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (EmailService != null)
{
var msg = new IdentityMessage
{
Destination = await GetEmailAsync(user, cancellationToken),
Subject = subject,
Body = body,
};
await EmailService.SendAsync(msg, cancellationToken);
}
}
/// <summary>
/// Send a user a sms message
/// </summary>
/// <param name="user"></param>
/// <param name="messageProvider"></param>
/// <param name="message"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task SendSmsAsync(TUser user, string message,
public virtual async Task<IdentityResult> SendMessageAsync(string messageProvider, IdentityMessage message,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
if (message == null)
{
throw new ArgumentNullException("user");
throw new ArgumentNullException(nameof(message));
}
if (SmsService != null)
if (!_msgProviders.ContainsKey(messageProvider))
{
var msg = new IdentityMessage
{
Destination = await GetPhoneNumberAsync(user, cancellationToken),
Body = message
};
await SmsService.SendAsync(msg, cancellationToken);
throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture,
Resources.NoMessageProvider, messageProvider));
}
await _msgProviders[messageProvider].SendAsync(message, cancellationToken);
return IdentityResult.Success;
}
// IUserLockoutStore methods

View File

@ -10,12 +10,12 @@ namespace Microsoft.AspNet.Identity
{
public static class EntityInMemoryTestServiceCollectionExtensions
{
public static IdentityBuilder<IdentityUser, IdentityRole> AddIdentityInMemory(this ServiceCollection services, InMemoryContext context)
public static IdentityBuilder AddIdentityInMemory(this ServiceCollection services, InMemoryContext context)
{
return services.AddIdentityInMemory<IdentityUser, IdentityRole, InMemoryContext>(context);
}
public static IdentityBuilder<TUser, TRole> AddIdentityInMemory<TUser, TRole, TDbContext>(this ServiceCollection services, TDbContext context)
public static IdentityBuilder AddIdentityInMemory<TUser, TRole, TDbContext>(this ServiceCollection services, TDbContext context)
where TUser : IdentityUser
where TRole : IdentityRole
where TDbContext : DbContext

View File

@ -19,7 +19,8 @@ namespace Microsoft.AspNet.Identity.EntityFramework.InMemory.Test
var services = new ServiceCollection();
services.AddEntityFramework().AddInMemoryStore();
var store = new RoleStore<IdentityRole>(new InMemoryContext());
services.AddIdentity().AddRoleStore(store);
services.AddIdentity();
services.AddInstance<IRoleStore<IdentityRole>>(store);
var provider = services.BuildServiceProvider();
var manager = provider.GetRequiredService<RoleManager<IdentityRole>>();
Assert.NotNull(manager);

View File

@ -1,10 +1,8 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Identity.Test;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
using Microsoft.Framework.Logging;
namespace Microsoft.AspNet.Identity.EntityFramework.InMemory.Test
{
@ -25,7 +23,8 @@ namespace Microsoft.AspNet.Identity.EntityFramework.InMemory.Test
public static RoleManager<IdentityRole> CreateRoleManager(InMemoryContext context)
{
var services = new ServiceCollection();
services.AddIdentity<IdentityUser, IdentityRole>().AddRoleStore(new RoleStore<IdentityRole>(context));
services.AddIdentity<IdentityUser, IdentityRole>();
services.AddInstance<IRoleStore<IdentityRole>>(new RoleStore<IdentityRole>(context));
return services.BuildServiceProvider().GetRequiredService<RoleManager<IdentityRole>>();
}

View File

@ -4,34 +4,62 @@
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.DependencyInjection.Fallback;
using Microsoft.Framework.OptionsModel;
using System.Collections.Generic;
using Xunit;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using Microsoft.AspNet.Security.DataProtection;
namespace Microsoft.AspNet.Identity.Test
{
public class IdentityBuilderTest
{
[Fact]
public void CanSpecifyUserValidatorInstance()
public void CanOverrideUserStore()
{
var services = new ServiceCollection();
var validator = new UserValidator<IdentityUser>();
services.AddIdentity().AddUserValidator(validator);
Assert.Equal(validator, services.BuildServiceProvider().GetRequiredService<IUserValidator<IdentityUser>>());
services.AddIdentity().AddUserStore<MyUberThingy>();
var thingy = services.BuildServiceProvider().GetRequiredService<IUserStore<IdentityUser>>() as MyUberThingy;
Assert.NotNull(thingy);
}
[Fact]
public void CanSpecifyPasswordValidatorInstance()
public void CanOverrideRoleStore()
{
var services = new ServiceCollection();
var validator = new PasswordValidator<IdentityUser>();
services.AddIdentity().AddPasswordValidator(validator);
Assert.Equal(validator, services.BuildServiceProvider().GetRequiredService<IPasswordValidator<IdentityUser>>());
services.AddIdentity().AddRoleStore<MyUberThingy>();
var thingy = services.BuildServiceProvider().GetRequiredService<IRoleStore<IdentityRole>>() as MyUberThingy;
Assert.NotNull(thingy);
}
[Fact]
public void CanSpecifyPasswordHasherInstance()
public void CanOverrideRoleValidator()
{
CanOverride<IPasswordHasher<IdentityUser>>(new PasswordHasher<IdentityUser>(new PasswordHasherOptionsAccessor()));
var services = new ServiceCollection();
services.AddIdentity().AddRoleValidator<MyUberThingy>();
var thingy = services.BuildServiceProvider().GetRequiredService<IRoleValidator<IdentityRole>>() as MyUberThingy;
Assert.NotNull(thingy);
}
[Fact]
public void CanOverrideUserValidator()
{
var services = new ServiceCollection();
services.AddIdentity().AddUserValidator<MyUberThingy>();
var thingy = services.BuildServiceProvider().GetRequiredService<IUserValidator<IdentityUser>>() as MyUberThingy;
Assert.NotNull(thingy);
}
[Fact]
public void CanOverridePasswordValidator()
{
var services = new ServiceCollection();
services.AddIdentity().AddPasswordValidator<MyUberThingy>();
var thingy = services.BuildServiceProvider().GetRequiredService<IPasswordValidator<IdentityUser>>() as MyUberThingy;
Assert.NotNull(thingy);
}
[Fact]
@ -52,12 +80,130 @@ namespace Microsoft.AspNet.Identity.Test
Assert.NotNull(hasher);
}
private static void CanOverride<TService>(TService instance)
where TService : class
[Fact]
public void EnsureDefaultTokenProviders()
{
var services = new ServiceCollection();
services.AddIdentity().AddInstance(instance);
Assert.Equal(instance, services.BuildServiceProvider().GetRequiredService<TService>());
services.AddIdentity().AddDefaultTokenProviders();
services.Add(DataProtectionServices.GetDefaultServices());
services.Add(OptionsServices.GetDefaultServices());
var provider = services.BuildServiceProvider();
var tokenProviders = provider.GetRequiredService<IEnumerable<IUserTokenProvider<IdentityUser>>>();
Assert.Equal(3, tokenProviders.Count());
}
private class MyUberThingy : IUserValidator<IdentityUser>, IPasswordValidator<IdentityUser>, IRoleValidator<IdentityRole>, IUserStore<IdentityUser>, IRoleStore<IdentityRole>
{
public Task CreateAsync(IdentityRole role, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task CreateAsync(IdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task DeleteAsync(IdentityRole role, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task DeleteAsync(IdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public void Dispose()
{
throw new NotImplementedException();
}
public Task<IdentityUser> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task<IdentityUser> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task<string> GetNormalizedUserNameAsync(IdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task<string> GetRoleIdAsync(IdentityRole role, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task<string> GetRoleNameAsync(IdentityRole role, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task<string> GetUserIdAsync(IdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task<string> GetUserNameAsync(IdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task SetNormalizedUserNameAsync(IdentityUser user, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task SetRoleNameAsync(IdentityRole role, string roleName, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task SetUserNameAsync(IdentityUser user, string userName, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task UpdateAsync(IdentityRole role, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task UpdateAsync(IdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task<IdentityResult> ValidateAsync(RoleManager<IdentityRole> manager, IdentityRole role, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task<IdentityResult> ValidateAsync(UserManager<IdentityUser> manager, IdentityUser user, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task<IdentityResult> ValidateAsync(IdentityUser user, string password, UserManager<IdentityUser> manager, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
Task<IdentityRole> IRoleStore<IdentityRole>.FindByIdAsync(string roleId, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
Task<IdentityRole> IRoleStore<IdentityRole>.FindByNameAsync(string roleName, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
}

View File

@ -24,7 +24,7 @@ namespace Microsoft.AspNet.Identity.Test
public TestManager(IUserStore<TestUser> store, IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<TestUser> passwordHasher, IUserValidator<TestUser> userValidator,
IPasswordValidator<TestUser> passwordValidator)
: base(store, optionsAccessor, passwordHasher, userValidator, passwordValidator, null, null) { }
: base(store, optionsAccessor, passwordHasher, userValidator, passwordValidator, null, null, null) { }
}
[Fact]
@ -538,13 +538,13 @@ namespace Microsoft.AspNet.Identity.Test
var passwordValidator = new PasswordValidator<TestUser>();
Assert.Throws<ArgumentNullException>("store",
() => new UserManager<TestUser>(null, null, null, null, null, null, null));
() => new UserManager<TestUser>(null, null, null, null, null, null, null, null));
Assert.Throws<ArgumentNullException>("optionsAccessor",
() => new UserManager<TestUser>(store, null, null, null, null, null, null));
() => 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));
() => new UserManager<TestUser>(store, optionsAccessor, null, null, null, null, null, null));
var manager = new UserManager<TestUser>(store, optionsAccessor, passwordHasher, userValidator, passwordValidator, null, null);
var manager = new UserManager<TestUser>(store, optionsAccessor, passwordHasher, userValidator, passwordValidator, null, null, null);
Assert.Throws<ArgumentNullException>("value", () => manager.PasswordHasher = null);
Assert.Throws<ArgumentNullException>("value", () => manager.Options = null);
@ -671,10 +671,6 @@ namespace Microsoft.AspNet.Identity.Test
async () => await manager.GetLockoutEndDateAsync(null));
await Assert.ThrowsAsync<ArgumentNullException>("user",
async () => await manager.IsLockedOutAsync(null));
await Assert.ThrowsAsync<ArgumentNullException>("user",
async () => await manager.SendEmailAsync(null, null, null));
await Assert.ThrowsAsync<ArgumentNullException>("user",
async () => await manager.SendSmsAsync(null, null));
}
[Fact]
@ -714,6 +710,7 @@ namespace Microsoft.AspNet.Identity.Test
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.GenerateEmailConfirmationTokenAsync(null));
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.IsEmailConfirmedAsync(null));
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.ConfirmEmailAsync(null, null));
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.SendMessageAsync(null, null));
}
private class BadPasswordValidator<TUser> : IPasswordValidator<TUser> where TUser : class

View File

@ -42,7 +42,8 @@ namespace Microsoft.AspNet.Identity.Test
new UserValidator<TUser>(),
new PasswordValidator<TUser>(),
new UpperInvariantUserNameNormalizer(),
new List<IUserTokenProvider<TUser>>());
new List<IUserTokenProvider<TUser>>(),
new List<IIdentityMessageProvider>());
}
public static Mock<RoleManager<TRole>> MockRoleManager<TRole>() where TRole : class
@ -61,7 +62,7 @@ namespace Microsoft.AspNet.Identity.Test
var options = new OptionsManager<IdentityOptions>(null);
var validator = new Mock<UserValidator<TUser>>();
var userManager = new UserManager<TUser>(store, options, new PasswordHasher<TUser>(new PasswordHasherOptionsAccessor()),
validator.Object, new PasswordValidator<TUser>(), new UpperInvariantUserNameNormalizer(), null);
validator.Object, new PasswordValidator<TUser>(), new UpperInvariantUserNameNormalizer(), null, null);
validator.Setup(v => v.ValidateAsync(userManager, It.IsAny<TUser>(), CancellationToken.None))
.Returns(Task.FromResult(IdentityResult.Success)).Verifiable();
return userManager;

View File

@ -1,15 +1,18 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Identity.Test
{
public class TestMessageService : IIdentityMessageService
public class TestMessageService<TUser> : IIdentityMessageProvider
{
public IdentityMessage Message { get; set; }
public string Name { get; set; } = "Test";
public Task SendAsync(IdentityMessage message, CancellationToken cancellationToken = default(CancellationToken))
{
Message = message;

View File

@ -11,8 +11,6 @@ using Microsoft.AspNet.Testing;
using Xunit;
using Microsoft.AspNet.Security.DataProtection;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.OptionsModel;
using Microsoft.AspNet.Hosting;
using Microsoft.Framework.DependencyInjection.Fallback;
namespace Microsoft.AspNet.Identity.Test
@ -30,8 +28,8 @@ namespace Microsoft.AspNet.Identity.Test
{
protected virtual void SetupIdentityServices(IServiceCollection services, object context = null)
{
services.Add(OptionsServices.GetDefaultServices());
services.Add(HostingServices.GetDefaultServices());
services.AddOptions();
services.AddHosting();
services.Add(DataProtectionServices.GetDefaultServices());
services.AddIdentity<TUser, TRole>().AddDefaultTokenProviders();
AddUserStore(services, context);
@ -1206,8 +1204,8 @@ namespace Microsoft.AspNet.Identity.Test
public async Task CanEmailTwoFactorToken()
{
var manager = CreateManager();
var messageService = new TestMessageService();
manager.EmailService = messageService;
var messageService = new TestMessageService<TUser> { Name = "Email" };
manager.RegisterMessageProvider(messageService);
const string factorId = "Email"; // default
var user = new TUser() { UserName = "EmailCodeTest", Email = "foo@foo.com" };
user.EmailConfirmed = true;
@ -1251,8 +1249,8 @@ namespace Microsoft.AspNet.Identity.Test
o.BodyFormat = body;
});
var manager = CreateManager(null, services);
var messageService = new TestMessageService();
manager.EmailService = messageService;
var messageService = new TestMessageService<TUser> { Name = "Email" };
manager.RegisterMessageProvider(messageService);
var user = CreateTestUser();
user.Email = user.UserName + "@foo.com";
const string password = "password";
@ -1300,30 +1298,16 @@ namespace Microsoft.AspNet.Identity.Test
}
[Fact]
public async Task CanSendSms()
public async Task CanSendMessage()
{
var manager = CreateManager();
var messageService = new TestMessageService();
manager.SmsService = messageService;
var messageService = new TestMessageService<TUser>();
manager.RegisterMessageProvider(messageService);
var user = CreateTestUser();
user.PhoneNumber = "4251234567";
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
await manager.SendSmsAsync(user, "Hi");
Assert.NotNull(messageService.Message);
Assert.Equal("Hi", messageService.Message.Body);
}
[Fact]
public async Task CanSendEmail()
{
var manager = CreateManager();
var messageService = new TestMessageService();
manager.EmailService = messageService;
var user = CreateTestUser();
user.Email = user.UserName + "@foo.com";
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
await manager.SendEmailAsync(user, "Hi", "Body");
await manager.SendMessageAsync(messageService.Name, new IdentityMessage { Destination = "foo", Subject = "Hi", Body = "Body" });
Assert.NotNull(messageService.Message);
Assert.Equal("foo", messageService.Message.Destination);
Assert.Equal("Hi", messageService.Message.Subject);
Assert.Equal("Body", messageService.Message.Body);
}
@ -1332,8 +1316,8 @@ namespace Microsoft.AspNet.Identity.Test
public async Task CanSmsTwoFactorToken()
{
var manager = CreateManager();
var messageService = new TestMessageService();
manager.SmsService = messageService;
var messageService = new TestMessageService<TUser> { Name = "SMS" };
manager.RegisterMessageProvider(messageService);
const string factorId = "Phone"; // default
var user = CreateTestUser();
user.PhoneNumber = "4251234567";
@ -1360,8 +1344,8 @@ namespace Microsoft.AspNet.Identity.Test
o.MessageFormat = "Your code is: {0}";
});
var manager = CreateManager(null, services);
var messageService = new TestMessageService();
manager.SmsService = messageService;
var messageService = new TestMessageService<TUser> { Name = "SMS" };
manager.RegisterMessageProvider(messageService);
var user = CreateTestUser();
user.PhoneNumber = "4251234567";
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));