@@ -20,6 +20,9 @@ using System.Windows; | |||
using System.Net; | |||
using Teknik.Areas.Users.Utility; | |||
using Teknik.Filters; | |||
using QRCoder; | |||
using System.Text; | |||
using TwoStepsAuthenticator; | |||
namespace Teknik.Areas.Users.Controllers | |||
{ | |||
@@ -274,7 +277,7 @@ namespace Teknik.Areas.Users.Controllers | |||
} | |||
[HttpPost] | |||
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) | |||
public ActionResult Edit(string curPass, string newPass, string newPassConfirm, string pgpPublicKey, string recoveryEmail, bool twoFactorEnabled, string website, string quote, string about, string blogTitle, string blogDesc, bool saveKey, bool serverSideEncrypt) | |||
{ | |||
if (ModelState.IsValid) | |||
{ | |||
@@ -316,6 +319,14 @@ namespace Teknik.Areas.Users.Controllers | |||
user.SecuritySettings.RecoveryVerified = false; | |||
} | |||
user.SecuritySettings.TwoFactorEnabled = twoFactorEnabled; | |||
string newKey = string.Empty; | |||
if (twoFactorEnabled) | |||
{ | |||
newKey = Authenticator.GenerateKey(); | |||
} | |||
user.SecuritySettings.TwoFactorKey = newKey; | |||
user.UserSettings.Website = website; | |||
user.UserSettings.Quote = quote; | |||
user.UserSettings.About = about; | |||
@@ -517,5 +528,35 @@ namespace Teknik.Areas.Users.Controllers | |||
} | |||
return Json(new { error = "Unable to reset user password" }); | |||
} | |||
[HttpPost] | |||
public ActionResult ConfirmAuthenticatorCode(string code) | |||
{ | |||
User user = UserHelper.GetUser(db, User.Identity.Name); | |||
if (user != null) | |||
{ | |||
if (user.SecuritySettings.TwoFactorEnabled) | |||
{ | |||
string key = user.SecuritySettings.TwoFactorKey; | |||
TimeAuthenticator ta = new TimeAuthenticator(); | |||
bool isValid = false; | |||
return Json(new { result = true }); | |||
} | |||
return Json(new { error = "User does not have Two Factor Authentication enabled" }); | |||
} | |||
return Json(new { error = "User does not exist" }); | |||
} | |||
[HttpPost] | |||
public ActionResult GenerateQrCode(string content) | |||
{ | |||
QRCodeGenerator qrGenerator = new QRCodeGenerator(); | |||
QRCodeData qrCodeData = qrGenerator.CreateQrCode(content, QRCodeGenerator.ECCLevel.Q); | |||
SvgQRCode qrCode = new SvgQRCode(qrCodeData); | |||
string qrCodeImage = qrCode.GetGraphic(20); | |||
return File(Encoding.UTF8.GetBytes(qrCodeImage), "image/svg+xml"); | |||
} | |||
} | |||
} |
@@ -62,6 +62,7 @@ | |||
password = $("#update_password").val(); | |||
password_confirm = $("#update_password_confirm").val(); | |||
update_pgp_public_key = $("#update_pgp_public_key").val(); | |||
update_security_two_factor = $("#update_security_two_factor").val(); | |||
recovery = $("#update_recovery_email").val(); | |||
website = $("#update_website").val(); | |||
quote = $("#update_quote").val(); |
@@ -0,0 +1 @@ | |||
|
@@ -15,6 +15,60 @@ | |||
<div class="container"> | |||
@if (!Model.Error) | |||
{ | |||
<div class="modal fade" id="authenticatorSetup" tabindex="-1" role="dialog" aria-labelledby="authenticatorSetupLabel" aria-hidden="true"> | |||
<div class="modal-dialog"> | |||
<div class="modal-content"> | |||
<div class="modal-header"> | |||
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button> | |||
<h4 class="modal-title" id="authSetupTitleLabel">Set Up a Third Party App to Generate Codes</h4> | |||
</div> | |||
<div class="modal-body"> | |||
@if (Model.SecuritySettings.TwoFactorEnabled) | |||
{ | |||
<form class="form" action="@Url.SubRouteUrl("user", "User.Action", new { action = "ConfirmAuthenticatorCode" })" method="post" id="confirmAuthSetup"> | |||
<p>To get a third party app working, either scan the QR code below or type the secret key into the app.</p> | |||
<div class="row"> | |||
<div class="col-sm-2"> | |||
QR Code: | |||
</div> | |||
<div class="col-sm-10 text-right"> | |||
<!-- QR Code --> | |||
<img src="@Url.SubRouteUrl("user", "User.Action", new { action = "GenerateQRCode", content = Model.SecuritySettings.TwoFactorKey })" alt="qr code" /> | |||
</div> | |||
</div> | |||
<div class="row"> | |||
<div class="col-sm-2"> | |||
Secret Key: | |||
</div> | |||
<div class="col-sm-10"> | |||
<span class="text-success" id="authSetupSecretKey"></span> | |||
</div> | |||
</div> | |||
<hr /> | |||
<p>To confirm the third party app is set up correctly, enter the security code that appears on your phone.</p> | |||
<div class="row"> | |||
<div class="col-sm-2"> | |||
Security Code: | |||
</div> | |||
<div class="col-sm-10"> | |||
<input class="form-control" id="auth_setup_code" name="auth_setup_code" title="Authenticator Security Code" type="text" /> | |||
</div> | |||
</div> | |||
<hr /> | |||
<br /> | |||
<div class="row"> | |||
<div class="col-sm-12"> | |||
<div class="form-group text-right"> | |||
<button class="btn btn-primary" id="auth_setup_confirm" type="submit" name="auth_setup_confirm">Confirm</button> | |||
</div> | |||
</div> | |||
</div> | |||
</form> | |||
} | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="row"> | |||
<div class="col-sm-12"> | |||
<form class="form" action="##" method="post" id="updateForm"> | |||
@@ -97,6 +151,22 @@ | |||
</div> | |||
</div> | |||
</div> | |||
<div class="row"> | |||
<div class="col-sm-6"> | |||
<div class="checkbox"> | |||
<label> | |||
<label for="update_security_two_factor"><h4>Enable Two Factor Authentication</h4></label> | |||
<input id="update_security_two_factor" name="update_security_two_factor" title="whether the key should be saved on the server or not" type="checkbox" value="true" @(Model.SecuritySettings.TwoFactorEnabled ? "checked" : string.Empty) /> | |||
@if (Model.SecuritySettings.TwoFactorEnabled) | |||
{ | |||
<p class="form-control-static"> | |||
<small><a href="#" class="text-primary" id="SetupAuthenticator"><i class="fa fa-lock"></i> Set Up Authenticator</a></small> | |||
</p> | |||
} | |||
</label> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<!-- Blog Settings --> | |||
<div class="tab-pane" id="blog"> |
@@ -1,6 +1,7 @@ | |||
using Microsoft.Win32; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Drawing; | |||
using System.Dynamic; | |||
using System.IO; | |||
using System.Linq; | |||
@@ -157,5 +158,18 @@ namespace Teknik | |||
} | |||
return hashString; | |||
} | |||
public static byte[] ImageToByte(Image img) | |||
{ | |||
byte[] byteArray = new byte[0]; | |||
using (MemoryStream stream = new MemoryStream()) | |||
{ | |||
img.Save(stream, System.Drawing.Imaging.ImageFormat.Png); | |||
stream.Close(); | |||
byteArray = stream.ToArray(); | |||
} | |||
return byteArray; | |||
} | |||
} | |||
} |
@@ -91,6 +91,10 @@ | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="PresentationFramework" /> | |||
<Reference Include="QRCoder, Version=1.1.8.0, Culture=neutral, processorArchitecture=MSIL"> | |||
<HintPath>..\packages\QRCoder.1.1.8\lib\net40\QRCoder.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="RouteDebugger, Version=2.1.5.0, Culture=neutral, processorArchitecture=MSIL"> | |||
<HintPath>..\packages\routedebugger.2.1.5\lib\net40\RouteDebugger.dll</HintPath> | |||
<Private>True</Private> | |||
@@ -154,6 +158,14 @@ | |||
<HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Razor.dll</HintPath> | |||
</Reference> | |||
<Reference Include="System.Xml.Linq" /> | |||
<Reference Include="TwoStepsAuthenticator, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL"> | |||
<HintPath>..\packages\TwoStepsAuthenticator.1.1.0\lib\net45\TwoStepsAuthenticator.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="UnityEngine, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL"> | |||
<HintPath>..\packages\QRCoder.1.1.8\lib\net40\UnityEngine.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="WebGrease, Version=1.6.5135.21930, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> | |||
<HintPath>..\packages\WebGrease.1.6.0\lib\WebGrease.dll</HintPath> | |||
<Private>True</Private> | |||
@@ -544,6 +556,7 @@ | |||
<Content Include="Areas\User\Views\User\ViewRecoveryEmailVerification.cshtml" /> | |||
<Content Include="Areas\User\Views\User\ResetPasswordVerification.cshtml" /> | |||
<Content Include="Areas\User\Views\User\ResetPassword.cshtml" /> | |||
<Content Include="Areas\User\Views\User\AuthenticatorSetup.cshtml" /> | |||
<None Include="Properties\PublishProfiles\Teknik Dev.pubxml" /> | |||
<None Include="Properties\PublishProfiles\Teknik Production.pubxml" /> | |||
<None Include="Scripts\jquery-2.1.4.intellisense.js" /> |
@@ -27,7 +27,9 @@ | |||
<package id="nClam" version="2.0.6.0" targetFramework="net452" /> | |||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" userInstalled="true" /> | |||
<package id="Piwik.Tracker" version="2.8.0.0" targetFramework="net452" /> | |||
<package id="QRCoder" version="1.1.8" targetFramework="net452" /> | |||
<package id="Respond" version="1.4.2" targetFramework="net452" userInstalled="true" /> | |||
<package id="routedebugger" version="2.1.5" targetFramework="net452" userInstalled="true" /> | |||
<package id="TwoStepsAuthenticator" version="1.1.0" targetFramework="net452" /> | |||
<package id="WebGrease" version="1.6.0" targetFramework="net46" userInstalled="true" /> | |||
</packages> |