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) {