diff --git a/samples/IdentitySample.Mvc/Startup.cs b/samples/IdentitySample.Mvc/Startup.cs index 4d190c54ad..f3dfea2ed4 100644 --- a/samples/IdentitySample.Mvc/Startup.cs +++ b/samples/IdentitySample.Mvc/Startup.cs @@ -6,7 +6,10 @@ using Microsoft.Data.Entity; using Microsoft.Framework.ConfigurationModel; using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.Logging; -using Microsoft.Framework.Logging.Console; +#if DNX451 +using NLog.Config; +using NLog.Targets; +#endif namespace IdentitySamples { @@ -60,8 +63,18 @@ namespace IdentitySamples public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) { - loggerFactory.AddConsole(); +#if DNX451 + var config = new LoggingConfiguration(); + // Step 2. Create targets and add them to the configuration + var consoleTarget = new ColoredConsoleTarget(); + config.AddTarget("console", consoleTarget); + consoleTarget.Layout = @"${date:format=HH\\:MM\\:ss} ${ndc} ${logger} ${message} "; + var rule1 = new LoggingRule("*", NLog.LogLevel.Debug, consoleTarget); + config.LoggingRules.Add(rule1); + + loggerFactory.AddNLog(new global::NLog.LogFactory(config)); +#endif app.UseErrorPage(ErrorPageOptions.ShowAll) .UseStaticFiles() .UseIdentity() diff --git a/samples/IdentitySample.Mvc/Views/Account/Login.cshtml b/samples/IdentitySample.Mvc/Views/Account/Login.cshtml index 7c43c1654f..3e7989f785 100644 --- a/samples/IdentitySample.Mvc/Views/Account/Login.cshtml +++ b/samples/IdentitySample.Mvc/Views/Account/Login.cshtml @@ -1,7 +1,7 @@ @model IdentitySample.Models.LoginViewModel @using System.Collections.Generic @using Microsoft.AspNet.Http -@using Microsoft.AspNet.Http.Security +@using Microsoft.AspNet.Http.Authentication @{ //TODO: Until we have a way to specify the layout page at application level. diff --git a/samples/IdentitySample.Mvc/project.json b/samples/IdentitySample.Mvc/project.json index 2b17e32a3f..55497b4aca 100644 --- a/samples/IdentitySample.Mvc/project.json +++ b/samples/IdentitySample.Mvc/project.json @@ -20,7 +20,8 @@ "EntityFramework.SqlServer": "7.0.0-*", "Microsoft.Framework.ConfigurationModel.Json": "1.0.0-*", "Microsoft.Framework.OptionsModel": "1.0.0-*", - "Microsoft.Framework.Logging.Console": "1.0.0-*" + "Microsoft.Framework.Logging.Console": "1.0.0-*", + "Microsoft.Framework.Logging.NLog": "1.0.0-*" }, "commands": { "web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:41532", diff --git a/src/Microsoft.AspNet.Identity/IdentityLogger.cs b/src/Microsoft.AspNet.Identity/IdentityLogger.cs new file mode 100644 index 0000000000..a895cba90d --- /dev/null +++ b/src/Microsoft.AspNet.Identity/IdentityLogger.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Runtime.CompilerServices; +using Microsoft.Framework.Logging; + +namespace Microsoft.AspNet.Identity.Logging +{ + public static class IdentityLoggerExtensions + { + private static TResult Log(this ILogger logger, TResult result, Func getLevel, + Func messageAccessor) + { + var logLevel = getLevel(result); + + // Check if log level is enabled before creating the message. + if (logger.IsEnabled(logLevel)) + { + logger.Log(logLevel, 0, messageAccessor(), null, (msg, exp) => (string)msg); + } + + return result; + } + + public static SignInResult Log(this ILogger logger, SignInResult result, [CallerMemberName]string methodName = null) + => logger.Log(result, r => r.GetLogLevel(), () => Resources.FormatLoggingResult(methodName, result)); + + public static IdentityResult Log(this ILogger logger, IdentityResult result, [CallerMemberName]string methodName = null) + => logger.Log(result, r => r.GetLogLevel(), () => Resources.FormatLoggingResult(methodName, result)); + + public static bool Log(this ILogger logger, bool result, [CallerMemberName]string methodName = null) + => logger.Log(result, b => b ? LogLevel.Verbose : LogLevel.Warning, + () => Resources.FormatLoggingResult(methodName, result)); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/IdentityResult.cs b/src/Microsoft.AspNet.Identity/IdentityResult.cs index b6b795abba..0cd3269b2b 100644 --- a/src/Microsoft.AspNet.Identity/IdentityResult.cs +++ b/src/Microsoft.AspNet.Identity/IdentityResult.cs @@ -13,9 +13,7 @@ namespace Microsoft.AspNet.Identity public class IdentityResult { private static readonly IdentityResult _success = new IdentityResult { Succeeded = true }; - private List _errors = new List(); - /// /// True if the operation was successful /// @@ -24,16 +22,13 @@ namespace Microsoft.AspNet.Identity /// /// List of errors /// - public IEnumerable Errors { get { return _errors; } } + public IEnumerable Errors => _errors; /// /// Static success result /// /// - public static IdentityResult Success - { - get { return _success; } - } + public static IdentityResult Success => _success; /// /// Failed helper method @@ -51,21 +46,23 @@ namespace Microsoft.AspNet.Identity } /// - /// Log Identity result + /// Return string representation of IdentityResult /// - /// - /// - public virtual void Log(ILogger logger, string message) + /// "Succedded", if result is suceeded else "Failed:error codes" + public override string ToString() { - // TODO: Take logging level as a parameter - if (Succeeded) - { - logger.LogInformation(Resources.FormatLogIdentityResultSuccess(message)); - } - else - { - logger.LogWarning(Resources.FormatLogIdentityResultFailure(message, string.Join(",", Errors.Select(x => x.Code).ToList()))); - } + return Succeeded ? + "Succeeded" : + string.Format("{0} : {1}", "Failed", string.Join(",", Errors.Select(x => x.Code).ToList())); + } + + /// + /// Get the level to log this result + /// + /// LogLevel to log + public virtual LogLevel GetLogLevel() + { + return Succeeded ? LogLevel.Verbose : LogLevel.Warning; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/PasswordHasher.cs b/src/Microsoft.AspNet.Identity/PasswordHasher.cs index 44a658e8fd..93d8cc6ac5 100644 --- a/src/Microsoft.AspNet.Identity/PasswordHasher.cs +++ b/src/Microsoft.AspNet.Identity/PasswordHasher.cs @@ -59,7 +59,7 @@ namespace Microsoft.AspNet.Identity default: throw new InvalidOperationException(Resources.InvalidPasswordHasherCompatibilityMode); } - + _rng = options.Rng; } diff --git a/src/Microsoft.AspNet.Identity/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Identity/Properties/Resources.Designer.cs index 416c98f9e5..f3f767543b 100644 --- a/src/Microsoft.AspNet.Identity/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Identity/Properties/Resources.Designer.cs @@ -651,67 +651,35 @@ namespace Microsoft.AspNet.Identity } /// - /// {0} : Failed : {1} + /// {0} : {1} /// - internal static string LogIdentityResultFailure + internal static string LoggingResult { - get { return GetString("LogIdentityResultFailure"); } + get { return GetString("LoggingResult"); } } /// - /// {0} : Failed : {1} + /// {0} : {1} /// - internal static string FormatLogIdentityResultFailure(object p0, object p1) + internal static string FormatLoggingResult(object p0, object p1) { - return string.Format(CultureInfo.CurrentCulture, GetString("LogIdentityResultFailure"), p0, p1); - } - - /// - /// {0} : Success - /// - internal static string LogIdentityResultSuccess - { - get { return GetString("LogIdentityResultSuccess"); } - } - - /// - /// {0} : Success - /// - internal static string FormatLogIdentityResultSuccess(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("LogIdentityResultSuccess"), p0); - } - - /// - /// {0} : Result : {1} - /// - internal static string LoggingSigninResult - { - get { return GetString("LoggingSigninResult"); } - } - - /// - /// {0} : Result : {1} - /// - internal static string FormatLoggingSigninResult(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("LoggingSigninResult"), p0, p1); + return string.Format(CultureInfo.CurrentCulture, GetString("LoggingResult"), p0, p1); } /// /// {0} for user: {1} /// - internal static string LoggingResultMessage + internal static string LoggingResultMessageForUser { - get { return GetString("LoggingResultMessage"); } + get { return GetString("LoggingResultMessageForUser"); } } /// /// {0} for user: {1} /// - internal static string FormatLoggingResultMessage(object p0, object p1) + internal static string FormatLoggingResultMessageForUser(object p0, object p1) { - return string.Format(CultureInfo.CurrentCulture, GetString("LoggingResultMessage"), p0, p1); + return string.Format(CultureInfo.CurrentCulture, GetString("LoggingResultMessageForUser"), p0, p1); } /// diff --git a/src/Microsoft.AspNet.Identity/Resources.resx b/src/Microsoft.AspNet.Identity/Resources.resx index f3cd081aa8..7497fbcea1 100644 --- a/src/Microsoft.AspNet.Identity/Resources.resx +++ b/src/Microsoft.AspNet.Identity/Resources.resx @@ -277,19 +277,11 @@ User is not in role '{0}'. Error when a user is not in the role - - {0} : Failed : {1} - Logging method execution failure + + {0} : {1} + Logging statement for a result object - - {0} : Success - Logging method execution success - - - {0} : Result : {1} - Logging statement for SignInManager - - + {0} for user: {1} Message prefix for Identity result diff --git a/src/Microsoft.AspNet.Identity/RoleManager.cs b/src/Microsoft.AspNet.Identity/RoleManager.cs index 3c3c14aaae..9fcd1b7f5e 100644 --- a/src/Microsoft.AspNet.Identity/RoleManager.cs +++ b/src/Microsoft.AspNet.Identity/RoleManager.cs @@ -4,11 +4,13 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNet.Hosting; using Microsoft.AspNet.Http; +using Microsoft.AspNet.Identity.Logging; using Microsoft.Framework.Logging; namespace Microsoft.AspNet.Identity @@ -20,13 +22,17 @@ namespace Microsoft.AspNet.Identity public class RoleManager : IDisposable where TRole : class { private bool _disposed; - private HttpContext _context; + private readonly HttpContext _context; /// - /// Constructor + /// Constructor /// /// The IRoleStore commits changes via the UpdateAsync/CreateAsync methods - /// + /// IEnumerable of role validators + /// user property normalizers + /// IdentityErrorDescribers + /// Logger for RoleManager + /// HttpContext accessor object public RoleManager(IRoleStore store, IEnumerable> roleValidators, ILookupNormalizer keyNormalizer, @@ -59,6 +65,11 @@ namespace Microsoft.AspNet.Identity /// protected IRoleStore Store { get; private set; } + /// + /// Used for logging results + /// + protected internal virtual ILogger Logger { get; set; } + /// /// Used to validate roles before persisting changes /// @@ -69,11 +80,6 @@ namespace Microsoft.AspNet.Identity /// internal IdentityErrorDescriber ErrorDescriber { get; set; } - /// - /// Used to log results - /// - internal ILogger Logger { get; set; } - /// /// Used to normalize user names, role names, emails for uniqueness /// @@ -119,13 +125,7 @@ namespace Microsoft.AspNet.Identity } } - private CancellationToken CancellationToken - { - get - { - return _context?.RequestAborted ?? CancellationToken.None; - } - } + private CancellationToken CancellationToken => _context?.RequestAborted ?? CancellationToken.None; /// /// Dispose this object @@ -162,20 +162,23 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("role"); } - var result = await ValidateRoleInternal(role); if (!result.Succeeded) { return result; } await UpdateNormalizedRoleNameAsync(role); - return await LogResultAsync(await Store.CreateAsync(role, CancellationToken), role); + result = await Store.CreateAsync(role, CancellationToken); + using (await BeginLoggingScopeAsync(role)) + { + return Logger.Log(result); + } } /// /// Update the user's normalized user name /// - /// + /// /// public virtual async Task UpdateNormalizedRoleNameAsync(TRole role) { @@ -197,7 +200,10 @@ namespace Microsoft.AspNet.Identity throw new ArgumentNullException("role"); } - return await LogResultAsync(await UpdateRoleAsync(role), role); + using (await BeginLoggingScopeAsync(role)) + { + return Logger.Log(await UpdateRoleAsync(role)); + } } private async Task UpdateRoleAsync(TRole role) @@ -223,7 +229,11 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("role"); } - return await LogResultAsync(await Store.DeleteAsync(role, CancellationToken), role); + + using (await BeginLoggingScopeAsync(role)) + { + return Logger.Log(await Store.DeleteAsync(role, CancellationToken)); + } } /// @@ -284,9 +294,13 @@ namespace Microsoft.AspNet.Identity public virtual async Task SetRoleNameAsync(TRole role, string name) { ThrowIfDisposed(); - await Store.SetRoleNameAsync(role, name, CancellationToken); - await UpdateNormalizedRoleNameAsync(role); - return await LogResultAsync(IdentityResult.Success, role); + + using (await BeginLoggingScopeAsync(role)) + { + await Store.SetRoleNameAsync(role, name, CancellationToken); + await UpdateNormalizedRoleNameAsync(role); + return Logger.Log(IdentityResult.Success); + } } /// @@ -345,8 +359,12 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("role"); } - await claimStore.AddClaimAsync(role, claim, CancellationToken); - return await LogResultAsync(await UpdateRoleAsync(role), role); + + using (await BeginLoggingScopeAsync(role)) + { + await claimStore.AddClaimAsync(role, claim, CancellationToken); + return Logger.Log(await UpdateRoleAsync(role)); + } } /// @@ -363,8 +381,12 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("role"); } - await claimStore.RemoveClaimAsync(role, claim, CancellationToken); - return await LogResultAsync(await UpdateRoleAsync(role), role); + + using (await BeginLoggingScopeAsync(role)) + { + await claimStore.RemoveClaimAsync(role, claim, CancellationToken); + return Logger.Log(await UpdateRoleAsync(role)); + } } /// @@ -383,20 +405,12 @@ namespace Microsoft.AspNet.Identity return await claimStore.GetClaimsAsync(role, CancellationToken); } - /// - /// Logs the current Identity Result and returns result object - /// - /// - /// - /// - /// - protected async Task LogResultAsync(IdentityResult result, - TRole role, [System.Runtime.CompilerServices.CallerMemberName] string methodName = "") + protected virtual async Task BeginLoggingScopeAsync(TRole role, [CallerMemberName] string methodName = null) { - result.Log(Logger, Resources.FormatLoggingResultMessageForRole(methodName, await GetRoleIdAsync(role))); - - return result; + var state = Resources.FormatLoggingResultMessageForRole(methodName, await GetRoleIdAsync(role)); + return Logger.BeginScope(state); } + private void ThrowIfDisposed() { diff --git a/src/Microsoft.AspNet.Identity/SignInManager.cs b/src/Microsoft.AspNet.Identity/SignInManager.cs index 9dca0ee9ac..e085e7fa58 100644 --- a/src/Microsoft.AspNet.Identity/SignInManager.cs +++ b/src/Microsoft.AspNet.Identity/SignInManager.cs @@ -4,12 +4,14 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using System.Security.Claims; using System.Security.Principal; using System.Threading.Tasks; using Microsoft.AspNet.Hosting; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Authentication; +using Microsoft.AspNet.Identity.Logging; using Microsoft.Framework.Logging; using Microsoft.Framework.OptionsModel; @@ -31,6 +33,7 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException(nameof(userManager)); } + if (contextAccessor == null || contextAccessor.HttpContext == null) { throw new ArgumentNullException(nameof(contextAccessor)); @@ -48,29 +51,28 @@ namespace Microsoft.AspNet.Identity Logger = logger?.CreateLogger>() ?? new Logger>(new LoggerFactory()); } + protected internal virtual ILogger Logger { get; set; } internal UserManager UserManager { get; private set; } internal HttpContext Context { get; private set; } internal IUserClaimsPrincipalFactory ClaimsFactory { get; private set; } internal IdentityOptions Options { get; private set; } - internal ILogger Logger { get; set; } + // Should this be a func? - public virtual async Task CreateUserPrincipalAsync(TUser user) - { - return await ClaimsFactory.CreateAsync(user); - } + public virtual async Task CreateUserPrincipalAsync(TUser user) => await ClaimsFactory.CreateAsync(user); public virtual async Task CanSignInAsync(TUser user) { if (Options.SignIn.RequireConfirmedEmail && !(await UserManager.IsEmailConfirmedAsync(user))) { - return await LogResultAsync(false, user); + return Logger.Log(false); } if (Options.SignIn.RequireConfirmedPhoneNumber && !(await UserManager.IsPhoneNumberConfirmedAsync(user))) { - return await LogResultAsync(false, user); + return Logger.Log(false); } - return await LogResultAsync(true, user); + + return Logger.Log(true); } public virtual async Task SignInAsync(TUser user, bool isPersistent, string authenticationMethod = null) @@ -149,31 +151,35 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException(nameof(user)); } - var error = await PreSignInCheck(user); - if (error != null) - { - return await LogResultAsync(error, user); - } - if (await IsLockedOut(user)) - { - return await LogResultAsync(SignInResult.LockedOut, user); - } - if (await UserManager.CheckPasswordAsync(user, password)) - { - await ResetLockout(user); - return await LogResultAsync(await SignInOrTwoFactorAsync(user, isPersistent), user); - } - if (UserManager.SupportsUserLockout && shouldLockout) - { - // If lockout is requested, increment access failed count which might lock out the user - await UserManager.AccessFailedAsync(user); - if (await UserManager.IsLockedOutAsync(user)) - { - return await LogResultAsync(SignInResult.LockedOut, user); + using (await BeginLoggingScopeAsync(user)) + { + var error = await PreSignInCheck(user); + if (error != null) + { + return Logger.Log(error); } + if (await IsLockedOut(user)) + { + return Logger.Log(SignInResult.LockedOut); + } + if (await UserManager.CheckPasswordAsync(user, password)) + { + await ResetLockout(user); + return Logger.Log(await SignInOrTwoFactorAsync(user, isPersistent)); + } + if (UserManager.SupportsUserLockout && shouldLockout) + { + // If lockout is requested, increment access failed count which might lock out the user + await UserManager.AccessFailedAsync(user); + if (await UserManager.IsLockedOutAsync(user)) + { + + return Logger.Log(SignInResult.LockedOut); + } + } + return Logger.Log(SignInResult.Failed); } - return await LogResultAsync(SignInResult.Failed, user); } public virtual async Task PasswordSignInAsync(string userName, string password, @@ -184,6 +190,7 @@ namespace Microsoft.AspNet.Identity { return SignInResult.Failed; } + return await PasswordSignInAsync(user, password, isPersistent, shouldLockout); } @@ -205,8 +212,7 @@ namespace Microsoft.AspNet.Identity public virtual async Task IsTwoFactorClientRememberedAsync(TUser user) { var userId = await UserManager.GetUserIdAsync(user); - var result = - await Context.AuthenticateAsync(IdentityOptions.TwoFactorRememberMeCookieAuthenticationScheme); + var result = await Context.AuthenticateAsync(IdentityOptions.TwoFactorRememberMeCookieAuthenticationScheme); return (result?.Principal != null && result.Principal.FindFirstValue(ClaimTypes.Name) == userId); } @@ -239,38 +245,41 @@ namespace Microsoft.AspNet.Identity { return SignInResult.Failed; } - var error = await PreSignInCheck(user); - if (error != null) + + using (await BeginLoggingScopeAsync(user)) { - return await LogResultAsync(error, user); - } - if (await UserManager.VerifyTwoFactorTokenAsync(user, provider, code)) - { - // When token is verified correctly, clear the access failed count used for lockout - await ResetLockout(user); - // Cleanup external cookie - if (twoFactorInfo.LoginProvider != null) + var error = await PreSignInCheck(user); + if (error != null) { - Context.Response.SignOut(IdentityOptions.ExternalCookieAuthenticationScheme); + return Logger.Log(error); } - await SignInAsync(user, isPersistent, twoFactorInfo.LoginProvider); - if (rememberClient) + if (await UserManager.VerifyTwoFactorTokenAsync(user, provider, code)) { - await RememberTwoFactorClientAsync(user); + // When token is verified correctly, clear the access failed count used for lockout + await ResetLockout(user); + // Cleanup external cookie + if (twoFactorInfo.LoginProvider != null) + { + Context.Response.SignOut(IdentityOptions.ExternalCookieAuthenticationScheme); + } + await SignInAsync(user, isPersistent, twoFactorInfo.LoginProvider); + if (rememberClient) + { + await RememberTwoFactorClientAsync(user); + } + await UserManager.ResetAccessFailedCountAsync(user); + await SignInAsync(user, isPersistent); + return Logger.Log(SignInResult.Success); } - await UserManager.ResetAccessFailedCountAsync(user); - await SignInAsync(user, isPersistent); - return await LogResultAsync(SignInResult.Success, user); + // If the token is incorrect, record the failure which also may cause the user to be locked out + await UserManager.AccessFailedAsync(user); + return Logger.Log(SignInResult.Failed); } - // If the token is incorrect, record the failure which also may cause the user to be locked out - await UserManager.AccessFailedAsync(user); - return await LogResultAsync(SignInResult.Failed, user); } /// /// Returns the user who has started the two factor authentication process /// - /// /// public virtual async Task GetTwoFactorAuthenticationUserAsync() { @@ -290,12 +299,16 @@ namespace Microsoft.AspNet.Identity { return SignInResult.Failed; } - var error = await PreSignInCheck(user); - if (error != null) + + using (await BeginLoggingScopeAsync(user)) { - return await LogResultAsync(error, user); + var error = await PreSignInCheck(user); + if (error != null) + { + return Logger.Log(error); + } + return Logger.Log(await SignInOrTwoFactorAsync(user, isPersistent, loginProvider)); } - return await LogResultAsync(await SignInOrTwoFactorAsync(user, isPersistent, loginProvider), user); } private const string LoginProviderKey = "LoginProvider"; @@ -384,34 +397,12 @@ namespace Microsoft.AspNet.Identity return null; } - /// - /// Log boolean result for user and return result - /// - /// - /// - /// - /// - protected async virtual Task LogResultAsync(bool result, TUser user, [System.Runtime.CompilerServices.CallerMemberName] string methodName = "") + protected virtual async Task BeginLoggingScopeAsync(TUser user, [CallerMemberName] string methodName = null) { - Logger.LogInformation(Resources.FormatLoggingSigninResult(Resources.FormatLoggingResultMessage(methodName, - await UserManager.GetUserIdAsync(user)), result)); - - return result; - } - - /// - /// Log SignInStatus for user and return SignInStatus - /// - /// - /// - /// - /// - protected async virtual Task LogResultAsync(SignInResult status, TUser user, [System.Runtime.CompilerServices.CallerMemberName] string methodName = "") - { - status.Log(Logger, Resources.FormatLoggingResultMessage(methodName, await UserManager.GetUserIdAsync(user))); - - return status; + var state = Resources.FormatLoggingResultMessageForUser(methodName, await UserManager.GetUserIdAsync(user)); + return Logger.BeginScope(state); } + internal static ClaimsPrincipal StoreTwoFactorInfo(string userId, string loginProvider) { @@ -423,7 +414,6 @@ namespace Microsoft.AspNet.Identity } return new ClaimsPrincipal(identity); } - internal class TwoFactorAuthenticationInfo { public string UserId { get; set; } diff --git a/src/Microsoft.AspNet.Identity/SignInResult.cs b/src/Microsoft.AspNet.Identity/SignInResult.cs index 3e8cf03537..94342ffea8 100644 --- a/src/Microsoft.AspNet.Identity/SignInResult.cs +++ b/src/Microsoft.AspNet.Identity/SignInResult.cs @@ -40,74 +40,51 @@ namespace Microsoft.AspNet.Identity /// Static success result /// /// - public static SignInResult Success - { - get { return _success; } - } + public static SignInResult Success => _success; /// /// Static failure result /// /// - public static SignInResult Failed - { - get { return _failed; } - } + public static SignInResult Failed => _failed; /// /// Static locked out result /// /// - public static SignInResult LockedOut - { - get { return _lockedOut; } - } + public static SignInResult LockedOut => _lockedOut; /// /// Static not allowed result /// /// - public static SignInResult NotAllowed - { - get { return _notAllowed; } - } + public static SignInResult NotAllowed => _notAllowed; /// /// Static two factor required result /// /// - public static SignInResult TwoFactorRequired + public static SignInResult TwoFactorRequired => _twoFactorRequired; + + /// + /// Returns string representation of the result. + /// + /// + public override string ToString() { - get { return _twoFactorRequired; } + return IsLockedOut ? "Lockedout" : + IsNotAllowed ? "NotAllowed" : + RequiresTwoFactor ? "RequiresTwoFactor" : + Succeeded ? "Succeeded" : "Failed"; } /// - /// Log result based on properties + /// Returns the level at which this result should be logged /// - /// - /// - public virtual void Log(ILogger logger, string message) + /// + public virtual LogLevel GetLogLevel() { - if (IsLockedOut) - { - logger.LogInformation(Resources.FormatLoggingSigninResult(message, "Lockedout")); - } - else if (IsNotAllowed) - { - logger.LogInformation(Resources.FormatLoggingSigninResult(message, "NotAllowed")); - } - else if (RequiresTwoFactor) - { - logger.LogInformation(Resources.FormatLoggingSigninResult(message, "RequiresTwoFactor")); - } - else if (Succeeded) - { - logger.LogInformation(Resources.FormatLoggingSigninResult(message, "Succeeded")); - } - else - { - logger.LogInformation(Resources.FormatLoggingSigninResult(message, "Failed")); - } + return Succeeded || RequiresTwoFactor ? LogLevel.Verbose : LogLevel.Warning; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Identity/UserManager.cs b/src/Microsoft.AspNet.Identity/UserManager.cs index 9a5c3c1a32..9b4a56e44f 100644 --- a/src/Microsoft.AspNet.Identity/UserManager.cs +++ b/src/Microsoft.AspNet.Identity/UserManager.cs @@ -5,12 +5,14 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Runtime.CompilerServices; using System.Security.Claims; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNet.Hosting; using Microsoft.AspNet.Http; +using Microsoft.AspNet.Identity.Logging; using Microsoft.Framework.Logging; using Microsoft.Framework.OptionsModel; @@ -27,7 +29,7 @@ namespace Microsoft.AspNet.Identity private TimeSpan _defaultLockout = TimeSpan.Zero; private bool _disposed; - private HttpContext _context; + private readonly HttpContext _context; /// /// Constructor @@ -40,7 +42,8 @@ namespace Microsoft.AspNet.Identity /// /// /// - /// + /// + /// public UserManager(IUserStore store, IOptions optionsAccessor, IPasswordHasher passwordHasher, @@ -94,6 +97,11 @@ namespace Microsoft.AspNet.Identity /// protected internal IUserStore Store { get; set; } + /// + /// Used to log messages + /// + protected internal virtual ILogger Logger { get; set; } + internal IPasswordHasher PasswordHasher { get; set; } internal IList> UserValidators { get; } = new List>(); @@ -104,8 +112,6 @@ namespace Microsoft.AspNet.Identity internal IdentityErrorDescriber ErrorDescriber { get; set; } - internal ILogger Logger { get; set; } - internal IdentityOptions Options { get; set; } /// @@ -244,13 +250,7 @@ namespace Microsoft.AspNet.Identity } } - private CancellationToken CancellationToken - { - get - { - return _context?.RequestAborted ?? CancellationToken.None; - } - } + private CancellationToken CancellationToken => _context?.RequestAborted ?? CancellationToken.None; /// /// Dispose the store context @@ -331,7 +331,12 @@ namespace Microsoft.AspNet.Identity } await UpdateNormalizedUserNameAsync(user); await UpdateNormalizedEmailAsync(user); - return await LogResultAsync(await Store.CreateAsync(user, CancellationToken), user); + + result = await Store.CreateAsync(user, CancellationToken); + using (await BeginLoggingScopeAsync(user)) + { + return Logger.Log(result); + } } /// @@ -346,7 +351,11 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - return await LogResultAsync(await UpdateUserAsync(user), user); + + using (await BeginLoggingScopeAsync(user)) + { + return Logger.Log(await UpdateUserAsync(user)); + } } /// @@ -361,7 +370,11 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - return await LogResultAsync(await Store.DeleteAsync(user, CancellationToken), user); + + using (await BeginLoggingScopeAsync(user)) + { + return Logger.Log(await Store.DeleteAsync(user, CancellationToken)); + } } /// @@ -432,7 +445,7 @@ namespace Microsoft.AspNet.Identity /// /// Normalize a key (user name, email) for uniqueness comparisons /// - /// + /// /// public virtual string NormalizeKey(string key) { @@ -478,8 +491,12 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - await UpdateUserName(user, userName); - return await LogResultAsync(await UpdateUserAsync(user), user); + + using (await BeginLoggingScopeAsync(user)) + { + await UpdateUserName(user, userName); + return Logger.Log(await UpdateUserAsync(user)); + } } private async Task UpdateUserName(TUser user, string userName) @@ -513,14 +530,18 @@ namespace Microsoft.AspNet.Identity { return false; } - var result = await VerifyPasswordAsync(passwordStore, user, password); - if (result == PasswordVerificationResult.SuccessRehashNeeded) - { - await UpdatePasswordHash(passwordStore, user, password, validatePassword: false); - await UpdateUserAsync(user); - } - return await LogResultAsync(result != PasswordVerificationResult.Failed, user); + using (await BeginLoggingScopeAsync(user)) + { + var result = await VerifyPasswordAsync(passwordStore, user, password); + if (result == PasswordVerificationResult.SuccessRehashNeeded) + { + Logger.Log(await UpdatePasswordHash(passwordStore, user, password, validatePassword: false)); + Logger.Log(await UpdateUserAsync(user)); + } + + return Logger.Log(result != PasswordVerificationResult.Failed); + } } /// @@ -536,7 +557,11 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - return await LogResultAsync(await passwordStore.HasPasswordAsync(user, CancellationToken), user); + + using (await BeginLoggingScopeAsync(user)) + { + return Logger.Log(await passwordStore.HasPasswordAsync(user, CancellationToken)); + } } /// @@ -553,17 +578,21 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - var hash = await passwordStore.GetPasswordHashAsync(user, CancellationToken); - if (hash != null) + + using (await BeginLoggingScopeAsync(user)) { - return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.UserAlreadyHasPassword()), user); + var hash = await passwordStore.GetPasswordHashAsync(user, CancellationToken); + if (hash != null) + { + return Logger.Log(IdentityResult.Failed(ErrorDescriber.UserAlreadyHasPassword())); + } + var result = await UpdatePasswordHash(passwordStore, user, password); + if (!result.Succeeded) + { + return Logger.Log(result); + } + return Logger.Log(await UpdateUserAsync(user)); } - var result = await UpdatePasswordHash(passwordStore, user, password); - if (!result.Succeeded) - { - return await LogResultAsync(result, user); - } - return await LogResultAsync(await UpdateUserAsync(user), user); } /// @@ -581,23 +610,26 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - if (await VerifyPasswordAsync(passwordStore, user, currentPassword) != PasswordVerificationResult.Failed) + + using (await BeginLoggingScopeAsync(user)) { - var result = await UpdatePasswordHash(passwordStore, user, newPassword); - if (!result.Succeeded) + if (await VerifyPasswordAsync(passwordStore, user, currentPassword) != PasswordVerificationResult.Failed) { - return await LogResultAsync(result, user); + var result = await UpdatePasswordHash(passwordStore, user, newPassword); + if (!result.Succeeded) + { + return Logger.Log(result); + } + return Logger.Log(await UpdateUserAsync(user)); } - return await LogResultAsync(await UpdateUserAsync(user), user); + return Logger.Log(IdentityResult.Failed(ErrorDescriber.PasswordMismatch())); } - return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.PasswordMismatch()), user); } /// /// Remove a user's password /// /// - /// public virtual async Task RemovePasswordAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken)) @@ -608,8 +640,12 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - await UpdatePasswordHash(passwordStore, user, null, validatePassword: false); - return await LogResultAsync(await UpdateUserAsync(user), user); + + using (await BeginLoggingScopeAsync(user)) + { + await UpdatePasswordHash(passwordStore, user, null, validatePassword: false); + return Logger.Log(await UpdateUserAsync(user)); + } } internal async Task UpdatePasswordHash(IUserPasswordStore passwordStore, @@ -682,8 +718,12 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - await UpdateSecurityStampInternal(user); - return await LogResultAsync(await UpdateUserAsync(user), user); + + using (await BeginLoggingScopeAsync(user)) + { + await UpdateSecurityStampInternal(user); + return Logger.Log(await UpdateUserAsync(user)); + } } /// @@ -694,9 +734,13 @@ namespace Microsoft.AspNet.Identity public virtual async Task GeneratePasswordResetTokenAsync(TUser user) { ThrowIfDisposed(); - var token = await GenerateUserTokenAsync(user, Options.PasswordResetTokenProvider, "ResetPassword"); - await LogResultAsync(IdentityResult.Success, user); - return token; + + using (await BeginLoggingScopeAsync(user)) + { + var token = await GenerateUserTokenAsync(user, Options.PasswordResetTokenProvider, "ResetPassword"); + Logger.Log(IdentityResult.Success); + return token; + } } /// @@ -713,18 +757,22 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - // Make sure the token is valid and the stamp matches - if (!await VerifyUserTokenAsync(user, Options.PasswordResetTokenProvider, "ResetPassword", token)) + + using (await BeginLoggingScopeAsync(user)) { - return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.InvalidToken()), user); + // Make sure the token is valid and the stamp matches + if (!await VerifyUserTokenAsync(user, Options.PasswordResetTokenProvider, "ResetPassword", token)) + { + return Logger.Log(IdentityResult.Failed(ErrorDescriber.InvalidToken())); + } + var passwordStore = GetPasswordStore(); + var result = await UpdatePasswordHash(passwordStore, user, newPassword); + if (!result.Succeeded) + { + return Logger.Log(result); + } + return Logger.Log(await UpdateUserAsync(user)); } - var passwordStore = GetPasswordStore(); - var result = await UpdatePasswordHash(passwordStore, user, newPassword); - if (!result.Succeeded) - { - return await LogResultAsync(result, user); - } - return await LogResultAsync(await UpdateUserAsync(user), user); } // Update the security stamp if the store supports it @@ -777,7 +825,8 @@ namespace Microsoft.AspNet.Identity /// Remove a user login /// /// - /// + /// + /// /// public virtual async Task RemoveLoginAsync(TUser user, string loginProvider, string providerKey) { @@ -795,9 +844,13 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - await loginStore.RemoveLoginAsync(user, loginProvider, providerKey, CancellationToken); - await UpdateSecurityStampInternal(user); - return await LogResultAsync(await UpdateUserAsync(user), user); + + using (await BeginLoggingScopeAsync(user)) + { + await loginStore.RemoveLoginAsync(user, loginProvider, providerKey, CancellationToken); + await UpdateSecurityStampInternal(user); + return Logger.Log(await UpdateUserAsync(user)); + } } /// @@ -818,13 +871,17 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - var existingUser = await FindByLoginAsync(login.LoginProvider, login.ProviderKey); - if (existingUser != null) + + using (await BeginLoggingScopeAsync(user)) { - return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.LoginAlreadyAssociated()), user); + var existingUser = await FindByLoginAsync(login.LoginProvider, login.ProviderKey); + if (existingUser != null) + { + return Logger.Log(IdentityResult.Failed(ErrorDescriber.LoginAlreadyAssociated())); + } + await loginStore.AddLoginAsync(user, login, CancellationToken); + return Logger.Log(await UpdateUserAsync(user)); } - await loginStore.AddLoginAsync(user, login, CancellationToken); - return await LogResultAsync(await UpdateUserAsync(user), user); } /// @@ -893,8 +950,12 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - await claimStore.AddClaimsAsync(user, claims, CancellationToken); - return await LogResultAsync(await UpdateUserAsync(user), user); + + using (await BeginLoggingScopeAsync(user)) + { + await claimStore.AddClaimsAsync(user, claims, CancellationToken); + return Logger.Log(await UpdateUserAsync(user)); + } } /// @@ -920,8 +981,12 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - await claimStore.ReplaceClaimAsync(user, claim, newClaim, CancellationToken); - return await LogResultAsync(await UpdateUserAsync(user), user); + + using (await BeginLoggingScopeAsync(user)) + { + await claimStore.ReplaceClaimAsync(user, claim, newClaim, CancellationToken); + return Logger.Log(await UpdateUserAsync(user)); + } } /// @@ -963,8 +1028,12 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("claims"); } - await claimStore.RemoveClaimsAsync(user, claims, CancellationToken); - return await LogResultAsync(await UpdateUserAsync(user), user); + + using (await BeginLoggingScopeAsync(user)) + { + await claimStore.RemoveClaimsAsync(user, claims, CancellationToken); + return Logger.Log(await UpdateUserAsync(user)); + } } /// @@ -1007,13 +1076,17 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - var userRoles = await userRoleStore.GetRolesAsync(user, CancellationToken); - if (userRoles.Contains(role)) + + using (await BeginLoggingScopeAsync(user)) { - return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.UserAlreadyInRole(role)), user); + var userRoles = await userRoleStore.GetRolesAsync(user, CancellationToken); + if (userRoles.Contains(role)) + { + return Logger.Log(IdentityResult.Failed(ErrorDescriber.UserAlreadyInRole(role))); + } + await userRoleStore.AddToRoleAsync(user, role, CancellationToken); + return Logger.Log(await UpdateUserAsync(user)); } - await userRoleStore.AddToRoleAsync(user, role, CancellationToken); - return await LogResultAsync(await UpdateUserAsync(user), user); } /// @@ -1034,16 +1107,20 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("roles"); } - var userRoles = await userRoleStore.GetRolesAsync(user, CancellationToken); - foreach (var role in roles) + + using (await BeginLoggingScopeAsync(user)) { - if (userRoles.Contains(role)) + var userRoles = await userRoleStore.GetRolesAsync(user, CancellationToken); + foreach (var role in roles) { - return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.UserAlreadyInRole(role)), user); + if (userRoles.Contains(role)) + { + return Logger.Log(IdentityResult.Failed(ErrorDescriber.UserAlreadyInRole(role))); + } + await userRoleStore.AddToRoleAsync(user, role, CancellationToken); } - await userRoleStore.AddToRoleAsync(user, role, CancellationToken); + return Logger.Log(await UpdateUserAsync(user)); } - return await LogResultAsync(await UpdateUserAsync(user), user); } /// @@ -1060,12 +1137,16 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - if (!await userRoleStore.IsInRoleAsync(user, role, CancellationToken)) + + using (await BeginLoggingScopeAsync(user)) { - return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.UserNotInRole(role)), user); + if (!await userRoleStore.IsInRoleAsync(user, role, CancellationToken)) + { + return Logger.Log(IdentityResult.Failed(ErrorDescriber.UserNotInRole(role))); + } + await userRoleStore.RemoveFromRoleAsync(user, role, CancellationToken); + return Logger.Log(await UpdateUserAsync(user)); } - await userRoleStore.RemoveFromRoleAsync(user, role, CancellationToken); - return await LogResultAsync(await UpdateUserAsync(user), user); } /// @@ -1086,15 +1167,19 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("roles"); } - foreach (var role in roles) + + using (await BeginLoggingScopeAsync(user)) { - if (!await userRoleStore.IsInRoleAsync(user, role, CancellationToken)) + foreach (var role in roles) { - return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.UserNotInRole(role)), user); + if (!await userRoleStore.IsInRoleAsync(user, role, CancellationToken)) + { + return Logger.Log(IdentityResult.Failed(ErrorDescriber.UserNotInRole(role))); + } + await userRoleStore.RemoveFromRoleAsync(user, role, CancellationToken); } - await userRoleStore.RemoveFromRoleAsync(user, role, CancellationToken); + return Logger.Log(await UpdateUserAsync(user)); } - return await LogResultAsync(await UpdateUserAsync(user), user); } /// @@ -1171,10 +1256,14 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - await store.SetEmailAsync(user, email, CancellationToken); - await store.SetEmailConfirmedAsync(user, false, CancellationToken); - await UpdateSecurityStampInternal(user); - return await LogResultAsync(await UpdateUserAsync(user), user); + + using (await BeginLoggingScopeAsync(user)) + { + await store.SetEmailAsync(user, email, CancellationToken); + await store.SetEmailConfirmedAsync(user, false, CancellationToken); + await UpdateSecurityStampInternal(user); + return Logger.Log(await UpdateUserAsync(user)); + } } /// @@ -1217,9 +1306,13 @@ namespace Microsoft.AspNet.Identity public async virtual Task GenerateEmailConfirmationTokenAsync(TUser user) { ThrowIfDisposed(); - var token = await GenerateUserTokenAsync(user, Options.EmailConfirmationTokenProvider, "Confirmation"); - await LogResultAsync(IdentityResult.Success, user); - return token; + + using (await BeginLoggingScopeAsync(user)) + { + var token = await GenerateUserTokenAsync(user, Options.EmailConfirmationTokenProvider, "Confirmation"); + Logger.Log(IdentityResult.Success); + return token; + } } /// @@ -1236,12 +1329,16 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - if (!await VerifyUserTokenAsync(user, Options.EmailConfirmationTokenProvider, "Confirmation", token)) + + using (await BeginLoggingScopeAsync(user)) { - return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.InvalidToken()), user); + if (!await VerifyUserTokenAsync(user, Options.EmailConfirmationTokenProvider, "Confirmation", token)) + { + return Logger.Log(IdentityResult.Failed(ErrorDescriber.InvalidToken())); + } + await store.SetEmailConfirmedAsync(user, true, CancellationToken); + return Logger.Log(await UpdateUserAsync(user)); } - await store.SetEmailConfirmedAsync(user, true, CancellationToken); - return await LogResultAsync(await UpdateUserAsync(user), user); } /// @@ -1269,13 +1366,18 @@ namespace Microsoft.AspNet.Identity /// Generate a change email token for the user using the UserTokenProvider /// /// + /// /// public virtual async Task GenerateChangeEmailTokenAsync(TUser user, string newEmail) { ThrowIfDisposed(); - var token = await GenerateUserTokenAsync(user, Options.ChangeEmailTokenProvider, GetChangeEmailPurpose(newEmail)); - await LogResultAsync(IdentityResult.Success, user); - return token; + + using (await BeginLoggingScopeAsync(user)) + { + var token = await GenerateUserTokenAsync(user, Options.ChangeEmailTokenProvider, GetChangeEmailPurpose(newEmail)); + Logger.Log(IdentityResult.Success); + return token; + } } /// @@ -1283,7 +1385,7 @@ namespace Microsoft.AspNet.Identity /// /// /// - /// + /// /// public virtual async Task ChangeEmailAsync(TUser user, string newEmail, string token) { @@ -1292,16 +1394,20 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - // Make sure the token is valid and the stamp matches - if (!await VerifyUserTokenAsync(user, Options.ChangeEmailTokenProvider, GetChangeEmailPurpose(newEmail), token)) + + using (await BeginLoggingScopeAsync(user)) { - return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.InvalidToken()), user); + // Make sure the token is valid and the stamp matches + if (!await VerifyUserTokenAsync(user, Options.ChangeEmailTokenProvider, GetChangeEmailPurpose(newEmail), token)) + { + return Logger.Log(IdentityResult.Failed(ErrorDescriber.InvalidToken())); + } + var store = GetEmailStore(); + await store.SetEmailAsync(user, newEmail, CancellationToken); + await store.SetEmailConfirmedAsync(user, true, CancellationToken); + await UpdateSecurityStampInternal(user); + return Logger.Log(await UpdateUserAsync(user)); } - var store = GetEmailStore(); - await store.SetEmailAsync(user, newEmail, CancellationToken); - await store.SetEmailConfirmedAsync(user, true, CancellationToken); - await UpdateSecurityStampInternal(user); - return await LogResultAsync(await UpdateUserAsync(user), user); } // IUserPhoneNumberStore methods @@ -1345,10 +1451,14 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - await store.SetPhoneNumberAsync(user, phoneNumber, CancellationToken); - await store.SetPhoneNumberConfirmedAsync(user, false, CancellationToken); - await UpdateSecurityStampInternal(user); - return await LogResultAsync(await UpdateUserAsync(user), user); + + using (await BeginLoggingScopeAsync(user)) + { + await store.SetPhoneNumberAsync(user, phoneNumber, CancellationToken); + await store.SetPhoneNumberConfirmedAsync(user, false, CancellationToken); + await UpdateSecurityStampInternal(user); + return Logger.Log(await UpdateUserAsync(user)); + } } /// @@ -1366,14 +1476,18 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - if (!await VerifyChangePhoneNumberTokenAsync(user, token, phoneNumber)) + + using (await BeginLoggingScopeAsync(user)) { - return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.InvalidToken()), user); + if (!await VerifyChangePhoneNumberTokenAsync(user, token, phoneNumber)) + { + return Logger.Log(IdentityResult.Failed(ErrorDescriber.InvalidToken())); + } + await store.SetPhoneNumberAsync(user, phoneNumber, CancellationToken); + await store.SetPhoneNumberConfirmedAsync(user, true, CancellationToken); + await UpdateSecurityStampInternal(user); + return Logger.Log(await UpdateUserAsync(user)); } - await store.SetPhoneNumberAsync(user, phoneNumber, CancellationToken); - await store.SetPhoneNumberConfirmedAsync(user, true, CancellationToken); - await UpdateSecurityStampInternal(user); - return await LogResultAsync(await UpdateUserAsync(user), user); } /// @@ -1407,11 +1521,15 @@ namespace Microsoft.AspNet.Identity public virtual async Task GenerateChangePhoneNumberTokenAsync(TUser user, string phoneNumber) { ThrowIfDisposed(); - var token = Rfc6238AuthenticationService.GenerateCode( - await CreateSecurityTokenAsync(user), phoneNumber) - .ToString(CultureInfo.InvariantCulture); - await LogResultAsync(IdentityResult.Success, user); - return token; + + using (await BeginLoggingScopeAsync(user)) + { + var token = Rfc6238AuthenticationService.GenerateCode( + await CreateSecurityTokenAsync(user), phoneNumber) + .ToString(CultureInfo.InvariantCulture); + Logger.Log(IdentityResult.Success); + return token; + } } /// @@ -1424,24 +1542,29 @@ namespace Microsoft.AspNet.Identity public virtual async Task VerifyChangePhoneNumberTokenAsync(TUser user, string token, string phoneNumber) { ThrowIfDisposed(); - var securityToken = await CreateSecurityTokenAsync(user); - int code; - if (securityToken != null && Int32.TryParse(token, out code)) + + using (await BeginLoggingScopeAsync(user)) { - if (Rfc6238AuthenticationService.ValidateCode(securityToken, code, phoneNumber)) + var securityToken = await CreateSecurityTokenAsync(user); + int code; + if (securityToken != null && Int32.TryParse(token, out code)) { - await LogResultAsync(IdentityResult.Success, user); - return true; + if (Rfc6238AuthenticationService.ValidateCode(securityToken, code, phoneNumber)) + { + Logger.Log(IdentityResult.Success); + return true; + } } + Logger.Log(IdentityResult.Failed(ErrorDescriber.InvalidToken())); + return false; } - await LogResultAsync(IdentityResult.Failed(ErrorDescriber.InvalidToken()), user); - return false; } /// /// Verify a user token with the specified purpose /// /// + /// /// /// /// @@ -1456,23 +1579,27 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException(nameof(tokenProvider)); } - if (!_tokenProviders.ContainsKey(tokenProvider)) - { - throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, Resources.NoTokenProvider, tokenProvider)); - } - // Make sure the token is valid - var result = await _tokenProviders[tokenProvider].ValidateAsync(purpose, token, this, user); - if (result) + using (await BeginLoggingScopeAsync(user)) { - await LogResultAsync(IdentityResult.Success, user); - } - else - { - await LogResultAsync(IdentityResult.Failed(ErrorDescriber.InvalidToken()), user); - } + if (!_tokenProviders.ContainsKey(tokenProvider)) + { + throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, Resources.NoTokenProvider, tokenProvider)); + } + // Make sure the token is valid + var result = await _tokenProviders[tokenProvider].ValidateAsync(purpose, token, this, user); - return result; + if (result) + { + Logger.Log(IdentityResult.Success); + } + else + { + Logger.Log(IdentityResult.Failed(ErrorDescriber.InvalidToken())); + } + + return result; + } } /// @@ -1480,6 +1607,7 @@ namespace Microsoft.AspNet.Identity /// /// /// + /// /// public virtual async Task GenerateUserTokenAsync(TUser user, string tokenProvider, string purpose) { @@ -1497,8 +1625,9 @@ namespace Microsoft.AspNet.Identity throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, Resources.NoTokenProvider, tokenProvider)); } + //TODO: Should we scope here ? var token = await _tokenProviders[tokenProvider].GenerateAsync(purpose, this, user); - await LogResultAsync(IdentityResult.Success, user); + Logger.Log(IdentityResult.Success); return token; } @@ -1543,7 +1672,7 @@ namespace Microsoft.AspNet.Identity /// Verify a user token with the specified provider /// /// - /// + /// /// /// public virtual async Task VerifyTwoFactorTokenAsync(TUser user, string tokenProvider, string token) @@ -1558,24 +1687,28 @@ namespace Microsoft.AspNet.Identity throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture, Resources.NoTokenProvider, tokenProvider)); } - // Make sure the token is valid - var result = await _tokenProviders[tokenProvider].ValidateAsync("TwoFactor", token, this, user); - if (result) + + using (await BeginLoggingScopeAsync(user)) { - await LogResultAsync(IdentityResult.Success, user); + // Make sure the token is valid + var result = await _tokenProviders[tokenProvider].ValidateAsync("TwoFactor", token, this, user); + if (result) + { + Logger.Log(IdentityResult.Success); + } + else + { + Logger.Log(IdentityResult.Failed(ErrorDescriber.InvalidToken())); + } + return result; } - else - { - await LogResultAsync(IdentityResult.Failed(ErrorDescriber.InvalidToken()), user); - } - return result; } /// /// Get a user token for a specific user factor provider /// /// - /// + /// /// public virtual async Task GenerateTwoFactorTokenAsync(TUser user, string tokenProvider) { @@ -1589,9 +1722,13 @@ namespace Microsoft.AspNet.Identity throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture, Resources.NoTokenProvider, tokenProvider)); } - var token = await _tokenProviders[tokenProvider].GenerateAsync("TwoFactor", this, user); - await LogResultAsync(IdentityResult.Success, user); - return token; + + using (await BeginLoggingScopeAsync(user)) + { + var token = await _tokenProviders[tokenProvider].GenerateAsync("TwoFactor", this, user); + Logger.Log(IdentityResult.Success); + return token; + } } // IUserFactorStore methods @@ -1635,9 +1772,13 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - await store.SetTwoFactorEnabledAsync(user, enabled, CancellationToken); - await UpdateSecurityStampInternal(user); - return await LogResultAsync(await UpdateUserAsync(user), user); + + using (await BeginLoggingScopeAsync(user)) + { + await store.SetTwoFactorEnabledAsync(user, enabled, CancellationToken); + await UpdateSecurityStampInternal(user); + return Logger.Log(await UpdateUserAsync(user)); + } } // IUserLockoutStore methods @@ -1686,8 +1827,12 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - await store.SetLockoutEnabledAsync(user, enabled, CancellationToken); - return await LogResultAsync(await UpdateUserAsync(user), user); + + using (await BeginLoggingScopeAsync(user)) + { + await store.SetLockoutEnabledAsync(user, enabled, CancellationToken); + return Logger.Log(await UpdateUserAsync(user)); + } } /// @@ -1736,12 +1881,16 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - if (!await store.GetLockoutEnabledAsync(user, CancellationToken)) + + using (await BeginLoggingScopeAsync(user)) { - return await LogResultAsync(IdentityResult.Failed(ErrorDescriber.UserLockoutNotEnabled()), user); + if (!await store.GetLockoutEnabledAsync(user, CancellationToken)) + { + return Logger.Log(IdentityResult.Failed(ErrorDescriber.UserLockoutNotEnabled())); + } + await store.SetLockoutEndDateAsync(user, lockoutEnd, CancellationToken); + return Logger.Log(await UpdateUserAsync(user)); } - await store.SetLockoutEndDateAsync(user, lockoutEnd, CancellationToken); - return await LogResultAsync(await UpdateUserAsync(user), user); } /// @@ -1759,16 +1908,20 @@ namespace Microsoft.AspNet.Identity { throw new ArgumentNullException("user"); } - // If this puts the user over the threshold for lockout, lock them out and reset the access failed count - var count = await store.IncrementAccessFailedCountAsync(user, CancellationToken); - if (count < Options.Lockout.MaxFailedAccessAttempts) + + using (await BeginLoggingScopeAsync(user)) { - return await LogResultAsync(await UpdateUserAsync(user), user); + // If this puts the user over the threshold for lockout, lock them out and reset the access failed count + var count = await store.IncrementAccessFailedCountAsync(user, CancellationToken); + if (count < Options.Lockout.MaxFailedAccessAttempts) + { + return Logger.Log(await UpdateUserAsync(user)); + } + await store.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.Add(Options.Lockout.DefaultLockoutTimeSpan), + CancellationToken); + await store.ResetAccessFailedCountAsync(user, CancellationToken); + return Logger.Log(await UpdateUserAsync(user)); } - await store.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.Add(Options.Lockout.DefaultLockoutTimeSpan), - CancellationToken); - await store.ResetAccessFailedCountAsync(user, CancellationToken); - return await LogResultAsync(await UpdateUserAsync(user), user); } /// @@ -1785,13 +1938,15 @@ namespace Microsoft.AspNet.Identity throw new ArgumentNullException("user"); } - if (await GetAccessFailedCountAsync(user) == 0) + using (await BeginLoggingScopeAsync(user)) { - return IdentityResult.Success; + if (await GetAccessFailedCountAsync(user) == 0) + { + return Logger.Log(IdentityResult.Success); + } + await store.ResetAccessFailedCountAsync(user, CancellationToken); + return Logger.Log(await UpdateUserAsync(user)); } - - await store.ResetAccessFailedCountAsync(user, CancellationToken); - return await LogResultAsync(await UpdateUserAsync(user), user); } /// @@ -1824,7 +1979,7 @@ namespace Microsoft.AspNet.Identity /// /// Get all the users in a role /// - /// + /// /// public virtual Task> GetUsersInRoleAsync(string roleName) { @@ -1838,42 +1993,12 @@ namespace Microsoft.AspNet.Identity return store.GetUsersInRoleAsync(roleName, CancellationToken); } - /// - /// Logs the current Identity Result and returns result object - /// - /// - /// - /// - /// - protected async Task LogResultAsync(IdentityResult result, - TUser user, [System.Runtime.CompilerServices.CallerMemberName] string methodName = "") + protected virtual async Task BeginLoggingScopeAsync(TUser user, [CallerMemberName] string methodName = null) { - result.Log(Logger, Resources.FormatLoggingResultMessage(methodName, await GetUserIdAsync(user))); - return result; - } - - /// - /// Logs result of operation being true/false - /// - /// - /// - /// - /// result - protected async Task LogResultAsync(bool result, - TUser user, [System.Runtime.CompilerServices.CallerMemberName] string methodName = "") - { - var baseMessage = Resources.FormatLoggingResultMessage(methodName, await GetUserIdAsync(user)); - if (result) - { - Logger.LogInformation(string.Format("{0} : {1}", baseMessage, result.ToString())); - } - else - { - Logger.LogWarning(string.Format("{0} : {1}", baseMessage, result.ToString())); - } - - return result; + var state = Resources.FormatLoggingResultMessageForUser(methodName, await GetUserIdAsync(user)); + return Logger.BeginScope(state); } + private void ThrowIfDisposed() { diff --git a/test/Microsoft.AspNet.Identity.Test/IdentityResultTest.cs b/test/Microsoft.AspNet.Identity.Test/IdentityResultTest.cs index 479f3f94ce..fcaf1edd4c 100644 --- a/test/Microsoft.AspNet.Identity.Test/IdentityResultTest.cs +++ b/test/Microsoft.AspNet.Identity.Test/IdentityResultTest.cs @@ -24,29 +24,5 @@ namespace Microsoft.AspNet.Identity.Test Assert.False(result.Succeeded); Assert.Equal(0, result.Errors.Count()); } - - [Fact] - public void VerifySuccessResultLog() - { - var result = IdentityResult.Success; - var logMessage = new StringBuilder(); - var logger = MockHelpers.MockILogger(logMessage); - - result.Log(logger.Object, "Operation"); - - Assert.Equal("Operation : Success", logMessage.ToString()); - } - - [Fact] - public void VerifyFailureResultLog() - { - var result = IdentityResult.Failed(new IdentityError() { Code = "Foo" }, new IdentityError() { Code = "Bar" }); - var logMessage = new StringBuilder(); - var logger = MockHelpers.MockILogger(logMessage); - - result.Log(logger.Object, "Operation"); - - Assert.Equal("Operation : Failed : Foo,Bar", logMessage.ToString()); - } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.Test/SignInManagerTest.cs b/test/Microsoft.AspNet.Identity.Test/SignInManagerTest.cs index cdc1449b09..948f53588e 100644 --- a/test/Microsoft.AspNet.Identity.Test/SignInManagerTest.cs +++ b/test/Microsoft.AspNet.Identity.Test/SignInManagerTest.cs @@ -129,7 +129,8 @@ namespace Microsoft.AspNet.Identity.Test var logger = MockHelpers.MockILogger>(logStore); var helper = new SignInManager(manager.Object, contextAccessor.Object, claimsFactory, options.Object, null); helper.Logger = logger.Object; - string expected = string.Format("{0} for user: {1} : Result : {2}", "PasswordSignInAsync", user.Id, "Lockedout"); + string expectedScope = string.Format("{0} for {1}: {2}", "PasswordSignInAsync", "user", user.Id); + string expectedLog = string.Format("{0} : {1}", "PasswordSignInAsync", "Lockedout"); // Act var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false); @@ -137,7 +138,8 @@ namespace Microsoft.AspNet.Identity.Test // Assert Assert.False(result.Succeeded); Assert.True(result.IsLockedOut); - Assert.NotEqual(-1, logStore.ToString().IndexOf(expected)); + Assert.NotEqual(-1, logStore.ToString().IndexOf(expectedLog)); + Assert.NotEqual(-1, logStore.ToString().IndexOf(expectedScope)); manager.Verify(); } @@ -178,14 +180,16 @@ namespace Microsoft.AspNet.Identity.Test var logger = MockHelpers.MockILogger>(logStore); var helper = new SignInManager(manager.Object, contextAccessor.Object, claimsFactory, options.Object, null); helper.Logger = logger.Object; - string expected = string.Format("{0} for user: {1} : Result : {2}", "PasswordSignInAsync", user.Id, "Succeeded"); + string expectedScope = string.Format("{0} for {1}: {2}", "PasswordSignInAsync", "user", user.Id); + string expectedLog = string.Format("{0} : {1}", "PasswordSignInAsync", "Succeeded"); // Act var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false); // Assert Assert.True(result.Succeeded); - Assert.NotEqual(-1, logStore.ToString().IndexOf(expected)); + Assert.NotEqual(-1, logStore.ToString().IndexOf(expectedLog)); + Assert.NotEqual(-1, logStore.ToString().IndexOf(expectedScope)); manager.Verify(); context.Verify(); response.Verify(); @@ -264,13 +268,14 @@ namespace Microsoft.AspNet.Identity.Test options.Setup(a => a.Options).Returns(identityOptions); var logStore = new StringBuilder(); var logger = MockHelpers.MockILogger>(logStore); - var helper = new SignInManager(manager.Object, - contextAccessor.Object, - new UserClaimsPrincipalFactory(manager.Object, roleManager.Object, options.Object), - options.Object, + var helper = new SignInManager(manager.Object, + contextAccessor.Object, + new UserClaimsPrincipalFactory(manager.Object, roleManager.Object, options.Object), + options.Object, null); helper.Logger = logger.Object; - string expected = string.Format("{0} for user: {1} : Result : {2}", "PasswordSignInAsync", user.Id, "RequiresTwoFactor"); + string expectedScope = string.Format("{0} for {1}: {2}", "PasswordSignInAsync", "user", user.Id); + string expectedLog = string.Format("{0} : {1}", "PasswordSignInAsync", "RequiresTwoFactor"); // Act var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false); @@ -278,7 +283,8 @@ namespace Microsoft.AspNet.Identity.Test // Assert Assert.False(result.Succeeded); Assert.True(result.RequiresTwoFactor); - Assert.NotEqual(-1, logStore.ToString().IndexOf(expected)); + Assert.NotEqual(-1, logStore.ToString().IndexOf(expectedLog)); + Assert.NotEqual(-1, logStore.ToString().IndexOf(expectedScope)); manager.Verify(); context.Verify(); response.Verify(); @@ -320,14 +326,16 @@ namespace Microsoft.AspNet.Identity.Test var logger = MockHelpers.MockILogger>(logStore); var helper = new SignInManager(manager.Object, contextAccessor.Object, claimsFactory, options.Object, null); helper.Logger = logger.Object; - string expected = string.Format("{0} for user: {1} : Result : {2}", "ExternalLoginSignInAsync", user.Id.ToString(), "Succeeded"); + string expectedScope = string.Format("{0} for {1}: {2}", "ExternalLoginSignInAsync", "user", user.Id); + string expectedLog = string.Format("{0} : {1}", "ExternalLoginSignInAsync", "Succeeded"); // Act var result = await helper.ExternalLoginSignInAsync(loginProvider, providerKey, isPersistent); // Assert Assert.True(result.Succeeded); - Assert.NotEqual(-1, logStore.ToString().IndexOf(expected)); + Assert.NotEqual(-1, logStore.ToString().IndexOf(expectedLog)); + Assert.NotEqual(-1, logStore.ToString().IndexOf(expectedScope)); manager.Verify(); context.Verify(); response.Verify(); @@ -405,14 +413,16 @@ namespace Microsoft.AspNet.Identity.Test var logger = MockHelpers.MockILogger>(logStore); var helper = new SignInManager(manager.Object, contextAccessor.Object, claimsFactory, options.Object, null); helper.Logger = logger.Object; - string expected = string.Format("{0} for user: {1} : Result : {2}", "TwoFactorSignInAsync", user.Id.ToString(), "Succeeded"); + string expectedScope = string.Format("{0} for {1}: {2}", "TwoFactorSignInAsync", "user", user.Id); + string expectedLog = string.Format("{0} : {1}", "TwoFactorSignInAsync", "Succeeded"); // Act var result = await helper.TwoFactorSignInAsync(provider, code, isPersistent, rememberClient); // Assert Assert.True(result.Succeeded); - Assert.NotEqual(-1, logStore.ToString().IndexOf(expected)); + Assert.NotEqual(-1, logStore.ToString().IndexOf(expectedLog)); + Assert.NotEqual(-1, logStore.ToString().IndexOf(expectedScope)); manager.Verify(); context.Verify(); response.Verify(); @@ -559,13 +569,15 @@ namespace Microsoft.AspNet.Identity.Test var logger = MockHelpers.MockILogger>(logStore); var helper = new SignInManager(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object); helper.Logger = logger.Object; - string expected = string.Format("{0} for user: {1} : Result : {2}", "PasswordSignInAsync", user.Id.ToString(), "Failed"); + string expectedScope = string.Format("{0} for {1}: {2}", "PasswordSignInAsync", "user", user.Id); + string expectedLog = string.Format("{0} : {1}", "PasswordSignInAsync", "Failed"); // Act var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false); // Assert Assert.False(result.Succeeded); - Assert.NotEqual(-1, logStore.ToString().IndexOf(expected)); + Assert.NotEqual(-1, logStore.ToString().IndexOf(expectedLog)); + Assert.NotEqual(-1, logStore.ToString().IndexOf(expectedScope)); manager.Verify(); context.Verify(); contextAccessor.Verify(); @@ -664,7 +676,8 @@ namespace Microsoft.AspNet.Identity.Test var logger = MockHelpers.MockILogger>(logStore); var helper = new SignInManager(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object); helper.Logger = logger.Object; - string expected = string.Format("{0} for user: {1} : Result : {2}", "CanSignInAsync", user.Id.ToString(), confirmed.ToString()); + string expectedScope = string.Format("{0} for {1}: {2}", "PasswordSignInAsync", "user", user.Id); + string expectedLog = string.Format("{0} : {1}", "CanSignInAsync", confirmed.ToString()); // Act var result = await helper.PasswordSignInAsync(user, "password", false, false); @@ -673,7 +686,9 @@ namespace Microsoft.AspNet.Identity.Test Assert.Equal(confirmed, result.Succeeded); Assert.NotEqual(confirmed, result.IsNotAllowed); - Assert.NotEqual(-1, logStore.ToString().IndexOf(expected)); + Assert.NotEqual(-1, logStore.ToString().IndexOf(expectedLog)); + Assert.NotEqual(-1, logStore.ToString().IndexOf(expectedScope)); + manager.Verify(); context.Verify(); response.Verify(); @@ -683,7 +698,7 @@ namespace Microsoft.AspNet.Identity.Test private static void SetupSignIn(Mock response, string userId = null, bool? isPersistent = null, string loginProvider = null) { response.Setup(r => r.SignIn(IdentityOptions.ApplicationCookieAuthenticationScheme, - It.Is(id => + It.Is(id => (userId == null || id.FindFirstValue(ClaimTypes.NameIdentifier) == userId) && (loginProvider == null || id.FindFirstValue(ClaimTypes.AuthenticationMethod) == loginProvider)), It.Is(v => isPersistent == null || v.IsPersistent == isPersistent))).Verifiable(); @@ -719,7 +734,8 @@ namespace Microsoft.AspNet.Identity.Test var logger = MockHelpers.MockILogger>(logStore); var helper = new SignInManager(manager.Object, contextAccessor.Object, claimsFactory.Object, options.Object, null); helper.Logger = logger.Object; - string expected = string.Format("{0} for user: {1} : Result : {2}", "CanSignInAsync", user.Id.ToString(), confirmed.ToString()); + string expectedScope = string.Format("{0} for {1}: {2}", "PasswordSignInAsync", "user", user.Id); + string expectedLog = string.Format("{0} : {1}", "CanSignInAsync", confirmed.ToString()); // Act var result = await helper.PasswordSignInAsync(user, "password", false, false); @@ -727,7 +743,8 @@ namespace Microsoft.AspNet.Identity.Test // Assert Assert.Equal(confirmed, result.Succeeded); Assert.NotEqual(confirmed, result.IsNotAllowed); - Assert.NotEqual(-1, logStore.ToString().IndexOf(expected)); + Assert.NotEqual(-1, logStore.ToString().IndexOf(expectedLog)); + Assert.NotEqual(-1, logStore.ToString().IndexOf(expectedScope)); manager.Verify(); context.Verify(); response.Verify(); diff --git a/test/Microsoft.AspNet.Identity.Test/SignInResultTest.cs b/test/Microsoft.AspNet.Identity.Test/SignInResultTest.cs deleted file mode 100644 index 267db1a00f..0000000000 --- a/test/Microsoft.AspNet.Identity.Test/SignInResultTest.cs +++ /dev/null @@ -1,71 +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.Text; -using Xunit; - -namespace Microsoft.AspNet.Identity.Test -{ - public class SignInResultTest - { - [Fact] - public void VerifyLogSuccess() - { - var result = SignInResult.Success; - var logMessage = new StringBuilder(); - var logger = MockHelpers.MockILogger(logMessage); - - result.Log(logger.Object, "Operation"); - - Assert.Equal("Operation : Result : Succeeded", logMessage.ToString()); - } - - [Fact] - public void VerifyLogLockedOut() - { - var result = SignInResult.LockedOut; - var logMessage = new StringBuilder(); - var logger = MockHelpers.MockILogger(logMessage); - - result.Log(logger.Object, "Operation"); - - Assert.Equal("Operation : Result : Lockedout", logMessage.ToString()); - } - - [Fact] - public void VerifyLogNotAllowed() - { - var result = SignInResult.NotAllowed; - var logMessage = new StringBuilder(); - var logger = MockHelpers.MockILogger(logMessage); - - result.Log(logger.Object, "Operation"); - - Assert.Equal("Operation : Result : NotAllowed", logMessage.ToString()); - } - - [Fact] - public void VerifyLogRequiresTwoFactor() - { - var result = SignInResult.TwoFactorRequired; - var logMessage = new StringBuilder(); - var logger = MockHelpers.MockILogger(logMessage); - - result.Log(logger.Object, "Operation"); - - Assert.Equal("Operation : Result : RequiresTwoFactor", logMessage.ToString()); - } - - [Fact] - public void VerifyLogRequiresFailed() - { - var result = SignInResult.Failed; - var logMessage = new StringBuilder(); - var logger = MockHelpers.MockILogger(logMessage); - - result.Log(logger.Object, "Operation"); - - Assert.Equal("Operation : Result : Failed", logMessage.ToString()); - } - } -} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Identity.Test/UserManagerTest.cs b/test/Microsoft.AspNet.Identity.Test/UserManagerTest.cs index 827e5e89e5..d1c01f17fd 100644 --- a/test/Microsoft.AspNet.Identity.Test/UserManagerTest.cs +++ b/test/Microsoft.AspNet.Identity.Test/UserManagerTest.cs @@ -420,6 +420,10 @@ namespace Microsoft.AspNet.Identity.Test store.Setup(s => s.GetPasswordHashAsync(user, CancellationToken.None)) .ReturnsAsync(hashed) .Verifiable(); + store.Setup(x => x.GetUserIdAsync(It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(Guid.NewGuid().ToString())); + store.Setup(x => x.UpdateAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(IdentityResult.Success)); + hasher.Setup(s => s.VerifyHashedPassword(user, hashed, pwd)).Returns(PasswordVerificationResult.SuccessRehashNeeded).Verifiable(); hasher.Setup(s => s.HashPassword(user, pwd)).Returns(rehashed).Verifiable(); var userManager = MockHelpers.TestUserManager(store.Object); @@ -540,7 +544,9 @@ namespace Microsoft.AspNet.Identity.Test [Fact] public async Task SecurityStampMethodsFailWhenStoreNotImplemented() { - var manager = MockHelpers.TestUserManager(new NoopUserStore()); + var store = new Mock>(); + store.Setup(x => x.GetUserIdAsync(It.IsAny(), It.IsAny())).Returns(Task.FromResult(Guid.NewGuid().ToString())); + var manager = MockHelpers.TestUserManager(store.Object); Assert.False(manager.SupportsUserSecurityStamp); await Assert.ThrowsAsync(() => manager.UpdateSecurityStampAsync(null)); await Assert.ThrowsAsync(() => manager.GetSecurityStampAsync(null)); diff --git a/test/Shared/IdentityResultAssert.cs b/test/Shared/IdentityResultAssert.cs index 80fabb9c02..f5c38749f7 100644 --- a/test/Shared/IdentityResultAssert.cs +++ b/test/Shared/IdentityResultAssert.cs @@ -62,8 +62,10 @@ namespace Microsoft.AspNet.Identity.Test TestLogger testlogger = logger as TestLogger; if (testlogger != null) { - string expected = string.Format("{0} for {1}: {2} : Success", methodName, userOrRole, id); - Assert.True(testlogger.LogMessages.Contains(expected)); + string expectedScope = string.Format("{0} for {1}: {2}", methodName, userOrRole, id); + string expectedLog = string.Format("{0} : Succeeded", methodName); + Assert.True(testlogger.LogMessages.Contains(expectedScope)); + Assert.True(testlogger.LogMessages.Contains(expectedLog)); } else { @@ -84,15 +86,17 @@ namespace Microsoft.AspNet.Identity.Test } } - private static void VerifyFailureLog(ILogger logger, string className, string methodName, string userId, string userOrRole = "user", params IdentityError[] errors) + private static void VerifyFailureLog(ILogger logger, string className, string methodName, string id, string userOrRole = "user", params IdentityError[] errors) { TestLogger testlogger = logger as TestLogger; if (testlogger != null) { errors = errors ?? new IdentityError[] { new IdentityError() }; - string expected = string.Format("{0} for {1}: {2} : Failed : {3}", methodName, userOrRole, userId, string.Join(",", errors.Select(x => x.Code).ToList())); + string expectedScope = string.Format("{0} for {1}: {2}", methodName, userOrRole, id); + string expectedLog = string.Format("{0} : Failed : {1}", methodName, string.Join(",", errors.Select(x => x.Code).ToList())); - Assert.True(testlogger.LogMessages.Contains(expected)); + Assert.True(testlogger.LogMessages.Contains(expectedScope)); + Assert.True(testlogger.LogMessages.Contains(expectedLog)); } else { diff --git a/test/Shared/MockHelpers.cs b/test/Shared/MockHelpers.cs index 98f554c3f8..2f3efb40a6 100644 --- a/test/Shared/MockHelpers.cs +++ b/test/Shared/MockHelpers.cs @@ -42,8 +42,23 @@ namespace Microsoft.AspNet.Identity.Test logger.Setup(x => x.Log(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>())) .Callback((LogLevel logLevel, int eventId, object state, Exception exception, Func formatter) => - { logStore.Append(state.ToString()); }); - logger.Setup(x => x.IsEnabled(LogLevel.Information)).Returns(true); + { + if (formatter == null) + { + logStore.Append(state.ToString()); + } + else + { + logStore.Append(formatter(state, exception)); + } + logStore.Append(" "); + }); + logger.Setup(x => x.BeginScope(It.IsAny())).Callback((object state) => + { + logStore.Append(state.ToString()); + logStore.Append(" "); + }); + logger.Setup(x => x.IsEnabled(LogLevel.Verbose)).Returns(true); logger.Setup(x => x.IsEnabled(LogLevel.Warning)).Returns(true); return logger; @@ -75,8 +90,8 @@ namespace Microsoft.AspNet.Identity.Test store = store ?? new Mock>().Object; var roles = new List>(); roles.Add(new RoleValidator()); - return new RoleManager(store, roles, - new UpperInvariantLookupNormalizer(), + return new RoleManager(store, roles, + new UpperInvariantLookupNormalizer(), new IdentityErrorDescriber(), null, null); diff --git a/test/Shared/TestLogger.cs b/test/Shared/TestLogger.cs index 6032552c4c..8ad025d128 100644 --- a/test/Shared/TestLogger.cs +++ b/test/Shared/TestLogger.cs @@ -13,7 +13,8 @@ namespace Microsoft.AspNet.Identity.Test public IDisposable BeginScope(object state) { - throw new NotImplementedException(); + LogMessages.Add(state?.ToString()); + return null; } public bool IsEnabled(LogLevel logLevel) @@ -23,7 +24,14 @@ namespace Microsoft.AspNet.Identity.Test public void Log(LogLevel logLevel, int eventId, object state, Exception exception, Func formatter) { - LogMessages.Add(state.ToString()); + if (formatter == null) + { + LogMessages.Add(state.ToString()); + } + else + { + LogMessages.Add(formatter(state, exception)); + } } } } \ No newline at end of file diff --git a/test/Shared/UserManagerTestBase.cs b/test/Shared/UserManagerTestBase.cs index d397e6331c..cf94c2539d 100644 --- a/test/Shared/UserManagerTestBase.cs +++ b/test/Shared/UserManagerTestBase.cs @@ -141,13 +141,13 @@ namespace Microsoft.AspNet.Identity.Test var user = new TUser() { UserName = "UpdatePassword" }; IdentityResultAssert.IsSuccess(await manager.CreateAsync(user, "password")); Assert.True(await manager.CheckPasswordAsync(user, "password")); - string expectedLog = string.Format("{0} for user: {1} : {2}", "CheckPasswordAsync", user.Id.ToString(), true.ToString()); + string expectedLog = string.Format("{0} : {1}", "CheckPasswordAsync", true.ToString()); IdentityResultAssert.VerifyLogMessage(manager.Logger, expectedLog); user.PasswordHash = manager.PasswordHasher.HashPassword(user, "New"); IdentityResultAssert.IsSuccess(await manager.UpdateAsync(user)); Assert.False(await manager.CheckPasswordAsync(user, "password")); - expectedLog = string.Format("{0} for user: {1} : {2}", "CheckPasswordAsync", user.Id.ToString(), false.ToString()); + expectedLog = string.Format("{0} : {1}", "CheckPasswordAsync", false.ToString()); IdentityResultAssert.VerifyLogMessage(manager.Logger, expectedLog); Assert.True(await manager.CheckPasswordAsync(user, "New")); } @@ -708,7 +708,8 @@ namespace Microsoft.AspNet.Identity.Test IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); IdentityResultAssert.IsSuccess(await manager.CreateAsync(user2)); var token = await manager.GenerateUserTokenAsync(user, "Static", "test"); - IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "GenerateUserTokenAsync", user.Id.ToString()); + var expectedLog = string.Format("{0} : {1}", "GenerateUserTokenAsync", "Succeeded"); + IdentityResultAssert.VerifyLogMessage(manager.Logger, expectedLog); Assert.True(await manager.VerifyUserTokenAsync(user, "Static", "test", token)); IdentityResultAssert.VerifyUserManagerSuccessLog(manager.Logger, "VerifyUserTokenAsync", user.Id.ToString());