Files
Orchard/src/Orchard.Web/Modules/Orchard.Users/Controllers/AccountController.cs

444 lines
19 KiB
C#
Raw Normal View History

using System;
using System.Text.RegularExpressions;
using System.Diagnostics.CodeAnalysis;
2010-04-16 13:33:45 -07:00
using Orchard.Localization;
using System.Web.Mvc;
using System.Web.Security;
using Orchard.Logging;
using Orchard.Mvc;
using Orchard.Mvc.Extensions;
using Orchard.Security;
using Orchard.Themes;
using Orchard.Users.Services;
using Orchard.ContentManagement;
using Orchard.Users.Models;
using Orchard.UI.Notify;
using Orchard.Users.Events;
using Orchard.Utility.Extensions;
namespace Orchard.Users.Controllers {
[HandleError, Themed]
public class AccountController : Controller {
private readonly IAuthenticationService _authenticationService;
private readonly IMembershipService _membershipService;
private readonly IUserService _userService;
private readonly IOrchardServices _orchardServices;
private readonly IUserEventHandler _userEventHandler;
public AccountController(
IAuthenticationService authenticationService,
IMembershipService membershipService,
IUserService userService,
IOrchardServices orchardServices,
IUserEventHandler userEventHandler) {
_authenticationService = authenticationService;
_membershipService = membershipService;
_userService = userService;
_orchardServices = orchardServices;
_userEventHandler = userEventHandler;
Logger = NullLogger.Instance;
2010-04-16 13:33:45 -07:00
T = NullLocalizer.Instance;
}
public ILogger Logger { get; set; }
2010-04-16 13:33:45 -07:00
public Localizer T { get; set; }
[AlwaysAccessible]
public ActionResult AccessDenied() {
var returnUrl = Request.QueryString["ReturnUrl"];
var currentUser = _authenticationService.GetAuthenticatedUser();
if (currentUser == null) {
Logger.Information("Access denied to anonymous request on {0}", returnUrl);
var shape = _orchardServices.New.LogOn().Title(T("Access Denied").Text);
return new ShapeResult(this, shape);
}
//TODO: (erikpo) Add a setting for whether or not to log access denieds since these can fill up a database pretty fast from bots on a high traffic site
//Suggestion: Could instead use the new AccessDenined IUserEventHandler method and let modules decide if they want to log this event?
Logger.Information("Access denied to user #{0} '{1}' on {2}", currentUser.Id, currentUser.UserName, returnUrl);
_userEventHandler.AccessDenied(currentUser);
return View();
}
[AlwaysAccessible]
public ActionResult LogOn(string returnUrl) {
if (_authenticationService.GetAuthenticatedUser() != null)
return this.RedirectLocal(returnUrl);
var shape = _orchardServices.New.LogOn().Title(T("Log On").Text);
return new ShapeResult(this, shape);
}
[HttpPost]
[AlwaysAccessible]
[ValidateInput(false)]
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings",
Justification = "Needs to take same parameter type as Controller.Redirect()")]
public ActionResult LogOn(string userNameOrEmail, string password, string returnUrl, bool rememberMe = false) {
_userEventHandler.LoggingIn(userNameOrEmail, password);
2010-04-16 13:14:45 -07:00
var user = ValidateLogOn(userNameOrEmail, password);
if (!ModelState.IsValid) {
var shape = _orchardServices.New.LogOn().Title(T("Log On").Text);
return new ShapeResult(this, shape);
}
_authenticationService.SignIn(user, rememberMe);
_userEventHandler.LoggedIn(user);
return this.RedirectLocal(returnUrl);
}
public ActionResult LogOff(string returnUrl) {
2011-03-21 15:37:20 -07:00
var wasLoggedInUser = _authenticationService.GetAuthenticatedUser();
_authenticationService.SignOut();
if (wasLoggedInUser != null)
_userEventHandler.LoggedOut(wasLoggedInUser);
return this.RedirectLocal(returnUrl);
}
int MinPasswordLength {
get {
return _membershipService.GetSettings().MinRequiredPasswordLength;
}
}
[AlwaysAccessible]
public ActionResult Register() {
// ensure users can register
var registrationSettings = _orchardServices.WorkContext.CurrentSite.As<RegistrationSettingsPart>();
if ( !registrationSettings.UsersCanRegister ) {
return HttpNotFound();
}
ViewData["PasswordLength"] = MinPasswordLength;
var shape = _orchardServices.New.Register();
return new ShapeResult(this, shape);
}
[HttpPost]
[AlwaysAccessible]
[ValidateInput(false)]
2013-11-19 10:06:34 +01:00
public ActionResult Register(string userName, string email, string password, string confirmPassword, string returnUrl = null) {
// ensure users can register
var registrationSettings = _orchardServices.WorkContext.CurrentSite.As<RegistrationSettingsPart>();
if ( !registrationSettings.UsersCanRegister ) {
return HttpNotFound();
}
ViewData["PasswordLength"] = MinPasswordLength;
if (ValidateRegistration(userName, email, password, confirmPassword)) {
// Attempt to register the user
// No need to report this to IUserEventHandler because _membershipService does that for us
var user = _membershipService.CreateUser(new CreateUserParams(userName, password, email, null, null, false));
if (user != null) {
if ( user.As<UserPart>().EmailStatus == UserStatus.Pending ) {
2013-10-28 17:43:32 -07:00
var siteUrl = _orchardServices.WorkContext.CurrentSite.BaseUrl;
if(String.IsNullOrWhiteSpace(siteUrl)) {
siteUrl = HttpContext.Request.ToRootUrlString();
}
_userService.SendChallengeEmail(user.As<UserPart>(), nonce => Url.MakeAbsolute(Url.Action("ChallengeEmail", "Account", new {Area = "Orchard.Users", nonce = nonce}), siteUrl));
_userEventHandler.SentChallengeEmail(user);
return RedirectToAction("ChallengeEmailSent");
}
if (user.As<UserPart>().RegistrationStatus == UserStatus.Pending) {
return RedirectToAction("RegistrationPending");
}
_authenticationService.SignIn(user, false /* createPersistentCookie */);
2013-11-19 10:06:34 +01:00
return this.RedirectLocal(returnUrl);
}
ModelState.AddModelError("_FORM", T(ErrorCodeToString(/*createStatus*/MembershipCreateStatus.ProviderError)));
}
// If we got this far, something failed, redisplay form
var shape = _orchardServices.New.Register();
return new ShapeResult(this, shape);
}
[AlwaysAccessible]
public ActionResult RequestLostPassword() {
// ensure users can request lost password
var registrationSettings = _orchardServices.WorkContext.CurrentSite.As<RegistrationSettingsPart>();
if ( !registrationSettings.EnableLostPassword ) {
return HttpNotFound();
}
return View();
}
[HttpPost]
[AlwaysAccessible]
public ActionResult RequestLostPassword(string username) {
// ensure users can request lost password
var registrationSettings = _orchardServices.WorkContext.CurrentSite.As<RegistrationSettingsPart>();
if ( !registrationSettings.EnableLostPassword ) {
return HttpNotFound();
}
if(String.IsNullOrWhiteSpace(username)){
ModelState.AddModelError("userNameOrEmail", T("Invalid username or E-mail."));
return View();
}
2013-10-28 17:43:32 -07:00
var siteUrl = _orchardServices.WorkContext.CurrentSite.BaseUrl;
2012-02-28 16:48:26 -08:00
if (String.IsNullOrWhiteSpace(siteUrl)) {
siteUrl = HttpContext.Request.ToRootUrlString();
}
_userService.SendLostPasswordEmail(username, nonce => Url.MakeAbsolute(Url.Action("LostPassword", "Account", new { Area = "Orchard.Users", nonce = nonce }), siteUrl));
_orchardServices.Notifier.Information(T("Check your e-mail for the confirmation link."));
2013-11-19 10:06:34 +01:00
return RedirectToAction("LogOn");
}
[Authorize]
[AlwaysAccessible]
public ActionResult ChangePassword() {
ViewData["PasswordLength"] = MinPasswordLength;
return View();
}
[Authorize]
[HttpPost]
[AlwaysAccessible]
[ValidateInput(false)]
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
Justification = "Exceptions result in password not being changed.")]
public ActionResult ChangePassword(string currentPassword, string newPassword, string confirmPassword) {
ViewData["PasswordLength"] = MinPasswordLength;
if ( !ValidateChangePassword(currentPassword, newPassword, confirmPassword) ) {
return View();
}
try {
var validated = _membershipService.ValidateUser(User.Identity.Name, currentPassword);
if ( validated != null ) {
_membershipService.SetPassword(validated, newPassword);
_userEventHandler.ChangedPassword(validated);
return RedirectToAction("ChangePasswordSuccess");
}
ModelState.AddModelError("_FORM",
T("The current password is incorrect or the new password is invalid."));
return ChangePassword();
} catch {
ModelState.AddModelError("_FORM", T("The current password is incorrect or the new password is invalid."));
return ChangePassword();
}
}
[AlwaysAccessible]
public ActionResult LostPassword(string nonce) {
if ( _userService.ValidateLostPassword(nonce) == null ) {
return RedirectToAction("LogOn");
}
ViewData["PasswordLength"] = MinPasswordLength;
return View();
}
[HttpPost]
[AlwaysAccessible]
[ValidateInput(false)]
public ActionResult LostPassword(string nonce, string newPassword, string confirmPassword) {
IUser user;
if ( (user = _userService.ValidateLostPassword(nonce)) == null ) {
return Redirect("~/");
}
ViewData["PasswordLength"] = MinPasswordLength;
if (newPassword == null || newPassword.Length < MinPasswordLength) {
ModelState.AddModelError("newPassword", T("You must specify a new password of {0} or more characters.", MinPasswordLength));
}
if (!String.Equals(newPassword, confirmPassword, StringComparison.Ordinal)) {
ModelState.AddModelError("_FORM", T("The new password and confirmation password do not match."));
}
if (!ModelState.IsValid) {
return View();
}
_membershipService.SetPassword(user, newPassword);
_userEventHandler.ChangedPassword(user);
return RedirectToAction("ChangePasswordSuccess");
}
[AlwaysAccessible]
public ActionResult ChangePasswordSuccess() {
return View();
}
public ActionResult RegistrationPending() {
return View();
}
public ActionResult ChallengeEmailSent() {
2010-09-02 21:29:10 -07:00
return View();
}
public ActionResult ChallengeEmailSuccess() {
2010-09-02 21:29:10 -07:00
return View();
}
public ActionResult ChallengeEmailFail() {
2010-09-02 21:29:10 -07:00
return View();
}
public ActionResult ChallengeEmail(string nonce) {
var user = _userService.ValidateChallenge(nonce);
if ( user != null ) {
_userEventHandler.ConfirmedEmail(user);
return RedirectToAction("ChallengeEmailSuccess");
}
return RedirectToAction("ChallengeEmailFail");
}
#region Validation Methods
private bool ValidateChangePassword(string currentPassword, string newPassword, string confirmPassword) {
if ( String.IsNullOrEmpty(currentPassword) ) {
ModelState.AddModelError("currentPassword", T("You must specify a current password."));
}
if ( newPassword == null || newPassword.Length < MinPasswordLength ) {
ModelState.AddModelError("newPassword", T("You must specify a new password of {0} or more characters.", MinPasswordLength));
}
if ( !String.Equals(newPassword, confirmPassword, StringComparison.Ordinal) ) {
ModelState.AddModelError("_FORM", T("The new password and confirmation password do not match."));
}
return ModelState.IsValid;
}
2010-04-16 13:14:45 -07:00
private IUser ValidateLogOn(string userNameOrEmail, string password) {
2010-05-13 16:31:40 -07:00
bool validate = true;
2010-04-16 13:14:45 -07:00
if (String.IsNullOrEmpty(userNameOrEmail)) {
2010-04-16 13:33:45 -07:00
ModelState.AddModelError("userNameOrEmail", T("You must specify a username or e-mail."));
2010-05-13 16:31:40 -07:00
validate = false;
}
if (String.IsNullOrEmpty(password)) {
2010-04-16 13:33:45 -07:00
ModelState.AddModelError("password", T("You must specify a password."));
2010-05-13 16:31:40 -07:00
validate = false;
}
2010-05-13 16:31:40 -07:00
if (!validate)
return null;
2010-04-16 13:14:45 -07:00
var user = _membershipService.ValidateUser(userNameOrEmail, password);
if (user == null) {
_userEventHandler.LogInFailed(userNameOrEmail, password);
2010-04-16 13:33:45 -07:00
ModelState.AddModelError("_FORM", T("The username or e-mail or password provided is incorrect."));
}
return user;
}
private bool ValidateRegistration(string userName, string email, string password, string confirmPassword) {
2010-05-13 16:31:40 -07:00
bool validate = true;
if (String.IsNullOrEmpty(userName)) {
2010-04-16 13:33:45 -07:00
ModelState.AddModelError("username", T("You must specify a username."));
2010-05-13 16:31:40 -07:00
validate = false;
}
2014-03-20 15:28:05 -07:00
else {
if (userName.Length >= 255) {
ModelState.AddModelError("username", T("The username you provided is too long."));
validate = false;
}
}
if (String.IsNullOrEmpty(email)) {
2010-04-16 13:33:45 -07:00
ModelState.AddModelError("email", T("You must specify an email address."));
2010-05-13 16:31:40 -07:00
validate = false;
}
2014-03-20 15:28:05 -07:00
else if (email.Length >= 255) {
ModelState.AddModelError("email", T("The email address you provided is too long."));
validate = false;
}
else if (!Regex.IsMatch(email, UserPart.EmailPattern, RegexOptions.IgnoreCase)) {
// http://haacked.com/archive/2007/08/21/i-knew-how-to-validate-an-email-address-until-i.aspx
ModelState.AddModelError("email", T("You must specify a valid email address."));
validate = false;
}
2010-05-13 16:31:40 -07:00
if (!validate)
return false;
if (!_userService.VerifyUserUnicity(userName, email)) {
ModelState.AddModelError("userExists", T("User with that username and/or email already exists."));
}
if (password == null || password.Length < MinPasswordLength) {
2010-04-16 13:33:45 -07:00
ModelState.AddModelError("password", T("You must specify a password of {0} or more characters.", MinPasswordLength));
}
if (!String.Equals(password, confirmPassword, StringComparison.Ordinal)) {
2010-04-16 13:33:45 -07:00
ModelState.AddModelError("_FORM", T("The new password and confirmation password do not match."));
}
return ModelState.IsValid;
}
private static string ErrorCodeToString(MembershipCreateStatus createStatus) {
// See http://msdn.microsoft.com/en-us/library/system.web.security.membershipcreatestatus.aspx for
// a full list of status codes.
switch (createStatus) {
case MembershipCreateStatus.DuplicateUserName:
return "Username already exists. Please enter a different user name.";
case MembershipCreateStatus.DuplicateEmail:
return "A username for that e-mail address already exists. Please enter a different e-mail address.";
case MembershipCreateStatus.InvalidPassword:
return "The password provided is invalid. Please enter a valid password value.";
case MembershipCreateStatus.InvalidEmail:
return "The e-mail address provided is invalid. Please check the value and try again.";
case MembershipCreateStatus.InvalidAnswer:
return "The password retrieval answer provided is invalid. Please check the value and try again.";
case MembershipCreateStatus.InvalidQuestion:
return "The password retrieval question provided is invalid. Please check the value and try again.";
case MembershipCreateStatus.InvalidUserName:
return "The user name provided is invalid. Please check the value and try again.";
case MembershipCreateStatus.ProviderError:
return
"The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
case MembershipCreateStatus.UserRejected:
return
"The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
default:
return
"An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
}
}
#endregion
}
}