1
0
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:
Uncled1023 2016-06-02 15:23:17 -07:00
parent 1821cf75ad
commit d6613e587f
17 changed files with 613 additions and 16 deletions

View File

@ -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" });
}
}
}

View 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; }
}
}

View 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; }
}
}

View File

@ -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;

View File

@ -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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</button>' + html.error + '</div>');
}
}
});
return false;
});
});

View File

@ -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 }
);

View File

@ -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

View 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 RecoveryEmailVerificationViewModel : ViewModelBase
{
public bool Success { get; set; }
}
}

View 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 ResetPasswordVerificationViewModel : ViewModelBase
{
public bool Success { get; set; }
}
}

View 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; }
}
}

View File

@ -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; }

View 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>

View File

@ -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>

View File

@ -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">

View File

@ -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>

View File

@ -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");

View File

@ -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" />