diff --git a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Base/System/SystemSettings.cs b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Base/System/SystemSettings.cs index 7af8b4f3..d713ba88 100644 --- a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Base/System/SystemSettings.cs +++ b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Base/System/SystemSettings.cs @@ -47,6 +47,14 @@ namespace WebsitePanel.EnterpriseServer public const string PACKAGE_DISPLAY_SETTINGS = "PackageDisplaySettings"; public const string RDS_SETTINGS = "RdsSettings"; public const string WEBDAV_PORTAL_SETTINGS = "WebdavPortalSettings"; + public const string TWILIO_SETTINGS = "TwilioSettings"; + + + //Keys + public const string TWILIO_ACCOUNTSID_KEY = "TwilioAccountSid"; + public const string TWILIO_AUTHTOKEN_KEY = "TwilioAuthToken"; + public const string TWILIO_PHONEFROM_KEY = "TwilioPhoneFrom"; + public const string WEBDAV_PASSWORD_RESET_ENABLED_KEY = "WebdavPswResetEnabled"; // key to access to wpi main & custom feed in wpi settings diff --git a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Client/OrganizationProxy.cs b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Client/OrganizationProxy.cs index fbafa0aa..05baf043 100644 --- a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Client/OrganizationProxy.cs +++ b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Client/OrganizationProxy.cs @@ -2648,21 +2648,23 @@ namespace WebsitePanel.EnterpriseServer.HostedSolution { /// [System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/SendResetUserPasswordEmail", RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)] - public void SendResetUserPasswordEmail(int itemId, int accountId, string reason, string mailTo) { + public void SendResetUserPasswordEmail(int itemId, int accountId, string reason, string mailTo, bool finalStep) { this.Invoke("SendResetUserPasswordEmail", new object[] { itemId, accountId, reason, - mailTo}); + mailTo, + finalStep}); } /// - public System.IAsyncResult BeginSendResetUserPasswordEmail(int itemId, int accountId, string reason, string mailTo, System.AsyncCallback callback, object asyncState) { + public System.IAsyncResult BeginSendResetUserPasswordEmail(int itemId, int accountId, string reason, string mailTo, bool finalStep, System.AsyncCallback callback, object asyncState) { return this.BeginInvoke("SendResetUserPasswordEmail", new object[] { itemId, accountId, reason, - mailTo}, callback, asyncState); + mailTo, + finalStep}, callback, asyncState); } /// @@ -2671,12 +2673,12 @@ namespace WebsitePanel.EnterpriseServer.HostedSolution { } /// - public void SendResetUserPasswordEmailAsync(int itemId, int accountId, string reason, string mailTo) { - this.SendResetUserPasswordEmailAsync(itemId, accountId, reason, mailTo, null); + public void SendResetUserPasswordEmailAsync(int itemId, int accountId, string reason, string mailTo, bool finalStep) { + this.SendResetUserPasswordEmailAsync(itemId, accountId, reason, mailTo, finalStep, null); } /// - public void SendResetUserPasswordEmailAsync(int itemId, int accountId, string reason, string mailTo, object userState) { + public void SendResetUserPasswordEmailAsync(int itemId, int accountId, string reason, string mailTo, bool finalStep, object userState) { if ((this.SendResetUserPasswordEmailOperationCompleted == null)) { this.SendResetUserPasswordEmailOperationCompleted = new System.Threading.SendOrPostCallback(this.OnSendResetUserPasswordEmailOperationCompleted); } @@ -2684,7 +2686,8 @@ namespace WebsitePanel.EnterpriseServer.HostedSolution { itemId, accountId, reason, - mailTo}, this.SendResetUserPasswordEmailOperationCompleted, userState); + mailTo, + finalStep}, this.SendResetUserPasswordEmailOperationCompleted, userState); } private void OnSendResetUserPasswordEmailOperationCompleted(object arg) { diff --git a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Code/ExchangeServer/ExchangeServerController.cs b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Code/ExchangeServer/ExchangeServerController.cs index 47a17473..12ea3e0a 100644 --- a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Code/ExchangeServer/ExchangeServerController.cs +++ b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Code/ExchangeServer/ExchangeServerController.cs @@ -2616,7 +2616,9 @@ namespace WebsitePanel.EnterpriseServer items["AccountDomain"] = account.PrimaryEmailAddress.Substring(account.PrimaryEmailAddress.IndexOf("@") + 1); items["DefaultDomain"] = org.DefaultDomain; - var passwordResetUrl = OrganizationController.GenerateUserPasswordResetLink(account.ItemId, account.AccountId); + Guid token; + + var passwordResetUrl = OrganizationController.GenerateUserPasswordResetLink(account.ItemId, account.AccountId,out token); if (!string.IsNullOrEmpty(passwordResetUrl)) { items["PswResetUrl"] = passwordResetUrl; diff --git a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Code/HostedSolution/OrganizationController.cs b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Code/HostedSolution/OrganizationController.cs index 5fdbd9d7..2b3165f1 100644 --- a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Code/HostedSolution/OrganizationController.cs +++ b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer.Code/HostedSolution/OrganizationController.cs @@ -1596,9 +1596,12 @@ namespace WebsitePanel.EnterpriseServer string body = settings["PasswordResetLinkSmsBody"]; + var pincode = GeneratePincode(); + Guid token; + var items = new Hashtable(); - items["passwordResetLink"] = GenerateUserPasswordResetLink(user.ItemId, user.AccountId); + items["passwordResetLink"] = GenerateUserPasswordResetLink(user.ItemId, user.AccountId, out token, pincode); body = PackageController.EvaluateTemplate(body, items); @@ -1614,6 +1617,8 @@ namespace WebsitePanel.EnterpriseServer { throw new Exception(response.RestException.Message); } + + SetAccessTokenResponse(token, pincode); } catch (Exception ex) { @@ -1702,19 +1707,25 @@ namespace WebsitePanel.EnterpriseServer return random.Next(10000, 99999).ToString(CultureInfo.InvariantCulture); } - private static TwilioRestClient GetTwilioRestClient() - { - string accountSid = ConfigurationManager.AppSettings["WebsitePanel.Twilio.AccountSid"]; - string authToken = ConfigurationManager.AppSettings["WebsitePanel.Twilio.AuthorizationToken"]; - - return new TwilioRestClient(accountSid, authToken); - } - private static SMSMessage SendSms(string to, string body) { - var client = GetTwilioRestClient(); + SystemSettings settings = SystemController.GetSystemSettingsInternal(SystemSettings.TWILIO_SETTINGS, false); - string phoneFrom = ConfigurationManager.AppSettings["WebsitePanel.Twilio.PhoneFrom"]; + if (settings == null) + { + throw new Exception("Twilio settings are not set"); + } + + string accountSid = settings.GetValueOrDefault(SystemSettings.TWILIO_ACCOUNTSID_KEY, string.Empty); + string authToken = settings.GetValueOrDefault(SystemSettings.TWILIO_AUTHTOKEN_KEY, string.Empty); + string phoneFrom = settings.GetValueOrDefault(SystemSettings.TWILIO_PHONEFROM_KEY, string.Empty); + + if (string.IsNullOrEmpty(accountSid) || string.IsNullOrEmpty(accountSid) || string.IsNullOrEmpty(accountSid)) + { + throw new Exception("Twilio settings are not set (System settings)"); + } + + var client = new TwilioRestClient(accountSid, authToken); return client.SendSmsMessage(phoneFrom, to, body); } @@ -1726,7 +1737,8 @@ namespace WebsitePanel.EnterpriseServer /// User Id /// Reason why reset email is sent. /// Optional, if null accountID user PrimaryEmailAddress will be used - public static void SendResetUserPasswordEmail(int itemId, int accountId, string reason, string mailTo = null) + /// Url direction + public static void SendResetUserPasswordEmail(int itemId, int accountId, string reason, string mailTo, bool finalStep) { // load organization Organization org = GetOrganization(itemId); @@ -1753,16 +1765,16 @@ namespace WebsitePanel.EnterpriseServer var logoUrl = generalSettings != null ? generalSettings.OrganizationLogoUrl : string.Empty; - SendUserPasswordEmail(owner, user, reason, mailTo, logoUrl, UserSettings.USER_PASSWORD_RESET_LETTER, "USER_PASSWORD_RESET_LETTER"); + SendUserPasswordEmail(owner, user, reason, mailTo, logoUrl, UserSettings.USER_PASSWORD_RESET_LETTER, "USER_PASSWORD_RESET_LETTER", finalStep); } public static void SendUserExpirationPasswordEmail(UserInfo owner, OrganizationUser user, string reason, string mailTo, string logoUrl) { - SendUserPasswordEmail(owner, user, reason, user.PrimaryEmailAddress, logoUrl, UserSettings.USER_PASSWORD_EXPIRATION_LETTER, "USER_PASSWORD_EXPIRATION_LETTER"); + SendUserPasswordEmail(owner, user, reason, user.PrimaryEmailAddress, logoUrl, UserSettings.USER_PASSWORD_EXPIRATION_LETTER, "USER_PASSWORD_EXPIRATION_LETTER", false); } - public static void SendUserPasswordEmail(UserInfo owner, OrganizationUser user, string reason, string mailTo, string logoUrl, string settingsName, string taskName) + public static void SendUserPasswordEmail(UserInfo owner, OrganizationUser user, string reason, string mailTo, string logoUrl, string settingsName, string taskName, bool finalStep) { UserSettings settings = UserController.GetUserSettings(owner.UserId, settingsName); @@ -1789,14 +1801,23 @@ namespace WebsitePanel.EnterpriseServer priority = (MailPriority) Enum.Parse(typeof (MailPriority), settings["Priority"], true); } + Guid token; + + string pincode = finalStep ? GeneratePincode() : null; + Hashtable items = new Hashtable(); items["user"] = user; items["logoUrl"] = logoUrl; - items["passwordResetLink"] = GenerateUserPasswordResetLink(user.ItemId, user.AccountId); + items["passwordResetLink"] = GenerateUserPasswordResetLink(user.ItemId, user.AccountId, out token, pincode); body = PackageController.EvaluateTemplate(body, items); + if (finalStep) + { + SetAccessTokenResponse(token, pincode); + } + TaskManager.Write("Organization ID : " + user.ItemId); TaskManager.Write("Account : " + user.DisplayName); TaskManager.Write("Reason : " + reason); @@ -1837,14 +1858,15 @@ namespace WebsitePanel.EnterpriseServer return SystemController.GetSystemSettingsInternal(SystemSettings.WEBDAV_PORTAL_SETTINGS, false); } - public static string GenerateUserPasswordResetLink(int itemId, int accountId) + public static string GenerateUserPasswordResetLink(int itemId, int accountId, out Guid tokenGuid, string pincode = null) { - string passwordResetUrlFormat = "account/password-reset/step-2"; + string passwordResetUrlFormat = string.IsNullOrEmpty(pincode) ? "account/password-reset/step-2" : "account/password-reset/step-final"; var settings = GetWebDavSystemSettings(); if (settings == null || !settings.GetValueOrDefault(SystemSettings.WEBDAV_PASSWORD_RESET_ENABLED_KEY, false) ||!settings.Contains("WebdavPortalUrl")) { + tokenGuid = new Guid(); return string.Empty; } @@ -1852,8 +1874,17 @@ namespace WebsitePanel.EnterpriseServer var token = CreateAccessToken(itemId, accountId, AccessTokenTypes.PasswrodReset); - return webdavPortalUrl.Append(passwordResetUrlFormat) - .Append(token.AccessTokenGuid.ToString("n")).ToString(); + tokenGuid = token.AccessTokenGuid; + + var resultUrl = webdavPortalUrl.Append(passwordResetUrlFormat) + .Append(token.AccessTokenGuid.ToString("n")); + + if (string.IsNullOrEmpty(pincode) == false) + { + resultUrl = resultUrl.Append(pincode); + } + + return resultUrl.ToString(); } private static AccessToken CreateAccessToken(int itemId, int accountId, AccessTokenTypes type) diff --git a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer/Web.config b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer/Web.config index fc4d3348..3a721fdd 100644 --- a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer/Web.config +++ b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer/Web.config @@ -20,10 +20,6 @@ - - - - diff --git a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer/esOrganizations.asmx.cs b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer/esOrganizations.asmx.cs index 9ce4e663..ce6aa6f4 100644 --- a/WebsitePanel/Sources/WebsitePanel.EnterpriseServer/esOrganizations.asmx.cs +++ b/WebsitePanel/Sources/WebsitePanel.EnterpriseServer/esOrganizations.asmx.cs @@ -356,9 +356,9 @@ namespace WebsitePanel.EnterpriseServer } [WebMethod] - public void SendResetUserPasswordEmail(int itemId, int accountId, string reason, string mailTo = null) + public void SendResetUserPasswordEmail(int itemId, int accountId, string reason, string mailTo, bool finalStep) { - OrganizationController.SendResetUserPasswordEmail(itemId, accountId, reason, mailTo); + OrganizationController.SendResetUserPasswordEmail(itemId, accountId, reason, mailTo, finalStep); } #endregion diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/App_Start/RouteConfig.cs b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/App_Start/RouteConfig.cs index 98ab21f4..d26285c0 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/App_Start/RouteConfig.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/App_Start/RouteConfig.cs @@ -38,14 +38,14 @@ namespace WebsitePanel.WebDavPortal routes.MapRoute( name: AccountRouteNames.PasswordResetSendSms, - url: "account/password-reset/step-final/{token}", + url: "account/password-reset/send-new-sms/{token}", defaults: new { controller = "Account", action = "PasswordResetSendSms" } ); routes.MapRoute( name: AccountRouteNames.PasswordResetFinalStep, - url: "account/password-reset/send-new-sms/{token}", - defaults: new { controller = "Account", action = "PasswordResetFinalStep" } + url: "account/password-reset/step-final/{token}/{pincode}", + defaults: new { controller = "Account", action = "PasswordResetFinalStep", pincode = UrlParameter.Optional } ); routes.MapRoute( diff --git a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/AccountController.cs b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/AccountController.cs index 38b298db..776aef2e 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/AccountController.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebDavPortal/Controllers/AccountController.cs @@ -5,6 +5,7 @@ using System.Web.Mvc; using System.Web.Routing; using AutoMapper; using log4net; +using Microsoft.Web.Services3.Addressing; using WebsitePanel.Providers.HostedSolution; using WebsitePanel.WebDav.Core.Config; using WebsitePanel.WebDav.Core.Security.Authentication; @@ -186,7 +187,7 @@ namespace WebsitePanel.WebDavPortal.Controllers return View(model); } - WspContext.Services.Organizations.SendResetUserPasswordEmail(exchangeAccount.ItemId, exchangeAccount.AccountId, Resources.Messages.PasswordResetUserReason, exchangeAccount.PrimaryEmailAddress); + WspContext.Services.Organizations.SendResetUserPasswordEmail(exchangeAccount.ItemId, exchangeAccount.AccountId, Resources.Messages.PasswordResetUserReason, exchangeAccount.PrimaryEmailAddress, false); return View("PasswordResetEmailSent"); } @@ -257,15 +258,16 @@ namespace WebsitePanel.WebDavPortal.Controllers [HttpGet] [AllowAnonymous] - public ActionResult PasswordResetFinalStep(Guid token) + public ActionResult PasswordResetFinalStep(Guid token, string pincode) { - var smsResponse = Session[WebDavAppConfigManager.Instance.SessionKeys.PasswordResetSmsKey] as string; + var result = VerifyPincode(token, pincode); - if (_smsAuthService.VerifyResponse(token, smsResponse) == false) + if (result != null) { - return RedirectToRoute(AccountRouteNames.PasswordResetSms); + return result; } + var model = new PasswordEditor(); return View(model); @@ -273,20 +275,18 @@ namespace WebsitePanel.WebDavPortal.Controllers [HttpPost] [AllowAnonymous] - public ActionResult PasswordResetFinalStep(Guid token, PasswordEditor model) + public ActionResult PasswordResetFinalStep(Guid token, string pincode, PasswordEditor model) { if (!ModelState.IsValid) { return View(model); } - var smsResponse = Session[WebDavAppConfigManager.Instance.SessionKeys.PasswordResetSmsKey] as string; + var result = VerifyPincode(token, pincode); - if (_smsAuthService.VerifyResponse(token, smsResponse) == false) + if (result != null) { - AddMessage(MessageType.Error, Resources.Messages.IncorrectSmsResponse); - - return RedirectToRoute(AccountRouteNames.PasswordResetSms); + return result; } var tokenEntity = WspContext.Services.Organizations.GetPasswordresetAccessToken(token); @@ -333,6 +333,34 @@ namespace WebsitePanel.WebDavPortal.Controllers #region Helpers + /// + /// Verify pincode, if it's absent - verifying pincode from session + /// + /// Password reset token + /// Pincode to verify if session pincode is absent + private ActionResult VerifyPincode(Guid token, string pincode) + { + var smsResponse = Session[WebDavAppConfigManager.Instance.SessionKeys.PasswordResetSmsKey] as string; + + if (string.IsNullOrEmpty(pincode) == false) + { + smsResponse = pincode; + } + + if (_smsAuthService.VerifyResponse(token, smsResponse) == false) + { + AddMessage(MessageType.Error, Resources.Messages.IncorrectSmsResponse); + + return RedirectToRoute(AccountRouteNames.PasswordResetSms); + } + + var tokenEntity = WspContext.Services.Organizations.GetPasswordresetAccessToken(token); + + Session[WebDavAppConfigManager.Instance.SessionKeys.ItemId] = tokenEntity.ItemId; + + return null; + } + private UserProfile GetUserProfileModel(int itemId, int accountId) { var user = WspContext.Services.Organizations.GetUserGeneralSettingsWithExtraData(itemId, accountId); diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/App_LocalResources/SystemSettings.ascx.resx b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/App_LocalResources/SystemSettings.ascx.resx index 32b84ee8..050f293a 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/App_LocalResources/SystemSettings.ascx.resx +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/App_LocalResources/SystemSettings.ascx.resx @@ -180,4 +180,16 @@ Enable password reset: + + AccountSID + + + AuthToken + + + Phone From + + + Twilio + \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ExchangeServer/OrganizationUserResetPassword.ascx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ExchangeServer/OrganizationUserResetPassword.ascx.cs index 6fc86dd2..8b5a315d 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ExchangeServer/OrganizationUserResetPassword.ascx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ExchangeServer/OrganizationUserResetPassword.ascx.cs @@ -39,7 +39,7 @@ namespace WebsitePanel.Portal.ExchangeServer if (rbtnEmail.Checked) { - ES.Services.Organizations.SendResetUserPasswordEmail(PanelRequest.ItemID,PanelRequest.AccountID, txtReason.Text, txtEmailAddress.Text); + ES.Services.Organizations.SendResetUserPasswordEmail(PanelRequest.ItemID,PanelRequest.AccountID, txtReason.Text, txtEmailAddress.Text, true); } else { diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SystemSettings.ascx b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SystemSettings.ascx index a32574c2..83198973 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SystemSettings.ascx +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/SystemSettings.ascx @@ -98,6 +98,24 @@ + + + + + + + + + + + + + +
+
+
+
+
protected global::System.Web.UI.WebControls.TextBox txtWebdavPortalUrl; + /// + /// TwilioSettings control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::WebsitePanel.Portal.CollapsiblePanel TwilioSettings; + + /// + /// PanelTwilioSettings control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Panel PanelTwilioSettings; + + /// + /// lblAccountSid control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Localize lblAccountSid; + + /// + /// txtAccountSid control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox txtAccountSid; + + /// + /// lblAuthToken control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Localize lblAuthToken; + + /// + /// txtAuthToken control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox txtAuthToken; + + /// + /// lblPhoneFrom control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Localize lblPhoneFrom; + + /// + /// txtPhoneFrom control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox txtPhoneFrom; + /// /// btnSaveSettings control. ///