diff --git a/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj b/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj index 04182d1a9..ae3cb3f3a 100644 --- a/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj +++ b/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj @@ -148,6 +148,8 @@ + + diff --git a/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs b/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs new file mode 100644 index 000000000..010b3dcf7 --- /dev/null +++ b/src/Orchard.Tests.Modules/Users/Controllers/AccountControllerTests.cs @@ -0,0 +1,292 @@ +using System; +using System.Collections.Generic; +using System.Web; +using System.Web.Mvc; +using System.Web.Routing; +using System.Xml.Linq; +using Autofac; +using Moq; +using NUnit.Framework; +using Orchard.Caching; +using Orchard.ContentManagement.MetaData; +using Orchard.ContentManagement.MetaData.Models; +using Orchard.ContentManagement.MetaData.Services; +using Orchard.Core.Settings.Metadata; +using Orchard.Data; +using Orchard.DisplayManagement; +using Orchard.DisplayManagement.Descriptors; +using Orchard.DisplayManagement.Implementation; +using Orchard.Environment; +using Orchard.ContentManagement; +using Orchard.ContentManagement.Handlers; +using Orchard.ContentManagement.Records; +using Orchard.Environment.Extensions; +using Orchard.Localization; +using Orchard.Messaging.Events; +using Orchard.Messaging.Services; +using Orchard.Security; +using Orchard.Security.Permissions; +using Orchard.Tests.Stubs; +using Orchard.UI.Notify; +using Orchard.Users.Controllers; +using Orchard.Users.Handlers; +using Orchard.Users.Models; +using Orchard.Users.Services; +using Orchard.Users.ViewModels; +using Orchard.Settings; +using Orchard.Core.Settings.Services; +using Orchard.Tests.Messaging; +using Orchard.Environment.Configuration; +using Orchard.Core.Settings.Models; +using Orchard.Core.Settings.Handlers; +using Orchard.Messaging.Models; +using System.Collections.Specialized; + +namespace Orchard.Tests.Modules.Users.Controllers { + [TestFixture] + public class AccountControllerTests : DatabaseEnabledTestsBase { + private AccountController _controller; + private Mock _authorizer; + private Mock _workContext; + private MessagingChannelStub _channel; + + public override void Register(ContainerBuilder builder) { + builder.RegisterType().SingleInstance(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType(typeof(SettingsFormatter)) + .As(typeof(IMapper)) + .As(typeof(IMapper)); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As().InstancePerDependency(); + builder.RegisterType().As(); + builder.RegisterInstance(_channel = new MessagingChannelStub()).As(); + builder.RegisterInstance(new Mock().Object); + builder.RegisterInstance(new Mock().Object); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterInstance(new Mock().Object); + builder.RegisterInstance(new Mock().Object); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterInstance(new ShellSettings { Name = "Alpha", RequestUrlHost = "wiki.example.com", RequestUrlPrefix = "~/foo" }); + + _authorizer = new Mock(); + builder.RegisterInstance(_authorizer.Object); + + _authorizer.Setup(x => x.Authorize(It.IsAny(), It.IsAny())).Returns(true); + + _workContext = new Mock(); + _workContext.Setup(w => w.GetState(It.Is(s => s == "CurrentSite"))).Returns(() => { return _container.Resolve().GetSiteSettings(); }); + + var _workContextAccessor = new Mock(); + _workContextAccessor.Setup(w => w.GetContext()).Returns(_workContext.Object); + builder.RegisterInstance(_workContextAccessor.Object).As(); + + } + + protected override IEnumerable DatabaseTypes { + get { + return new[] { typeof(UserPartRecord), + typeof(SiteSettingsPartRecord), + typeof(RegistrationSettingsPartRecord), + typeof(ContentTypeRecord), + typeof(ContentItemRecord), + typeof(ContentItemVersionRecord), + }; + } + } + + public override void Init() { + base.Init(); + + var manager = _container.Resolve(); + + var superUser = manager.New("User"); + superUser.Record = new UserPartRecord { UserName = "admin", NormalizedUserName = "admin", Email = "admin@orcharproject.com" }; + manager.Create(superUser.ContentItem); + + _controller = _container.Resolve(); + + var mockHttpContext = new Mock(); + _controller.ControllerContext = new ControllerContext( + mockHttpContext.Object, + new RouteData( + new Route("foo", new MvcRouteHandler()), + new MvcRouteHandler()), + _controller); + } + + [Test] + public void UsersShouldNotBeAbleToRegisterIfNotAllowed() { + + // enable user registration + _container.Resolve().GetContext().CurrentSite.As().UsersCanRegister = false; + _session.Flush(); + + var result = _controller.Register(); + Assert.That(result, Is.TypeOf()); + + result = _controller.Register("bar", "bar@baz.com", "66554321", "66554321"); + Assert.That(result, Is.TypeOf()); + } + + [Test] + public void UsersShouldBeAbleToRegisterIfAllowed() { + + // disable user registration + _container.Resolve().GetContext().CurrentSite.As().UsersCanRegister = true; + _session.Flush(); + + var result = _controller.Register(); + Assert.That(result, Is.TypeOf()); + } + + [Test] + public void RegisteredUserShouldBeRedirectedToHomePage() { + + var registrationSettings = _container.Resolve().GetContext().CurrentSite.As(); + registrationSettings.UsersCanRegister = true; + registrationSettings.UsersAreModerated = false; + registrationSettings.UsersMustValidateEmail = false; + + _session.Flush(); + + var result = _controller.Register("bar", "bar@baz.com", "66554321", "66554321"); + + Assert.That(result, Is.TypeOf()); + Assert.That(((RedirectResult)result).Url, Is.EqualTo("~/")); + } + + + [Test] + public void RegisteredUserShouldBeModerated() { + + var registrationSettings = _container.Resolve().GetContext().CurrentSite.As(); + registrationSettings.UsersCanRegister = true; + registrationSettings.UsersAreModerated = true; + + _session.Flush(); + + var result = _controller.Register("bar", "bar@baz.com", "66554321", "66554321"); + + Assert.That(result, Is.TypeOf()); + Assert.That(((RedirectToRouteResult)result).RouteValues["action"], Is.EqualTo("RegistrationPending")); + Assert.That(_channel.Messages.Count, Is.EqualTo(0)); + } + + [Test] + public void SuperAdminShouldReceiveAMessageOnUserRegistration() { + + var registrationSettings = _container.Resolve().GetContext().CurrentSite.As(); + registrationSettings.UsersCanRegister = true; + registrationSettings.UsersAreModerated = true; + registrationSettings.NotifyModeration = true; + + _container.Resolve().GetContext().CurrentSite.As().SuperUser = "admin"; + _session.Flush(); + + var result = _controller.Register("bar", "bar@baz.com", "66554321", "66554321"); + _session.Flush(); + + var user = _container.Resolve().GetUser("bar"); + + Assert.That(result, Is.TypeOf()); + Assert.That(((RedirectToRouteResult)result).RouteValues["action"], Is.EqualTo("RegistrationPending")); + Assert.That(_channel.Messages.Count, Is.EqualTo(1)); + Assert.That(user, Is.Not.Null); + Assert.That(user.UserName, Is.EqualTo("bar")); + Assert.That(user.As(), Is.Not.Null); + Assert.That(user.As().EmailStatus, Is.EqualTo(UserStatus.Approved)); + Assert.That(user.As().RegistrationStatus, Is.EqualTo(UserStatus.Pending)); + } + + [Test] + public void InvalidLostPasswordRequestShouldNotResultInAnError() { + var registrationSettings = _container.Resolve().GetContext().CurrentSite.As(); + registrationSettings.UsersCanRegister = true; + _session.Flush(); + + _controller.Register("bar", "bar@baz.com", "66554321", "66554321"); + + _controller.Url = new UrlHelper(new RequestContext(new HttpContextStub(), new RouteData())); + + var result = _controller.LostPassword("foo"); + + Assert.That(result, Is.TypeOf()); + Assert.That(((RedirectToRouteResult)result).RouteValues["action"], Is.EqualTo("LogOn")); + Assert.That(_channel.Messages.Count, Is.EqualTo(0)); + } + + [Test] + public void ResetPasswordLinkShouldBeSent() { + var registrationSettings = _container.Resolve().GetContext().CurrentSite.As(); + registrationSettings.UsersCanRegister = true; + _session.Flush(); + + _controller.Register("bar", "bar@baz.com", "66554321", "66554321"); + _session.Flush(); + + _controller.Url = new UrlHelper(new RequestContext(new HttpContextStub(), new RouteData())); + var result = _controller.LostPassword("bar"); + Assert.That(result, Is.TypeOf()); + + Assert.That(((RedirectToRouteResult)result).RouteValues["action"], Is.EqualTo("LogOn")); + Assert.That(_channel.Messages.Count, Is.EqualTo(1)); + } + + [Test] + [Ignore("To be implemented")] + public void ChallengeEmailShouldUnlockAccount() { + } + + [Test] + [Ignore("To be implemented")] + public void LostPasswordEmailShouldAuthenticateUser() { + } + + class HttpContextStub : HttpContextBase { + public override HttpRequestBase Request { + get { return new HttpRequestStub(); } + } + + public override IHttpHandler Handler { get; set; } + } + + class HttpRequestStub : HttpRequestBase { + public override bool IsAuthenticated { + get { return false; } + } + + public override NameValueCollection Form { + get { + return new NameValueCollection(); + } + } + + public override Uri Url { + get { + return new Uri("http://orchardproject.net"); + } + } + + public override NameValueCollection Headers { + get { + var nv = new NameValueCollection(); + nv["Host"] = "orchardproject.net"; + return nv; + } + } + } + + } +} diff --git a/src/Orchard.Tests.Modules/Users/Services/UserServiceTests.cs b/src/Orchard.Tests.Modules/Users/Services/UserServiceTests.cs new file mode 100644 index 000000000..efa354cc8 --- /dev/null +++ b/src/Orchard.Tests.Modules/Users/Services/UserServiceTests.cs @@ -0,0 +1,145 @@ +using System; +using System.Web.Security; +using System.Xml.Linq; +using Autofac; +using Moq; +using NHibernate; +using NUnit.Framework; +using Orchard.ContentManagement.MetaData; +using Orchard.ContentManagement.MetaData.Models; +using Orchard.ContentManagement.MetaData.Services; +using Orchard.Core.Settings.Metadata; +using Orchard.Data; +using Orchard.ContentManagement; +using Orchard.ContentManagement.Handlers; +using Orchard.ContentManagement.Records; +using Orchard.DisplayManagement; +using Orchard.DisplayManagement.Descriptors; +using Orchard.DisplayManagement.Implementation; +using Orchard.Environment; +using Orchard.Environment.Extensions; +using Orchard.Messaging.Events; +using Orchard.Messaging.Services; +using Orchard.Security; +using Orchard.Tests.Stubs; +using Orchard.Tests.Utility; +using Orchard.Users.Handlers; +using Orchard.Users.Models; +using Orchard.Users.Services; +using Orchard.Services; +using Orchard.Environment.Configuration; +using Orchard.Tests.Messaging; + +namespace Orchard.Tests.Modules.Users.Services { + [TestFixture] + public class UserServiceTests { + private IMembershipService _membershipService; + private IUserService _userService; + private IClock _clock; + private MessagingChannelStub _channel; + private ISessionFactory _sessionFactory; + private ISession _session; + private IContainer _container; + + + public class TestSessionLocator : ISessionLocator { + private readonly ISession _session; + + public TestSessionLocator(ISession session) { + _session = session; + } + + public ISession For(Type entityType) { + return _session; + } + } + + [TestFixtureSetUp] + public void InitFixture() { + var databaseFileName = System.IO.Path.GetTempFileName(); + _sessionFactory = DataUtility.CreateSessionFactory( + databaseFileName, + typeof(UserPartRecord), + typeof(ContentItemVersionRecord), + typeof(ContentItemRecord), + typeof(ContentTypeRecord)); + } + + [TestFixtureTearDown] + public void TermFixture() { + + } + + [SetUp] + public void Init() { + var builder = new ContainerBuilder(); + //builder.RegisterModule(new ImplicitCollectionSupportModule()); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterInstance(_clock = new StubClock()).As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType(typeof(SettingsFormatter)) + .As(typeof(IMapper)) + .As(typeof(IMapper)); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterAutoMocking(MockBehavior.Loose); + builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)); + builder.RegisterInstance(new Mock().Object); + builder.RegisterType().As(); + builder.RegisterInstance(_channel = new MessagingChannelStub()).As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterInstance(new ShellSettings { Name = "Alpha", RequestUrlHost = "wiki.example.com", RequestUrlPrefix = "~/foo" }); + + _session = _sessionFactory.OpenSession(); + builder.RegisterInstance(new TestSessionLocator(_session)).As(); + _container = builder.Build(); + _membershipService = _container.Resolve(); + _userService = _container.Resolve(); + } + + [Test] + public void NonceShouldBeDecryptable() { + var user = _membershipService.CreateUser(new CreateUserParams("foo", "66554321", "foo@bar.com", "", "", true)); + var nonce = _userService.CreateNonce(user, new TimeSpan(1, 0, 0)); + + Assert.That(nonce, Is.Not.Empty); + + string username; + DateTime validateByUtc; + + var result = _userService.DecryptNonce(nonce, out username, out validateByUtc); + + Assert.That(result, Is.True); + Assert.That(username, Is.EqualTo("foo")); + Assert.That(validateByUtc, Is.GreaterThan(_clock.UtcNow)); + } + + [Test] + public void NonceShouldNotBeUsedOnAnotherTenant() { + var user = _membershipService.CreateUser(new CreateUserParams("foo", "66554321", "foo@bar.com", "", "", true)); + var nonce = _userService.CreateNonce(user, new TimeSpan(1, 0, 0)); + + Assert.That(nonce, Is.Not.Empty); + + string username; + DateTime validateByUtc; + + _container.Resolve().Name = "Beta"; + + var result = _userService.DecryptNonce(nonce, out username, out validateByUtc); + + Assert.That(result, Is.False); + Assert.That(username, Is.EqualTo("foo")); + Assert.That(validateByUtc, Is.GreaterThan(_clock.UtcNow)); + } + + } +} diff --git a/src/Orchard.Tests/Messaging/MessagingChannelStub.cs b/src/Orchard.Tests/Messaging/MessagingChannelStub.cs new file mode 100644 index 000000000..cbde7b4aa --- /dev/null +++ b/src/Orchard.Tests/Messaging/MessagingChannelStub.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Orchard.Messaging.Services; +using Orchard.Messaging.Models; + +namespace Orchard.Tests.Messaging { + public class MessagingChannelStub : IMessagingChannel { + public List Messages { get; private set; } + + public MessagingChannelStub() { + Messages = new List(); + } + + #region IMessagingChannel Members + + public void SendMessage(MessageContext message) { + Messages.Add(message); + } + + public IEnumerable GetAvailableServices() { + yield return "email"; + } + + #endregion + } +} diff --git a/src/Orchard.Tests/Orchard.Framework.Tests.csproj b/src/Orchard.Tests/Orchard.Framework.Tests.csproj index 208366c7d..5815c2241 100644 --- a/src/Orchard.Tests/Orchard.Framework.Tests.csproj +++ b/src/Orchard.Tests/Orchard.Framework.Tests.csproj @@ -239,6 +239,7 @@ + diff --git a/src/Orchard.Web/Modules/Orchard.Users/Controllers/AccountController.cs b/src/Orchard.Web/Modules/Orchard.Users/Controllers/AccountController.cs index 4c199a891..3251b9105 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Controllers/AccountController.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Controllers/AccountController.cs @@ -120,8 +120,7 @@ namespace Orchard.Users.Controllers { if (user != null) { if ( user.As().EmailStatus == UserStatus.Pending ) { - string challengeToken = _userService.GetNonce(user.As()); - _userService.SendChallengeEmail(user.As(), Url.AbsoluteAction(() => Url.Action("ChallengeEmail", "Account", new { Area = "Orchard.Users", token = challengeToken }))); + _userService.SendChallengeEmail(user.As(), nonce => Url.AbsoluteAction(() => Url.Action("ChallengeEmail", "Account", new { Area = "Orchard.Users", nonce = nonce }))); return RedirectToAction("ChallengeEmailSent"); } diff --git a/src/Orchard.Web/Modules/Orchard.Users/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.Users/Controllers/AdminController.cs index 96f8d3c05..bdc1f9adc 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Controllers/AdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Controllers/AdminController.cs @@ -197,8 +197,7 @@ namespace Orchard.Users.Controllers { var user = Services.ContentManager.Get(id); if ( user != null ) { - string challengeToken = _userService.GetNonce(user.As()); - _userService.SendChallengeEmail(user.As(), Url.AbsoluteAction(() => Url.Action("ChallengeEmail", "Account", new {Area = "Orchard.Users", token = challengeToken}))); + _userService.SendChallengeEmail(user.As(), nonce => Url.AbsoluteAction(() => Url.Action("ChallengeEmail", "Account", new {Area = "Orchard.Users", nonce = nonce}))); } Services.Notifier.Information(T("Challenge email sent")); diff --git a/src/Orchard.Web/Modules/Orchard.Users/Services/IUserService.cs b/src/Orchard.Web/Modules/Orchard.Users/Services/IUserService.cs index d6ba66ff4..acd7a211f 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Services/IUserService.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Services/IUserService.cs @@ -5,12 +5,13 @@ namespace Orchard.Users.Services { string VerifyUserUnicity(string userName, string email); string VerifyUserUnicity(int id, string userName, string email); - void SendChallengeEmail(IUser user, string url); + void SendChallengeEmail(IUser user, Func createUrl); IUser ValidateChallenge(string challengeToken); bool SendLostPasswordEmail(string usernameOrEmail, Func createUrl); IUser ValidateLostPassword(string nonce); - string GetNonce(IUser user); + string CreateNonce(IUser user, TimeSpan delay); + bool DecryptNonce(string challengeToken, out string username, out DateTime validateByUtc); } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Users/Services/MembershipService.cs b/src/Orchard.Web/Modules/Orchard.Users/Services/MembershipService.cs index 7b1593207..13fe5349a 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Services/MembershipService.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Services/MembershipService.cs @@ -19,7 +19,6 @@ using Orchard.Services; namespace Orchard.Users.Services { [UsedImplicitly] public class MembershipService : IMembershipService { - private static readonly TimeSpan DelayToValidate = new TimeSpan(7, 0, 0, 0); // one week to validate email private readonly IOrchardServices _orchardServices; private readonly IMessageManager _messageManager; private readonly IEnumerable _userEventHandlers; diff --git a/src/Orchard.Web/Modules/Orchard.Users/Services/UserService.cs b/src/Orchard.Web/Modules/Orchard.Users/Services/UserService.cs index f0e2a6c22..ae4951d35 100644 --- a/src/Orchard.Web/Modules/Orchard.Users/Services/UserService.cs +++ b/src/Orchard.Web/Modules/Orchard.Users/Services/UserService.cs @@ -12,22 +12,26 @@ using System.Globalization; using System.Text; using System.Web.Security; using Orchard.Messaging.Services; +using Orchard.Environment.Configuration; namespace Orchard.Users.Services { [UsedImplicitly] public class UserService : IUserService { private static readonly TimeSpan DelayToValidate = new TimeSpan(7, 0, 0, 0); // one week to validate email + private static readonly TimeSpan DelayToResetPassword = new TimeSpan(1, 0, 0, 0); // 24 hours to validate email private readonly IContentManager _contentManager; private readonly IMembershipService _membershipService; private readonly IClock _clock; private readonly IMessageManager _messageManager; + private readonly ShellSettings _shellSettings; - public UserService(IContentManager contentManager, IMembershipService membershipService, IClock clock, IMessageManager messageManager) { + public UserService(IContentManager contentManager, IMembershipService membershipService, IClock clock, IMessageManager messageManager, ShellSettings shellSettings) { _contentManager = contentManager; _membershipService = membershipService; _clock = clock; _messageManager = messageManager; + _shellSettings = shellSettings; Logger = NullLogger.Instance; } @@ -63,23 +67,25 @@ namespace Orchard.Users.Services { return null; } - public string GetNonce(IUser user) { - var challengeToken = new XElement("Token", new XAttribute("username", user.UserName), new XAttribute("validate-by-utc", _clock.UtcNow.Add(DelayToValidate).ToString(CultureInfo.InvariantCulture))).ToString(); - var data = Encoding.UTF8.GetBytes(challengeToken); + public string CreateNonce(IUser user, TimeSpan delay) { + // the tenant's name is added to the token to prevent cross-tenant requests + var challengeToken = new XElement("n", new XAttribute("s", _shellSettings.Name), new XAttribute("un", user.UserName), new XAttribute("utc", _clock.UtcNow.ToUniversalTime().Add(delay).ToString(CultureInfo.InvariantCulture))).ToString(); + var data = Encoding.Unicode.GetBytes(challengeToken); return MachineKey.Encode(data, MachineKeyProtection.All); } - private bool DecryptNonce(string challengeToken, out string username, out DateTime validateByUtc) { + public bool DecryptNonce(string challengeToken, out string username, out DateTime validateByUtc) { username = null; validateByUtc = _clock.UtcNow; try { var data = MachineKey.Decode(challengeToken, MachineKeyProtection.All); - var xml = Encoding.UTF8.GetString(data); + var xml = Encoding.Unicode.GetString(data); var element = XElement.Parse(xml); - username = element.Attribute("username").Value; - validateByUtc = DateTime.Parse(element.Attribute("validate-by-utc").Value, CultureInfo.InvariantCulture); - return true; + var tenant = element.Attribute("s").Value; + username = element.Attribute("un").Value; + validateByUtc = DateTime.Parse(element.Attribute("utc").Value, CultureInfo.InvariantCulture); + return String.Equals(_shellSettings.Name, tenant, StringComparison.Ordinal) && _clock.UtcNow <= validateByUtc; } catch { return false; @@ -107,7 +113,9 @@ namespace Orchard.Users.Services { return user; } - public void SendChallengeEmail(IUser user, string url) { + public void SendChallengeEmail(IUser user, Func createUrl) { + string nonce = CreateNonce(user, DelayToValidate); + string url = createUrl(nonce); _messageManager.Send(user.ContentItem.Record, MessageTypes.Validation, "email", new Dictionary { { "ChallengeUrl", url } }); } @@ -116,7 +124,7 @@ namespace Orchard.Users.Services { var user = _contentManager.Query().Where(u => u.NormalizedUserName == lowerName || u.Email == lowerName).List().FirstOrDefault(); if (user != null) { - string nonce = GetNonce(user); + string nonce = CreateNonce(user, DelayToResetPassword); string url = createUrl(nonce); _messageManager.Send(user.ContentItem.Record, MessageTypes.LostPassword, "email", new Dictionary { { "LostPasswordUrl", url } }); diff --git a/src/Orchard/Messaging/Services/DefaultMessageManager.cs b/src/Orchard/Messaging/Services/DefaultMessageManager.cs index 9b21acaed..3f315d67d 100644 --- a/src/Orchard/Messaging/Services/DefaultMessageManager.cs +++ b/src/Orchard/Messaging/Services/DefaultMessageManager.cs @@ -18,6 +18,7 @@ namespace Orchard.Messaging.Services { IEnumerable channels) { _messageEventHandler = messageEventHandler; _channels = channels; + Logger = NullLogger.Instance; } public void Send(ContentItemRecord recipient, string type, string service, Dictionary properties = null) {