mirror of
https://git.teknik.io/Teknikode/Teknik.git
synced 2023-08-02 14:16:22 +02:00
Added password resets using recovery emails.
This commit is contained in:
parent
1821cf75ad
commit
d6613e587f
@ -104,6 +104,8 @@ namespace Teknik.Areas.Users.Controllers
|
||||
|
||||
model.UserID = user.UserId;
|
||||
model.Username = user.Username;
|
||||
model.RecoveryEmail = user.RecoveryEmail;
|
||||
model.RecoveryVerified = user.RecoveryVerified;
|
||||
|
||||
model.UserSettings = user.UserSettings;
|
||||
model.BlogSettings = user.BlogSettings;
|
||||
@ -250,13 +252,7 @@ namespace Teknik.Areas.Users.Controllers
|
||||
newUser.JoinDate = DateTime.Now;
|
||||
newUser.Username = model.Username;
|
||||
if (!string.IsNullOrEmpty(model.RecoveryEmail))
|
||||
{
|
||||
string recoveryCode = Teknik.Utility.RandomString(24);
|
||||
string resetUrl = Url.SubRouteUrl("user", "User.ResetPassword", new { Username = model.Username });
|
||||
string verifyUrl = Url.SubRouteUrl("user", "User.VerifyRecoveryEmail", new { Username = model.Username, Code = recoveryCode });
|
||||
//UserHelper.SendRecoveryEmailVerification(Config, model.Username, model.RecoveryEmail, resetUrl, verifyUrl); Not yet :)
|
||||
//newUser.RecoveryEmail = model.RecoveryEmail;
|
||||
}
|
||||
newUser.RecoveryEmail = model.RecoveryEmail;
|
||||
newUser.UserSettings = new UserSettings();
|
||||
if (!string.IsNullOrEmpty(model.PublicKey))
|
||||
newUser.UserSettings.PGPSignature = model.PublicKey;
|
||||
@ -264,6 +260,15 @@ namespace Teknik.Areas.Users.Controllers
|
||||
newUser.UploadSettings = new UploadSettings();
|
||||
|
||||
UserHelper.AddAccount(db, Config, newUser, model.Password);
|
||||
|
||||
// If they have a recovery email, let's send a verification
|
||||
if (!string.IsNullOrEmpty(model.RecoveryEmail))
|
||||
{
|
||||
string verifyCode = UserHelper.CreateRecoveryEmailVerification(db, Config, newUser);
|
||||
string resetUrl = Url.SubRouteUrl("user", "User.ResetPassword", new { Username = model.Username });
|
||||
string verifyUrl = Url.SubRouteUrl("user", "User.VerifyRecoveryEmail", new { Code = verifyCode });
|
||||
UserHelper.SendRecoveryEmailVerification(Config, model.Username, model.RecoveryEmail, resetUrl, verifyUrl);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@ -277,7 +282,7 @@ namespace Teknik.Areas.Users.Controllers
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult Edit(string curPass, string newPass, string newPassConfirm, string pgpPublicKey, string website, string quote, string about, string blogTitle, string blogDesc, bool saveKey, bool serverSideEncrypt)
|
||||
public ActionResult Edit(string curPass, string newPass, string newPassConfirm, string pgpPublicKey, string recoveryEmail, string website, string quote, string about, string blogTitle, string blogDesc, bool saveKey, bool serverSideEncrypt)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
@ -311,6 +316,14 @@ namespace Teknik.Areas.Users.Controllers
|
||||
}
|
||||
user.UserSettings.PGPSignature = pgpPublicKey;
|
||||
|
||||
bool newRecovery = false;
|
||||
if (recoveryEmail != user.RecoveryEmail)
|
||||
{
|
||||
newRecovery = true;
|
||||
user.RecoveryEmail = recoveryEmail;
|
||||
user.RecoveryVerified = false;
|
||||
}
|
||||
|
||||
user.UserSettings.Website = website;
|
||||
user.UserSettings.Quote = quote;
|
||||
user.UserSettings.About = about;
|
||||
@ -321,6 +334,15 @@ namespace Teknik.Areas.Users.Controllers
|
||||
user.UploadSettings.SaveKey = saveKey;
|
||||
user.UploadSettings.ServerSideEncrypt = serverSideEncrypt;
|
||||
UserHelper.EditAccount(db, Config, user, changePass, newPass);
|
||||
|
||||
// If they have a recovery email, let's send a verification
|
||||
if (!string.IsNullOrEmpty(recoveryEmail) && newRecovery)
|
||||
{
|
||||
string verifyCode = UserHelper.CreateRecoveryEmailVerification(db, Config, user);
|
||||
string resetUrl = Url.SubRouteUrl("user", "User.ResetPassword", new { Username = user.Username });
|
||||
string verifyUrl = Url.SubRouteUrl("user", "User.VerifyRecoveryEmail", new { Code = verifyCode });
|
||||
UserHelper.SendRecoveryEmailVerification(Config, user.Username, user.RecoveryEmail, resetUrl, verifyUrl);
|
||||
}
|
||||
return Json(new { result = true });
|
||||
}
|
||||
return Json(new { error = "User does not exist" });
|
||||
@ -356,5 +378,152 @@ namespace Teknik.Areas.Users.Controllers
|
||||
}
|
||||
return Json(new { error = "Unable to delete user" });
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public ActionResult VerifyRecoveryEmail(string code)
|
||||
{
|
||||
bool verified = true;
|
||||
if (string.IsNullOrEmpty(code))
|
||||
verified &= false;
|
||||
verified &= UserHelper.VerifyRecoveryEmail(db, Config, User.Identity.Name, code);
|
||||
|
||||
RecoveryEmailVerificationViewModel model = new RecoveryEmailVerificationViewModel();
|
||||
model.Success = verified;
|
||||
|
||||
return View("/Areas/User/Views/User/ViewRecoveryEmailVerification.cshtml", model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult ResendVerifyRecoveryEmail()
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
try
|
||||
{
|
||||
User user = UserHelper.GetUser(db, User.Identity.Name);
|
||||
if (user != null)
|
||||
{
|
||||
// If they have a recovery email, let's send a verification
|
||||
if (!string.IsNullOrEmpty(user.RecoveryEmail))
|
||||
{
|
||||
if (!user.RecoveryVerified)
|
||||
{
|
||||
string verifyCode = UserHelper.CreateRecoveryEmailVerification(db, Config, user);
|
||||
string resetUrl = Url.SubRouteUrl("user", "User.ResetPassword", new { Username = user.Username });
|
||||
string verifyUrl = Url.SubRouteUrl("user", "User.VerifyRecoveryEmail", new { Code = verifyCode });
|
||||
UserHelper.SendRecoveryEmailVerification(Config, user.Username, user.RecoveryEmail, resetUrl, verifyUrl);
|
||||
return Json(new { result = true });
|
||||
}
|
||||
return Json(new { error = "The recovery email is already verified" });
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Json(new { error = ex.GetFullMessage(true) });
|
||||
}
|
||||
}
|
||||
return Json(new { error = "Unable to resend verification" });
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[AllowAnonymous]
|
||||
public ActionResult ResetPassword(string username)
|
||||
{
|
||||
ResetPasswordViewModel model = new ResetPasswordViewModel();
|
||||
model.Username = username;
|
||||
|
||||
return View("/Areas/User/Views/User/ResetPassword.cshtml", model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[AllowAnonymous]
|
||||
public ActionResult SendResetPasswordVerification(string username)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
try
|
||||
{
|
||||
User user = UserHelper.GetUser(db, username);
|
||||
if (user != null)
|
||||
{
|
||||
// If they have a recovery email, let's send a verification
|
||||
if (!string.IsNullOrEmpty(user.RecoveryEmail) && user.RecoveryVerified)
|
||||
{
|
||||
string verifyCode = UserHelper.CreateResetPasswordVerification(db, Config, user);
|
||||
string resetUrl = Url.SubRouteUrl("user", "User.VerifyResetPassword", new { Username = user.Username, Code = verifyCode });
|
||||
UserHelper.SendResetPasswordVerification(Config, user.Username, user.RecoveryEmail, resetUrl);
|
||||
return Json(new { result = true });
|
||||
}
|
||||
return Json(new { error = "The username doesn't have a recovery email specified" });
|
||||
}
|
||||
return Json(new { error = "The username is not valid" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Json(new { error = ex.GetFullMessage(true) });
|
||||
}
|
||||
}
|
||||
return Json(new { error = "Unable to send reset link" });
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[AllowAnonymous]
|
||||
public ActionResult VerifyResetPassword(string username, string code)
|
||||
{
|
||||
bool verified = true;
|
||||
if (string.IsNullOrEmpty(code))
|
||||
verified &= false;
|
||||
verified &= UserHelper.VerifyResetPassword(db, Config, username, code);
|
||||
|
||||
if (verified)
|
||||
{
|
||||
// The password reset code is valid, let's log them in
|
||||
User user = UserHelper.GetUser(db, username);
|
||||
user.LastSeen = DateTime.Now;
|
||||
db.Entry(user).State = EntityState.Modified;
|
||||
db.SaveChanges();
|
||||
HttpCookie authcookie = UserHelper.CreateAuthCookie(user.Username, false, Request.Url.Host.GetDomain(), Request.IsLocal);
|
||||
Response.Cookies.Add(authcookie);
|
||||
}
|
||||
|
||||
ResetPasswordVerificationViewModel model = new ResetPasswordVerificationViewModel();
|
||||
model.Success = verified;
|
||||
|
||||
return View("/Areas/User/Views/User/ResetPasswordVerification.cshtml", model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult SetUserPassword(string password, string confirmPassword)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
try
|
||||
{
|
||||
User user = UserHelper.GetUser(db, User.Identity.Name);
|
||||
if (user != null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(password))
|
||||
{
|
||||
return Json(new { error = "Password must not be empty" });
|
||||
}
|
||||
if (password != confirmPassword)
|
||||
{
|
||||
return Json(new { error = "Passwords must match" });
|
||||
}
|
||||
|
||||
UserHelper.EditAccount(db, Config, user, true, password);
|
||||
|
||||
return Json(new { result = true });
|
||||
}
|
||||
return Json(new { error = "User does not exist" });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Json(new { error = ex.GetFullMessage(true) });
|
||||
}
|
||||
}
|
||||
return Json(new { error = "Unable to reset user password" });
|
||||
}
|
||||
}
|
||||
}
|
20
Teknik/Areas/User/Models/RecoveryEmailVerification.cs
Normal file
20
Teknik/Areas/User/Models/RecoveryEmailVerification.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
namespace Teknik.Areas.Users.Models
|
||||
{
|
||||
public class RecoveryEmailVerification
|
||||
{
|
||||
public int RecoveryEmailVerificationId { get; set; }
|
||||
|
||||
public int UserId { get; set; }
|
||||
|
||||
public User User { get; set; }
|
||||
|
||||
public string Code { get; set; }
|
||||
|
||||
public DateTime DateCreated { get; set; }
|
||||
}
|
||||
}
|
20
Teknik/Areas/User/Models/ResetPasswordVerification.cs
Normal file
20
Teknik/Areas/User/Models/ResetPasswordVerification.cs
Normal file
@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
namespace Teknik.Areas.Users.Models
|
||||
{
|
||||
public class ResetPasswordVerification
|
||||
{
|
||||
public int ResetPasswordVerificationId { get; set; }
|
||||
|
||||
public int UserId { get; set; }
|
||||
|
||||
public User User { get; set; }
|
||||
|
||||
public string Code { get; set; }
|
||||
|
||||
public DateTime DateCreated { get; set; }
|
||||
}
|
||||
}
|
@ -16,8 +16,6 @@ namespace Teknik.Areas.Users.Models
|
||||
|
||||
public string RecoveryEmail { get; set; }
|
||||
|
||||
public string RecoveryVerifyCode { get; set; }
|
||||
|
||||
public bool RecoveryVerified { get; set; }
|
||||
|
||||
public bool TransferAccount { get; set; }
|
||||
@ -43,7 +41,6 @@ namespace Teknik.Areas.Users.Models
|
||||
Username = string.Empty;
|
||||
HashedPassword = string.Empty;
|
||||
RecoveryEmail = string.Empty;
|
||||
RecoveryVerifyCode = string.Empty;
|
||||
RecoveryVerified = false;
|
||||
JoinDate = DateTime.Now;
|
||||
LastSeen = DateTime.Now;
|
||||
|
@ -2,6 +2,30 @@
|
||||
$("[name='update_upload_saveKey']").bootstrapSwitch();
|
||||
$("[name='update_upload_serverSideEncrypt']").bootstrapSwitch();
|
||||
|
||||
$('#ResendVerification').click(function () {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: resendVerifyURL,
|
||||
data: {},
|
||||
success: function (html) {
|
||||
if (html.result) {
|
||||
window.location.reload();
|
||||
}
|
||||
else {
|
||||
errorMsg = html;
|
||||
if (html.error) {
|
||||
errorMsg = html.error;
|
||||
if (html.error.message) {
|
||||
errorMsg = html.error.message;
|
||||
}
|
||||
}
|
||||
$("#top_msg").css('display', 'inline', 'important');
|
||||
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' + errorMsg + '</div>');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#delete_account').click(function () {
|
||||
bootbox.confirm("Are you sure you want to delete your account?", function (result) {
|
||||
if (result) {
|
||||
@ -38,6 +62,7 @@
|
||||
password = $("#update_password").val();
|
||||
password_confirm = $("#update_password_confirm").val();
|
||||
update_pgp_public_key = $("#update_pgp_public_key").val();
|
||||
recovery = $("#update_recovery_email").val();
|
||||
website = $("#update_website").val();
|
||||
quote = $("#update_quote").val();
|
||||
about = $("#update_about").val();
|
||||
@ -53,6 +78,7 @@
|
||||
newPass: password,
|
||||
newPassConfirm: password_confirm,
|
||||
pgpPublicKey: update_pgp_public_key,
|
||||
recoveryEmail: recovery,
|
||||
website: website,
|
||||
quote: quote,
|
||||
about: about,
|
||||
@ -77,4 +103,58 @@
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$("#reset_pass_send_submit").click(function () {
|
||||
var form = $('#reset_pass_send');
|
||||
username = $("#reset_username").val();
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: form.attr('action'),
|
||||
data: {
|
||||
username: username
|
||||
},
|
||||
success: function (html) {
|
||||
if (html.result) {
|
||||
$("#top_msg").css('display', 'inline', 'important');
|
||||
$("#top_msg").html('<div class="alert alert-success alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>The Password Reset Link has been sent to your recovery email.</div>');
|
||||
}
|
||||
else {
|
||||
var error = html;
|
||||
if (html.error)
|
||||
error = html.error;
|
||||
$("#top_msg").css('display', 'inline', 'important');
|
||||
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' + html.error + '</div>');
|
||||
}
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$("#setNewPass_submit").click(function () {
|
||||
var form = $('#setNewPass');
|
||||
password = $("#setNewPass_Password").val();
|
||||
confirmPassword = $("#setNewPass_ConfirmPassword").val();
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: form.attr('action'),
|
||||
data: {
|
||||
password: password,
|
||||
confirmPassword: confirmPassword
|
||||
},
|
||||
success: function (html) {
|
||||
if (html.result) {
|
||||
$("#top_msg").css('display', 'inline', 'important');
|
||||
$("#top_msg").html('<div class="alert alert-success alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>Password has successfully been reset.</div>');
|
||||
}
|
||||
else {
|
||||
var error = html;
|
||||
if (html.error)
|
||||
error = html.error;
|
||||
$("#top_msg").css('display', 'inline', 'important');
|
||||
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' + html.error + '</div>');
|
||||
}
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
});
|
@ -58,11 +58,19 @@ namespace Teknik.Areas.Users
|
||||
new { controller = "User", action = "ResetPassword", username = UrlParameter.Optional }, // Parameter defaults
|
||||
new[] { typeof(Controllers.UserController).Namespace }
|
||||
);
|
||||
context.MapSubdomainRoute(
|
||||
"User.VerifyResetPassword", // Route name
|
||||
new List<string>() { "user" }, // Subdomains
|
||||
new List<string>() { config.Host }, // domains
|
||||
"Reset/{username}/{code}", // URL with parameters
|
||||
new { controller = "User", action = "VerifyResetPassword" }, // Parameter defaults
|
||||
new[] { typeof(Controllers.UserController).Namespace }
|
||||
);
|
||||
context.MapSubdomainRoute(
|
||||
"User.VerifyRecoveryEmail", // Route name
|
||||
new List<string>() { "user" }, // Subdomains
|
||||
new List<string>() { config.Host }, // domains
|
||||
"VerifyEmail/{username}/{code}", // URL with parameters
|
||||
"VerifyEmail/{code}", // URL with parameters
|
||||
new { controller = "User", action = "VerifyRecoveryEmail" }, // Parameter defaults
|
||||
new[] { typeof(Controllers.UserController).Namespace }
|
||||
);
|
||||
@ -70,7 +78,7 @@ namespace Teknik.Areas.Users
|
||||
"User.Index", // Route name
|
||||
new List<string>() { "user" }, // Subdomains
|
||||
new List<string>() { config.Host }, // domains
|
||||
"{username}", // URL with parameters
|
||||
"u/{username}", // URL with parameters
|
||||
new { controller = "User", action = "Index", username = UrlParameter.Optional }, // Parameter defaults
|
||||
new[] { typeof(Controllers.UserController).Namespace }
|
||||
);
|
||||
@ -78,7 +86,7 @@ namespace Teknik.Areas.Users
|
||||
"User.PGPKey", // Route name
|
||||
new List<string>() { "user" }, // Subdomains
|
||||
new List<string>() { config.Host }, // domains
|
||||
"{username}/PGP", // URL with parameters
|
||||
"u/{username}/PGP", // URL with parameters
|
||||
new { controller = "User", action = "ViewRawPGP" }, // Parameter defaults
|
||||
new[] { typeof(Controllers.UserController).Namespace }
|
||||
);
|
||||
|
@ -313,6 +313,26 @@ namespace Teknik.Areas.Users.Utility
|
||||
}
|
||||
}
|
||||
|
||||
// Delete Recovery Email Verifications
|
||||
List<RecoveryEmailVerification> verCodes = db.RecoveryEmailVerifications.Include("User").Where(r => r.User.Username == user.Username).ToList();
|
||||
if (verCodes != null)
|
||||
{
|
||||
foreach (RecoveryEmailVerification verCode in verCodes)
|
||||
{
|
||||
db.RecoveryEmailVerifications.Remove(verCode);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete Password Reset Verifications
|
||||
List<ResetPasswordVerification> verPass = db.ResetPasswordVerifications.Include("User").Where(r => r.User.Username == user.Username).ToList();
|
||||
if (verPass != null)
|
||||
{
|
||||
foreach (ResetPasswordVerification ver in verPass)
|
||||
{
|
||||
db.ResetPasswordVerifications.Remove(ver);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete User
|
||||
db.Users.Remove(user);
|
||||
db.SaveChanges();
|
||||
@ -323,6 +343,30 @@ namespace Teknik.Areas.Users.Utility
|
||||
}
|
||||
}
|
||||
|
||||
public static string CreateRecoveryEmailVerification(TeknikEntities db, Config config, User user)
|
||||
{
|
||||
// Check to see if there already is a verification code for the user
|
||||
List<RecoveryEmailVerification> verCodes = db.RecoveryEmailVerifications.Include("User").Where(r => r.User.Username == user.Username).ToList();
|
||||
if (verCodes != null && verCodes.Any())
|
||||
{
|
||||
foreach (RecoveryEmailVerification verCode in verCodes)
|
||||
{
|
||||
db.RecoveryEmailVerifications.Remove(verCode);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new verification code and add it
|
||||
string verifyCode = Teknik.Utility.RandomString(24);
|
||||
RecoveryEmailVerification ver = new RecoveryEmailVerification();
|
||||
ver.UserId = user.UserId;
|
||||
ver.Code = verifyCode;
|
||||
ver.DateCreated = DateTime.Now;
|
||||
db.RecoveryEmailVerifications.Add(ver);
|
||||
db.SaveChanges();
|
||||
|
||||
return verifyCode;
|
||||
}
|
||||
|
||||
public static void SendRecoveryEmailVerification(Config config, string username, string email, string resetUrl, string verifyUrl)
|
||||
{
|
||||
SmtpClient client = new SmtpClient();
|
||||
@ -336,7 +380,9 @@ namespace Teknik.Areas.Users.Utility
|
||||
|
||||
MailMessage mail = new MailMessage(config.NoReplyEmail, email);
|
||||
mail.Subject = "Recovery Email Validation";
|
||||
mail.Body = string.Format(@"Thank you {0} for signing up for Teknik!
|
||||
mail.Body = string.Format(@"Hello {0},
|
||||
|
||||
Welcome to Teknik!
|
||||
|
||||
You are recieving this email because you have specified this email address as your recovery email. In the event that you forget your password, you can visit {1} and request a temporary password reset key be sent to this email. You will then be able to reset and choose a new password.
|
||||
|
||||
@ -352,6 +398,105 @@ Thank you and enjoy!
|
||||
|
||||
client.Send(mail);
|
||||
}
|
||||
|
||||
public static bool VerifyRecoveryEmail(TeknikEntities db, Config config, string username, string code)
|
||||
{
|
||||
User user = GetUser(db, username);
|
||||
RecoveryEmailVerification verCode = db.RecoveryEmailVerifications.Include("User").Where(r => r.User.Username == username && r.Code == code).FirstOrDefault();
|
||||
if (verCode != null)
|
||||
{
|
||||
// We have a match, so clear out the verifications for that user
|
||||
List<RecoveryEmailVerification> verCodes = db.RecoveryEmailVerifications.Include("User").Where(r => r.User.Username == username).ToList();
|
||||
if (verCodes != null && verCodes.Any())
|
||||
{
|
||||
foreach (RecoveryEmailVerification ver in verCodes)
|
||||
{
|
||||
db.RecoveryEmailVerifications.Remove(ver);
|
||||
}
|
||||
}
|
||||
// Update the user
|
||||
user.RecoveryVerified = true;
|
||||
db.Entry(user).State = EntityState.Modified;
|
||||
db.SaveChanges();
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static string CreateResetPasswordVerification(TeknikEntities db, Config config, User user)
|
||||
{
|
||||
// Check to see if there already is a verification code for the user
|
||||
List<ResetPasswordVerification> verCodes = db.ResetPasswordVerifications.Include("User").Where(r => r.User.Username == user.Username).ToList();
|
||||
if (verCodes != null && verCodes.Any())
|
||||
{
|
||||
foreach (ResetPasswordVerification verCode in verCodes)
|
||||
{
|
||||
db.ResetPasswordVerifications.Remove(verCode);
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new verification code and add it
|
||||
string verifyCode = Teknik.Utility.RandomString(24);
|
||||
ResetPasswordVerification ver = new ResetPasswordVerification();
|
||||
ver.UserId = user.UserId;
|
||||
ver.Code = verifyCode;
|
||||
ver.DateCreated = DateTime.Now;
|
||||
db.ResetPasswordVerifications.Add(ver);
|
||||
db.SaveChanges();
|
||||
|
||||
return verifyCode;
|
||||
}
|
||||
|
||||
public static void SendResetPasswordVerification(Config config, string username, string email, string resetUrl)
|
||||
{
|
||||
SmtpClient client = new SmtpClient();
|
||||
client.Host = config.ContactConfig.Host;
|
||||
client.Port = config.ContactConfig.Port;
|
||||
client.EnableSsl = config.ContactConfig.SSL;
|
||||
client.DeliveryMethod = SmtpDeliveryMethod.Network;
|
||||
client.UseDefaultCredentials = true;
|
||||
client.Credentials = new NetworkCredential(config.NoReplyEmail, config.ContactConfig.Password);
|
||||
client.Timeout = 5000;
|
||||
|
||||
MailMessage mail = new MailMessage(config.NoReplyEmail, email);
|
||||
mail.Subject = "Password Reset Request";
|
||||
mail.Body = string.Format(@"Hello {0},
|
||||
|
||||
You are recieving this email because either you or someone has requested a password reset for your account and this email was specified as the recovery email.
|
||||
|
||||
To proceed in resetting your password, please click the following link or paste it into your browser: {1}
|
||||
|
||||
If you recieved this email and you did not reset your password, you can ignore this email and email us at {2} to prevent it occuring again.
|
||||
|
||||
- Teknik Administration", username, resetUrl, config.SupportEmail);
|
||||
mail.BodyEncoding = UTF8Encoding.UTF8;
|
||||
mail.DeliveryNotificationOptions = DeliveryNotificationOptions.Never;
|
||||
|
||||
client.Send(mail);
|
||||
}
|
||||
|
||||
public static bool VerifyResetPassword(TeknikEntities db, Config config, string username, string code)
|
||||
{
|
||||
User user = GetUser(db, username);
|
||||
ResetPasswordVerification verCode = db.ResetPasswordVerifications.Include("User").Where(r => r.User.Username == username && r.Code == code).FirstOrDefault();
|
||||
if (verCode != null)
|
||||
{
|
||||
// We have a match, so clear out the verifications for that user
|
||||
List<ResetPasswordVerification> verCodes = db.ResetPasswordVerifications.Include("User").Where(r => r.User.Username == username).ToList();
|
||||
if (verCodes != null && verCodes.Any())
|
||||
{
|
||||
foreach (ResetPasswordVerification ver in verCodes)
|
||||
{
|
||||
db.ResetPasswordVerifications.Remove(ver);
|
||||
}
|
||||
}
|
||||
db.SaveChanges();
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Email Management
|
||||
|
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using Teknik.ViewModels;
|
||||
|
||||
namespace Teknik.Areas.Users.ViewModels
|
||||
{
|
||||
public class RecoveryEmailVerificationViewModel : ViewModelBase
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using Teknik.ViewModels;
|
||||
|
||||
namespace Teknik.Areas.Users.ViewModels
|
||||
{
|
||||
public class ResetPasswordVerificationViewModel : ViewModelBase
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
}
|
||||
}
|
13
Teknik/Areas/User/ViewModels/ResetPasswordViewModel.cs
Normal file
13
Teknik/Areas/User/ViewModels/ResetPasswordViewModel.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using Teknik.ViewModels;
|
||||
|
||||
namespace Teknik.Areas.Users.ViewModels
|
||||
{
|
||||
public class ResetPasswordViewModel : ViewModelBase
|
||||
{
|
||||
public string Username { get; set; }
|
||||
}
|
||||
}
|
@ -13,6 +13,10 @@ namespace Teknik.Areas.Users.ViewModels
|
||||
|
||||
public string Username { get; set; }
|
||||
|
||||
public string RecoveryEmail { get; set; }
|
||||
|
||||
public bool RecoveryVerified { get; set; }
|
||||
|
||||
public UserSettings UserSettings { get; set; }
|
||||
|
||||
public BlogSettings BlogSettings { get; set; }
|
||||
|
25
Teknik/Areas/User/Views/User/ResetPassword.cshtml
Normal file
25
Teknik/Areas/User/Views/User/ResetPassword.cshtml
Normal file
@ -0,0 +1,25 @@
|
||||
@model Teknik.Areas.Users.ViewModels.ResetPasswordViewModel
|
||||
|
||||
@Scripts.Render("~/bundles/user")
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="text-center">
|
||||
<h1>Reset Password</h1>
|
||||
<div class="col-md-4 col-md-offset-4">
|
||||
<form role="form" id="reset_pass_send" action="@Url.SubRouteUrl("user", "User.Action", new { action = "SendResetPasswordVerification" })" method="post" accept-charset="UTF-8">
|
||||
<div class="form-group">
|
||||
<label for="reset_username">Username</label>
|
||||
<input type="text" class="form-control" id="reset_username" value="@Model.Username" placeholder="Username" name="reset_username" />
|
||||
<span id="helpBlock" class="help-block">Send a temporary link to your recovery email to reset your password.</span>
|
||||
</div>
|
||||
<div class="form-group text-center">
|
||||
<button class="btn btn-primary" id="reset_pass_send_submit" type="submit" name="reset_pass_send_submit">Send Reset Link</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,33 @@
|
||||
@model Teknik.Areas.Users.ViewModels.ResetPasswordVerificationViewModel
|
||||
|
||||
@Scripts.Render("~/bundles/user")
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="text-center">
|
||||
<h1>@(Model.Success ? "Reset Your Password" : "Invalid Password Reset Verification")</h1>
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
@if (Model.Success)
|
||||
{
|
||||
<form role="form" id="setNewPass" action="@Url.SubRouteUrl("user", "User.Action", new { action = "SetUserPassword" })" method="post" accept-charset="UTF-8">
|
||||
<div class="form-group">
|
||||
<input type="password" class="form-control" id="setNewPass_Password" value="" placeholder="Password" name="setNewPass_Password" data-val-required="The Password field is required." data-val="true" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="password" class="form-control" id="setNewPass_ConfirmPassword" value="" placeholder="Confirm Password" name="setNewPass_ConfirmPassword" data-val-required="The Confirm Password field is required." data-val="true" />
|
||||
</div>
|
||||
<div class="form-group text-center">
|
||||
<button class="btn btn-primary" id="setNewPass_submit" type="submit" name="setNewPass_submit">Reset Password</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p>The verification you provided is not valid. Please check the URL and try again.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -6,6 +6,7 @@
|
||||
var homeUrl = '@Url.SubRouteUrl("www", "Home.Index")';
|
||||
var editUserURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "Edit" })';
|
||||
var deleteUserURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "Delete" })';
|
||||
var resendVerifyURL = '@Url.SubRouteUrl("user", "User.Action", new { action = "ResendVerifyRecoveryEmail"})';
|
||||
</script>
|
||||
|
||||
@Styles.Render("~/Content/user")
|
||||
@ -48,7 +49,30 @@
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
<label for="update_pgp_public_key"><h4>Public Key</h4></label>
|
||||
<textarea class="form-control" id="update_pgp_public_key" name="update_pgp_public_key" placeholder="Public Key Here" title="enter your pgp public key" rows="10">@Model.UserSettings.PGPSignature</textarea>
|
||||
<textarea class="form-control" id="update_pgp_public_key" name="update_pgp_public_key" placeholder="Public Key Here" title="enter your pgp public key" rows="15">@Model.UserSettings.PGPSignature</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<div class="row">
|
||||
<div class="form-group col-sm-12">
|
||||
<label for="update_recovery_email"><h4>Recovery Email</h4></label>
|
||||
<input class="form-control" name="update_recovery_email" id="update_recovery_email" placeholder="user@example.com" title="enter a recovery email." type="text" value="@Model.RecoveryEmail" />
|
||||
@if (!string.IsNullOrEmpty(Model.RecoveryEmail))
|
||||
{
|
||||
<p class="form-control-static">
|
||||
@if (Model.RecoveryVerified)
|
||||
{
|
||||
<span class="text-success"><i class="fa fa-check"></i> Verified</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="text-danger"><i class="fa fa-ban"></i> Unverified</span> <small><a href="#" class="text-primary" id="ResendVerification"><i class="fa fa-repeat"></i> Resend</a></small>
|
||||
}
|
||||
</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
@ -0,0 +1,21 @@
|
||||
@model Teknik.Areas.Users.ViewModels.RecoveryEmailVerificationViewModel
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="text-center">
|
||||
<h1>Verification @(Model.Success ? "Successful" : "Invalid")</h1>
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
@if (Model.Success)
|
||||
{
|
||||
<p>Your recovery email has been successfully verified. When you want to reset your pasword, the reset link will be sent to that email.</p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p>The verification you provided is not valid. Please check the URL and try again.</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -19,6 +19,8 @@ namespace Teknik.Models
|
||||
public DbSet<User> Users { get; set; }
|
||||
public DbSet<Group> Groups { get; set; }
|
||||
public DbSet<Role> Roles { get; set; }
|
||||
public DbSet<RecoveryEmailVerification> RecoveryEmailVerifications { get; set; }
|
||||
public DbSet<ResetPasswordVerification> ResetPasswordVerifications { get; set; }
|
||||
// User Settings
|
||||
public DbSet<UserSettings> UserSettings { get; set; }
|
||||
public DbSet<BlogSettings> BlogSettings { get; set; }
|
||||
@ -90,6 +92,8 @@ namespace Teknik.Models
|
||||
modelBuilder.Entity<User>().ToTable("Users");
|
||||
modelBuilder.Entity<Group>().ToTable("Groups");
|
||||
modelBuilder.Entity<Role>().ToTable("Roles");
|
||||
modelBuilder.Entity<RecoveryEmailVerification>().ToTable("RecoveryEmailVerifications");
|
||||
modelBuilder.Entity<ResetPasswordVerification>().ToTable("ResetPasswordVerifications");
|
||||
// User Settings
|
||||
modelBuilder.Entity<UserSettings>().ToTable("Users");
|
||||
modelBuilder.Entity<BlogSettings>().ToTable("Users");
|
||||
|
@ -209,11 +209,16 @@
|
||||
<Compile Include="Areas\Privacy\ViewModels\PrivacyViewModel.cs" />
|
||||
<Compile Include="Areas\User\Controllers\UserController.cs" />
|
||||
<Compile Include="Areas\User\Models\BlogSettings.cs" />
|
||||
<Compile Include="Areas\User\Models\ResetPasswordVerification.cs" />
|
||||
<Compile Include="Areas\User\Models\RecoveryEmailVerification.cs" />
|
||||
<Compile Include="Areas\User\Models\UploadSettings.cs" />
|
||||
<Compile Include="Areas\User\Models\UserSettings.cs" />
|
||||
<Compile Include="Areas\User\UserAreaRegistration.cs" />
|
||||
<Compile Include="Areas\User\Utility\UserHelper.cs" />
|
||||
<Compile Include="Areas\User\ViewModels\ProfileViewModel.cs" />
|
||||
<Compile Include="Areas\User\ViewModels\ResetPasswordVerificationViewModel.cs" />
|
||||
<Compile Include="Areas\User\ViewModels\ResetPasswordViewModel.cs" />
|
||||
<Compile Include="Areas\User\ViewModels\RecoveryEmailVerificationViewModel.cs" />
|
||||
<Compile Include="Areas\User\ViewModels\SettingsViewModel.cs" />
|
||||
<Compile Include="Areas\RSS\Controllers\RSSController.cs" />
|
||||
<Compile Include="Areas\RSS\RSSAreaRegistration.cs" />
|
||||
@ -533,6 +538,9 @@
|
||||
<Content Include="Areas\TOS\Views\TOS\Index.cshtml" />
|
||||
<Content Include="Areas\Vault\Views\web.config" />
|
||||
<Content Include="Areas\Vault\Views\Vault\ViewVault.cshtml" />
|
||||
<Content Include="Areas\User\Views\User\ViewRecoveryEmailVerification.cshtml" />
|
||||
<Content Include="Areas\User\Views\User\ResetPasswordVerification.cshtml" />
|
||||
<Content Include="Areas\User\Views\User\ResetPassword.cshtml" />
|
||||
<None Include="Properties\PublishProfiles\Teknik Dev.pubxml" />
|
||||
<None Include="Properties\PublishProfiles\Teknik Production.pubxml" />
|
||||
<None Include="Scripts\jquery-2.1.4.intellisense.js" />
|
||||
|
Loading…
Reference in New Issue
Block a user