Skip to content

Instantly share code, notes, and snippets.

@hawjeh
Created May 3, 2023 07:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hawjeh/c8daf9367fa84c4777cca341d6750a42 to your computer and use it in GitHub Desktop.
Save hawjeh/c8daf9367fa84c4777cca341d6750a42 to your computer and use it in GitHub Desktop.
Sitefinity v13 Custom Reset Password
<!DOCTYPE html>
<html ng-app="app" ng-controller="LayoutCtrl">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>{siteName}</title>
<style>
img {
width: 100px;
height: 100px;
}
form {
margin: 0 auto;
box-sizing: border-box;
border: 1px solid #ddd;
padding: 40px 50px;
width: 430px;
min-height: 430px;
background-color: #fff;
}
.sfSubmitBtns {
display: flex;
flex-direction: row;
}
.sfLoginBtn {
width: 45% !important;
margin-right: 10px;
}
.sfForgotPasswordBtn {
background-color: #1a4283 !important;
}
</style>
{stylesheets}
<script src="content/app/webfont-loader-top.js"></script>
</head>
<body ng-cloak>
<div class="sf-authentication-screen">
<h1 class="sf-logo"></h1>
<form name="form" method="post" action="{{model.fullLoginUrl}}" id="loginForm"
class="sf-login-form -sf-content-centered -sf-center-text">
<anti-forgery-token token="model.antiForgery"></anti-forgery-token>
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAMAAABHPGVmAAAAflBMVEXc7PXd7PXd7fXe7fXe7fbf7vbh7/bh7/fi7/fj8Pfk8Pfl8fjm8fjm8vjn8vjo8vjo8/np8/nq9Pnr9Pns9frt9fru9vrv9vrw9/vx+Pvy+Pvz+fz0+fz1+vz2+vz2+v34+/35+/36/P76/f77/f78/f78/v79/v7+//////+lNPEBAAABIElEQVR4Ae3V2w6aUBSE4dkCYj2gICoHQVRQ5v1fsNpoI0gTa9e+aLK+F/iTuRkopZRSSimlPuP6vmu5EB95c4wtdpYNH5olLNnxxQ5WbNmxhQUBewKIcxv2NK7tsawMZi58czGQNeOAmf215PfKOSCHrJIDSsgqOKCArJQDUshac8AasnwO8CGs4psK0iK+iSDNnNhzMhA3Z88cFiTsSGCD2fPF3sAKk/G3zMCWsOEvTQiLRqvscMhWI/zHvAV6Fh5kTbOWqYMXTso2m0KME1W8q0ODBxPWvKsiBxL85MqnczzGzTg+8+ma+PhHJijZVRdFza4yMPiet6n5kXrj4TuTvOXH2nwi97h/ttaIRjRiKeL9+CselFJKKaWUUj0/AWGBopm+4+PNAAAAAElFTkSuQmCC"
alt="Default profile photo" />
<h2>{{model.resources["loginHeader"]}}</h2>
<ul class="-sf-clearfix">
<li ng-show="model.showProviders">
<label for="membershipProvider">{{model.resources["provider"]}}</label>
<select id="membershipProvider" name="membershipProvider" ng-model="model.selectedProvider"
ng-options="p.friendlyName for p in model.providers track by p.name"></select>
</li>
<li>
<input required name="username" autofocus id="username" type="text" class="sf-input"
placeholder='{{model.resources["emailOrUsername"]}}' ng-model="model.username">
</li>
<li>
<input required id="password" name="password" type="password" class="sf-input"
placeholder='{{model.resources["password"]}}' ng-model="model.password" autocomplete="off">
</li>
<li class="sf-login-form__remember" ng-show="model.allowRememberMe">
<input type="checkbox" id="rememberMe" name="rememberMe" ng-model="model.rememberMe" value="true">
<label for="rememberMe">{{model.resources["rememberMe"]}}</label>
</li>
</ul>
<div class="-sf-txt-failure" ng-show="model.errorMessage">
{{model.errorMessage}}
</div>
<div class="sfSubmitBtns">
<button class="sf-button -sf-action sfLoginBtn" id="loginButton">
<strong>
Login
</strong>
</button>
<!--TODO: Change URL below -->
<a id="forgotPassowrd" class="sf-button -sf-action sfForgotPasswordBtn" href="/ForgotPassword">
<strong>Forgot Password?</strong>
</a>
</div>
<!--TODO: Uncheck below for external providers -->
<!-- <div ng-show="model.externalProviders">
<h2>{{model.resources["useYorAccount"]}}</h2>
<ul class="sf-mt--20">
<li ng-repeat="provider in model.externalProviders">
<a class="sf-button {{provider.linkCssClass}}" href="{{provider.href}}">{{provider.text}}</a>
</li>
</ul>
</div> -->
</form>
</div>
<script id='modelJson' type='application/json'>
{model}
</script>
{scripts}
</body>
</html>
protected void Application_Start()
{
AntiForgeryConfig.SuppressXFrameOptionsHeader = true;
}
protected void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext.Current.Response.AddHeader("X-Frame-Options", "SAMEORIGIN");
}
using System;
using System.Linq;
using System.Net.Mail;
using System.Web.Mvc;
using Telerik.Sitefinity;
using Telerik.Sitefinity.Data;
using Telerik.Sitefinity.DynamicModules;
using Telerik.Sitefinity.Model;
using Telerik.Sitefinity.Mvc;
using Telerik.Sitefinity.Security;
using Telerik.Sitefinity.Services;
using Telerik.Sitefinity.Services.Notifications;
using Telerik.Sitefinity.Utilities.TypeConverters;
using Telerik.Sitefinity.Versioning;
namespace SitefinityWebApp.Mvc.Controllers
{
[ControllerToolboxItem(
Title = "ForgotPassword",
Name = "ForgotPassword",
SectionName = "Sparks Custom App")]
public class ForgotPasswordController : Controller
{
private readonly Type forgotPasswordRecordType = TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.Sparks.ForgotPasswordRecord");
public ForgotPasswordController() { }
public ActionResult Index()
{
return View("Index", new ForgotPasswordModel());
}
public ActionResult ChangePassword(string id)
{
if (string.IsNullOrEmpty(id))
{
return View("Index", new ForgotPasswordModel());
}
var model = new ForgotPasswordModel()
{
RequestId = id
};
return View("ChangePassword", model);
}
[HttpPost]
public ActionResult RequestResetPasswordForm(ForgotPasswordModel model)
{
var email = model.Email;
var userManager = UserManager.GetManager();
var user = userManager.GetUsers().FirstOrDefault(o => o.Email.ToLower() == email.ToLower());
if (user != null)
{
var requestId = Guid.NewGuid().ToString();
var transactionName = "ResetPasswordRequest";
var versionManager = VersionManager.GetManager(null, transactionName);
var dynamicModuleManager = DynamicModuleManager.GetManager(string.Empty, transactionName);
dynamicModuleManager.Provider.SuppressSecurityChecks = true;
var item = dynamicModuleManager.CreateDataItem(forgotPasswordRecordType);
item.SetString("RequestId", requestId);
item.SetString("UserId", user.Id.ToString());
item.SetWorkflowStatus(dynamicModuleManager.Provider.ApplicationName, "Draft");
versionManager.CreateVersion(item, false);
TransactionManager.CommitTransaction(transactionName);
dynamicModuleManager.Provider.SuppressSecurityChecks = false;
var msg = string.Format("Please visit {0} to update password.", "https://domain/forgotpassword/changepassword?id=" + requestId); // To update url
SendEmail(email, msg);
model.Error = "Reset password link sent to email";
}
else
{
model.Error = "Invalid user";
}
return View("Index", model);
}
[HttpPost]
public ActionResult RequestChangePasswordForm(ForgotPasswordModel model)
{
var transactionName = "ChangePasswordRequest";
var dynamicModuleManager = DynamicModuleManager.GetManager(string.Empty, transactionName);
var item = dynamicModuleManager.GetDataItems(forgotPasswordRecordType).ToList().FirstOrDefault(x => x.GetValue<Lstring>("RequestId").ToString().ToLower() == model.RequestId.ToLower());
if (item != null)
{
if (model.NewPassword1.ToLower() == model.NewPassword2.ToLower())
{
var userManager = UserManager.GetManager();
var users = userManager.GetUsers().ToList();
var user = users.FirstOrDefault(o => o.Id.ToString().ToLower() == item.GetValue<Lstring>("UserId").ToString().ToLower());
if (user != null)
{
userManager.Provider.SuppressSecurityChecks = true;
// Reset password
var temp = user.ResetPassword();
userManager.SaveChanges();
// Change password
user.ChangePassword(temp, model.NewPassword1);
userManager.SaveChanges();
userManager.Provider.SuppressSecurityChecks = false;
dynamicModuleManager.Provider.SuppressSecurityChecks = true;
dynamicModuleManager.DeleteDataItem(item);
TransactionManager.CommitTransaction(transactionName);
dynamicModuleManager.Provider.SuppressSecurityChecks = false;
model.Error = "Password updated";
}
}
else
{
model.Error = "Password is not matched";
}
}
else
{
model.Error = "Invalid request";
}
return View("ChangePassword", model);
}
private void SendEmail(string email, string msg)
{
var context = new ServiceContext(string.Empty, "ResetPassword");
var profile = SystemManager.GetNotificationService().GetDefaultSenderProfile(context, "smtp");
var sender = profile.CustomProperties["defaultSenderEmailAddress"];
var message = new MailMessage
{
From = new MailAddress(sender, sender),
Subject = "Reset Password",
IsBodyHtml = true,
Body = msg,
};
message.To.Add(email);
var smtpServer = new SmtpClient
{
Host = profile.CustomProperties["host"],
Port = Convert.ToInt32(profile.CustomProperties["port"]),
EnableSsl = Convert.ToBoolean(profile.CustomProperties["useSSL"]),
DeliveryMethod = SmtpDeliveryMethod.Network,
Credentials = new System.Net.NetworkCredential(profile.CustomProperties["username"], profile.CustomProperties["password"])
};
smtpServer.Send(message);
}
protected override void HandleUnknownAction(string actionName)
{
this.ActionInvoker.InvokeAction(this.ControllerContext, "Index");
}
}
public class ForgotPasswordModel
{
public string RequestId { get; set; }
public string Email { get; set; }
public string NewPassword1 { get; set; }
public string NewPassword2 { get; set; }
public string Error { get; set; }
}
}
@model SitefinityWebApp.Mvc.Controllers.ForgotPasswordModel
@using Telerik.Sitefinity.UI.MVC;
<style>
.sfChangePasswordForm {
display: flex;
justify-content: center;
margin-top: 100px;
line-height: 2rem;
}
form[name="ChangePasswordForm"] {
min-width: 420px;
border: 1px solid lightgray;
padding: 2rem;
}
</style>
<div class="sfChangePasswordForm">
@using (Html.BeginFormSitefinity("RequestChangePasswordForm", "ChangePasswordForm", FormMethod.Post))
{
@Html.AntiForgeryToken()
<h2>Change Password</h2>
<div class="form-group d-none">
<label for="RequestId">Request Id</label>
<input name="RequestId" autofocus id="RequestId" type="text" class="form-control" value="@Model.RequestId" />
</div>
<div class="form-group mt-3 mb-3">
<label for="NewPassword1">New Password</label>
<input required name="NewPassword1" autofocus id="NewPassword1" type="password" class="form-control" />
</div>
<div class="form-group mt-3 mb-3">
<label for="NewPassword2">New Password</label>
<input required name="NewPassword2" autofocus id="NewPassword2" type="password" class="form-control" />
</div>
<button class="btn btn-primary" type="submit">Submit</button>
<a class="btn btn-secondary" href="/Sitefinity">
<strong>Back to Login</strong>
</a>
if (!string.IsNullOrEmpty(Model.Error))
{
<div class="alert alert-danger mt-4" role="alert" aria-live="assertive">
@Model.Error
</div>
}
}
</div>
@model SitefinityWebApp.Mvc.Controllers.ForgotPasswordModel
@using Telerik.Sitefinity.UI.MVC;
<style>
.sfResetPasswordForm {
display: flex;
justify-content: center;
margin-top: 100px;
line-height: 2rem;
}
form[name="ResetPasswordForm"] {
min-width: 420px;
border: 1px solid lightgray;
padding: 2rem;
}
</style>
<div class="sfResetPasswordForm">
@using (Html.BeginFormSitefinity("RequestResetPasswordForm", "ResetPasswordForm", FormMethod.Post))
{
@Html.AntiForgeryToken()
<h2>Reset Password</h2>
<div class="form-group mt-3 mb-3">
<label for="email">Email</label>
<input required name="email" autofocus id="email" type="text" class="form-control" />
</div>
<button class="btn btn-primary" type="submit">Submit</button>
<a class="btn btn-secondary" href="/Sitefinity">
<strong>Back to Login</strong>
</a>
if (!string.IsNullOrEmpty(Model.Error))
{
<div class="alert alert-danger mt-4" role="alert" aria-live="assertive">
@Model.Error
</div>
}
}
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment