1
0
mirror of https://git.teknik.io/Teknikode/Teknik.git synced 2023-08-02 14:16:22 +02:00

Added trusted device support for Two Factor authentication.

This commit is contained in:
Uncled1023 2016-06-30 17:00:44 -07:00
parent 011e737846
commit 000f977dfe
17 changed files with 202 additions and 44 deletions

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Web; using System.Web;
using System.Web.Mvc; using System.Web.Mvc;
using System.Web.Routing; using System.Web.Routing;
using Teknik.Helpers;
namespace Teknik namespace Teknik
{ {

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Teknik.Areas.Shortener.Models; using Teknik.Areas.Shortener.Models;
using Teknik.Helpers;
using Teknik.Models; using Teknik.Models;
namespace Teknik.Areas.Shortener namespace Teknik.Areas.Shortener

View File

@ -5,6 +5,7 @@ using System.Web;
using System.IO; using System.IO;
using Teknik.Configuration; using Teknik.Configuration;
using Teknik.Models; using Teknik.Models;
using Teknik.Helpers;
namespace Teknik.Areas.Upload namespace Teknik.Areas.Upload
{ {

View File

@ -23,6 +23,7 @@ using Teknik.Filters;
using QRCoder; using QRCoder;
using System.Text; using System.Text;
using TwoStepsAuthenticator; using TwoStepsAuthenticator;
using System.Drawing;
namespace Teknik.Areas.Users.Controllers namespace Teknik.Areas.Users.Controllers
{ {
@ -167,10 +168,30 @@ namespace Teknik.Areas.Users.Controllers
bool userValid = UserHelper.UserPasswordCorrect(db, Config, user, model.Password); bool userValid = UserHelper.UserPasswordCorrect(db, Config, user, model.Password);
if (userValid) if (userValid)
{ {
bool twoFactor = false;
string returnUrl = model.ReturnUrl; string returnUrl = model.ReturnUrl;
if (user.SecuritySettings.TwoFactorEnabled) if (user.SecuritySettings.TwoFactorEnabled)
{ {
twoFactor = true;
// We need to check their device, and two factor them // We need to check their device, and two factor them
if (user.SecuritySettings.AllowTrustedDevices)
{
// Check for the trusted device cookie
HttpCookie cookie = Request.Cookies[Constants.TRUSTEDDEVICECOOKIE + "_" + username];
if (cookie != null)
{
string token = cookie.Value;
if (user.TrustedDevices.Where(d => d.Token == token).FirstOrDefault() != null)
{
// The device token is attached to the user, let's let it slide
twoFactor = false;
}
}
}
}
if (twoFactor)
{
Session["AuthenticatedUser"] = user; Session["AuthenticatedUser"] = user;
if (string.IsNullOrEmpty(model.ReturnUrl)) if (string.IsNullOrEmpty(model.ReturnUrl))
returnUrl = Request.UrlReferrer.AbsoluteUri.ToString(); returnUrl = Request.UrlReferrer.AbsoluteUri.ToString();
@ -295,7 +316,7 @@ namespace Teknik.Areas.Users.Controllers
} }
[HttpPost] [HttpPost]
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) public ActionResult Edit(string curPass, string newPass, string newPassConfirm, string pgpPublicKey, string recoveryEmail, bool allowTrustedDevices, bool twoFactorEnabled, string website, string quote, string about, string blogTitle, string blogDesc, bool saveKey, bool serverSideEncrypt)
{ {
if (ModelState.IsValid) if (ModelState.IsValid)
{ {
@ -329,6 +350,7 @@ namespace Teknik.Areas.Users.Controllers
} }
user.SecuritySettings.PGPSignature = pgpPublicKey; user.SecuritySettings.PGPSignature = pgpPublicKey;
// Recovery Email
bool newRecovery = false; bool newRecovery = false;
if (recoveryEmail != user.SecuritySettings.RecoveryEmail) if (recoveryEmail != user.SecuritySettings.RecoveryEmail)
{ {
@ -337,24 +359,56 @@ namespace Teknik.Areas.Users.Controllers
user.SecuritySettings.RecoveryVerified = false; user.SecuritySettings.RecoveryVerified = false;
} }
// Trusted Devices
user.SecuritySettings.AllowTrustedDevices = allowTrustedDevices;
if (!allowTrustedDevices)
{
// They turned it off, let's clear the trusted devices
user.TrustedDevices.Clear();
List<TrustedDevice> foundDevices = db.TrustedDevices.Where(d => d.UserId == user.UserId).ToList();
if (foundDevices != null)
{
foreach (TrustedDevice device in foundDevices)
{
db.TrustedDevices.Remove(device);
}
}
}
// Two Factor Authentication
bool oldTwoFactor = user.SecuritySettings.TwoFactorEnabled; bool oldTwoFactor = user.SecuritySettings.TwoFactorEnabled;
user.SecuritySettings.TwoFactorEnabled = twoFactorEnabled; user.SecuritySettings.TwoFactorEnabled = twoFactorEnabled;
string newKey = string.Empty; string newKey = string.Empty;
if (twoFactorEnabled) if (!oldTwoFactor && twoFactorEnabled)
{ {
// They just enabled it, let's regen the key
newKey = Authenticator.GenerateKey(); newKey = Authenticator.GenerateKey();
} }
else if (!twoFactorEnabled)
{
// remove the key when it's disabled
newKey = string.Empty;
}
else
{
// No change, let's use the old value
newKey = user.SecuritySettings.TwoFactorKey;
}
user.SecuritySettings.TwoFactorKey = newKey; user.SecuritySettings.TwoFactorKey = newKey;
// Profile Info
user.UserSettings.Website = website; user.UserSettings.Website = website;
user.UserSettings.Quote = quote; user.UserSettings.Quote = quote;
user.UserSettings.About = about; user.UserSettings.About = about;
// Blogs
user.BlogSettings.Title = blogTitle; user.BlogSettings.Title = blogTitle;
user.BlogSettings.Description = blogDesc; user.BlogSettings.Description = blogDesc;
// Uploads
user.UploadSettings.SaveKey = saveKey; user.UploadSettings.SaveKey = saveKey;
user.UploadSettings.ServerSideEncrypt = serverSideEncrypt; user.UploadSettings.ServerSideEncrypt = serverSideEncrypt;
UserHelper.EditAccount(db, Config, user, changePass, newPass); UserHelper.EditAccount(db, Config, user, changePass, newPass);
// If they have a recovery email, let's send a verification // If they have a recovery email, let's send a verification
@ -556,19 +610,25 @@ namespace Teknik.Areas.Users.Controllers
[HttpGet] [HttpGet]
[AllowAnonymous] [AllowAnonymous]
public ActionResult ConfirmTwoFactorAuth(string returnUrl, bool rememberMe) public ActionResult ConfirmTwoFactorAuth(string returnUrl, bool rememberMe)
{
User user = (User)Session["AuthenticatedUser"];
if (user != null)
{ {
ViewBag.Title = "Unknown Device - " + Config.Title; ViewBag.Title = "Unknown Device - " + Config.Title;
ViewBag.Description = "We do not recognize this device."; ViewBag.Description = "We do not recognize this device.";
LoginViewModel model = new LoginViewModel(); TwoFactorViewModel model = new TwoFactorViewModel();
model.ReturnUrl = returnUrl; model.ReturnUrl = returnUrl;
model.RememberMe = rememberMe; model.RememberMe = rememberMe;
model.AllowTrustedDevice = user.SecuritySettings.AllowTrustedDevices;
return View("/Areas/User/Views/User/TwoFactorCheck.cshtml", model); return View("/Areas/User/Views/User/TwoFactorCheck.cshtml", model);
} }
return Redirect(Url.SubRouteUrl("error", "Error.Http403"));
}
[HttpPost] [HttpPost]
[AllowAnonymous] [AllowAnonymous]
public ActionResult ConfirmAuthenticatorCode(string code, string returnUrl, bool rememberMe) public ActionResult ConfirmAuthenticatorCode(string code, string returnUrl, bool rememberMe, bool rememberDevice, string deviceName)
{ {
User user = (User)Session["AuthenticatedUser"]; User user = (User)Session["AuthenticatedUser"];
if (user != null) if (user != null)
@ -586,6 +646,23 @@ namespace Teknik.Areas.Users.Controllers
HttpCookie authcookie = UserHelper.CreateAuthCookie(user.Username, rememberMe, Request.Url.Host.GetDomain(), Request.IsLocal); HttpCookie authcookie = UserHelper.CreateAuthCookie(user.Username, rememberMe, Request.Url.Host.GetDomain(), Request.IsLocal);
Response.Cookies.Add(authcookie); Response.Cookies.Add(authcookie);
if (user.SecuritySettings.AllowTrustedDevices && rememberDevice)
{
// They want to remember the device, and have allow trusted devices on
HttpCookie trustedDeviceCookie = UserHelper.CreateTrustedDeviceCookie(user.Username, Request.Url.Host.GetDomain(), Request.IsLocal);
Response.Cookies.Add(trustedDeviceCookie);
TrustedDevice device = new TrustedDevice();
device.UserId = user.UserId;
device.Name = (string.IsNullOrEmpty(deviceName)) ? "Unknown" : deviceName;
device.DateSeen = DateTime.Now;
device.Token = trustedDeviceCookie.Value;
// Add the token
db.TrustedDevices.Add(device);
db.SaveChanges();
}
if (string.IsNullOrEmpty(returnUrl)) if (string.IsNullOrEmpty(returnUrl))
returnUrl = Request.UrlReferrer.AbsoluteUri.ToString(); returnUrl = Request.UrlReferrer.AbsoluteUri.ToString();
return Json(new { result = returnUrl }); return Json(new { result = returnUrl });
@ -628,9 +705,9 @@ namespace Teknik.Areas.Users.Controllers
QRCodeGenerator qrGenerator = new QRCodeGenerator(); QRCodeGenerator qrGenerator = new QRCodeGenerator();
QRCodeData qrCodeData = qrGenerator.CreateQrCode(ProvisionUrl, QRCodeGenerator.ECCLevel.Q); QRCodeData qrCodeData = qrGenerator.CreateQrCode(ProvisionUrl, QRCodeGenerator.ECCLevel.Q);
SvgQRCode qrCode = new SvgQRCode(qrCodeData); QRCode qrCode = new QRCode(qrCodeData);
string qrCodeImage = qrCode.GetGraphic(20); Bitmap qrCodeImage = qrCode.GetGraphic(20);
return File(Encoding.UTF8.GetBytes(qrCodeImage), "image/svg+xml"); return File(Helpers.Utility.ImageToByte(qrCodeImage), "image/png");
} }
} }
} }

View File

@ -24,6 +24,8 @@ namespace Teknik.Areas.Users.Models
public bool RecoveryVerified { get; set; } public bool RecoveryVerified { get; set; }
public bool AllowTrustedDevices { get; set; }
public bool TwoFactorEnabled { get; set; } public bool TwoFactorEnabled { get; set; }
[CaseSensitive] [CaseSensitive]
@ -35,6 +37,7 @@ namespace Teknik.Areas.Users.Models
{ {
RecoveryEmail = string.Empty; RecoveryEmail = string.Empty;
RecoveryVerified = false; RecoveryVerified = false;
AllowTrustedDevices = false;
TwoFactorEnabled = false; TwoFactorEnabled = false;
TwoFactorKey = string.Empty; TwoFactorKey = string.Empty;
PGPSignature = string.Empty; PGPSignature = string.Empty;

View File

@ -6,9 +6,9 @@ using Teknik.Attributes;
namespace Teknik.Areas.Users.Models namespace Teknik.Areas.Users.Models
{ {
public class UserDevice public class TrustedDevice
{ {
public int UserDeviceId { get; set; } public int TrustedDeviceId { get; set; }
public int UserId { get; set; } public int UserId { get; set; }

View File

@ -33,7 +33,7 @@ namespace Teknik.Areas.Users.Models
public virtual UploadSettings UploadSettings { get; set; } public virtual UploadSettings UploadSettings { get; set; }
public virtual ICollection<UserDevice> Devices { get; set; } public virtual ICollection<TrustedDevice> TrustedDevices { get; set; }
public virtual ICollection<Upload.Models.Upload> Uploads { get; set; } public virtual ICollection<Upload.Models.Upload> Uploads { get; set; }
@ -47,7 +47,7 @@ namespace Teknik.Areas.Users.Models
JoinDate = DateTime.Now; JoinDate = DateTime.Now;
LastSeen = DateTime.Now; LastSeen = DateTime.Now;
Groups = new List<Group>(); Groups = new List<Group>();
Devices = new List<UserDevice>(); TrustedDevices = new List<TrustedDevice>();
} }
} }
} }

View File

@ -2,16 +2,18 @@
$("#authCheckStatus").css('display', 'none', 'important'); $("#authCheckStatus").css('display', 'none', 'important');
$("#verifyCodeSubmit").click(function () { $("#verifyCodeSubmit").click(function () {
setCode = $("#code").val(); setCode = $("#Code").val();
returnUrl = $("#returnUrl").val(); returnUrl = $("#ReturnUrl").val();
rememberMe = ($("#rememberMe").val() == 'True'); rememberMe = ($("#RememberMe").val() == 'True');
rememberDevice = $("#RememberDevice").is(":checked");
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: confirmAuthCodeURL, url: confirmAuthCodeURL,
data: { data: {
code: setCode, code: setCode,
returnUrl: returnUrl, returnUrl: returnUrl,
rememberMe: rememberMe rememberMe: rememberMe,
rememberDevice: rememberDevice
}, },
xhrFields: { xhrFields: {
withCredentials: true withCredentials: true

View File

@ -2,6 +2,7 @@
$("[name='update_upload_saveKey']").bootstrapSwitch(); $("[name='update_upload_saveKey']").bootstrapSwitch();
$("[name='update_upload_serverSideEncrypt']").bootstrapSwitch(); $("[name='update_upload_serverSideEncrypt']").bootstrapSwitch();
$("[name='update_security_two_factor']").bootstrapSwitch(); $("[name='update_security_two_factor']").bootstrapSwitch();
$("[name='update_security_allow_trusted']").bootstrapSwitch();
$('#ResendVerification').click(function () { $('#ResendVerification').click(function () {
$.ajax({ $.ajax({
@ -98,6 +99,7 @@
password = $("#update_password").val(); password = $("#update_password").val();
password_confirm = $("#update_password_confirm").val(); password_confirm = $("#update_password_confirm").val();
update_pgp_public_key = $("#update_pgp_public_key").val(); update_pgp_public_key = $("#update_pgp_public_key").val();
update_security_allow_trusted = $("#update_security_allow_trusted").is(":checked");
update_security_two_factor = $("#update_security_two_factor").is(":checked"); update_security_two_factor = $("#update_security_two_factor").is(":checked");
recovery = $("#update_recovery_email").val(); recovery = $("#update_recovery_email").val();
website = $("#update_website").val(); website = $("#update_website").val();
@ -115,6 +117,7 @@
newPass: password, newPass: password,
newPassConfirm: password_confirm, newPassConfirm: password_confirm,
pgpPublicKey: update_pgp_public_key, pgpPublicKey: update_pgp_public_key,
allowTrustedDevices: update_security_allow_trusted,
twoFactorEnabled: update_security_two_factor, twoFactorEnabled: update_security_two_factor,
recoveryEmail: recovery, recoveryEmail: recovery,
website: website, website: website,

View File

@ -437,7 +437,7 @@ namespace Teknik.Areas.Users.Utility
} }
// Create a new verification code and add it // Create a new verification code and add it
string verifyCode = Teknik.Utility.RandomString(24); string verifyCode = Helpers.Utility.RandomString(24);
RecoveryEmailVerification ver = new RecoveryEmailVerification(); RecoveryEmailVerification ver = new RecoveryEmailVerification();
ver.UserId = user.UserId; ver.UserId = user.UserId;
ver.Code = verifyCode; ver.Code = verifyCode;
@ -518,7 +518,7 @@ Thank you and enjoy!
} }
// Create a new verification code and add it // Create a new verification code and add it
string verifyCode = Teknik.Utility.RandomString(24); string verifyCode = Helpers.Utility.RandomString(24);
ResetPasswordVerification ver = new ResetPasswordVerification(); ResetPasswordVerification ver = new ResetPasswordVerification();
ver.UserId = user.UserId; ver.UserId = user.UserId;
ver.Code = verifyCode; ver.Code = verifyCode;
@ -858,7 +858,7 @@ If you recieved this email and you did not reset your password, you can ignore t
{ {
Config config = Config.Load(); Config config = Config.Load();
HttpCookie authcookie = FormsAuthentication.GetAuthCookie(username, remember); HttpCookie authcookie = FormsAuthentication.GetAuthCookie(username, remember);
authcookie.Name = "TeknikAuth"; authcookie.Name = Constants.AUTHCOOKIE;
authcookie.HttpOnly = true; authcookie.HttpOnly = true;
authcookie.Secure = true; authcookie.Secure = true;
@ -878,5 +878,35 @@ If you recieved this email and you did not reset your password, you can ignore t
return authcookie; return authcookie;
} }
public static HttpCookie CreateTrustedDeviceCookie(string username, string domain, bool local)
{
Config config = Config.Load();
byte[] time = BitConverter.GetBytes(DateTime.UtcNow.ToBinary());
byte[] key = Guid.NewGuid().ToByteArray();
string token = Convert.ToBase64String(time.Concat(key).ToArray());
HttpCookie trustCookie = new HttpCookie(Constants.TRUSTEDDEVICECOOKIE + "_" + username);
trustCookie.Value = token;
trustCookie.HttpOnly = true;
trustCookie.Secure = true;
trustCookie.Expires = DateTime.Now.AddYears(1);
// Set domain dependent on where it's being ran from
if (local) // localhost
{
trustCookie.Domain = null;
}
else if (config.DevEnvironment) // dev.example.com
{
trustCookie.Domain = string.Format("dev.{0}", domain);
}
else // A production instance
{
trustCookie.Domain = string.Format(".{0}", domain);
}
return trustCookie;
}
} }
} }

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Teknik.ViewModels;
namespace Teknik.Areas.Users.ViewModels
{
public class TwoFactorViewModel : ViewModelBase
{
public bool RememberMe { get; set; }
public string ReturnUrl { get; set; }
public bool AllowTrustedDevice { get; set; }
}
}

View File

@ -125,19 +125,6 @@
<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.SecuritySettings.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.SecuritySettings.PGPSignature</textarea>
</div> </div>
</div> </div>
<div class="row">
<div class="col-sm-6 text-left">
<label for="update_security_two_factor"><h4>Enable Two Factor Authentication</h4></label>
<div class="checkbox">
<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) />
</label>
</div>
<p class="form-control-static @(Model.SecuritySettings.TwoFactorEnabled ? string.Empty : "hide")" id="setupAuthenticatorLink">
<small><a href="#" class="text-primary" id="SetupAuthenticator" data-toggle="modal" data-target="#authenticatorSetup"><i class="fa fa-lock"></i> Set Up Authenticator</a></small>
</p>
</div>
</div>
<div class="row"> <div class="row">
<div class="col-sm-4"> <div class="col-sm-4">
<div class="row"> <div class="row">
@ -161,6 +148,27 @@
</div> </div>
</div> </div>
</div> </div>
<div class="row">
<div class="col-sm-4 text-left">
<label for="update_security_two_factor"><h4>Enable Two Factor Authentication</h4></label>
<div class="checkbox">
<label>
<input id="update_security_two_factor" name="update_security_two_factor" title="whether two factor authentication should occur for this account" type="checkbox" value="true" @(Model.SecuritySettings.TwoFactorEnabled ? "checked" : string.Empty) />
</label>
</div>
<p class="form-control-static @(Model.SecuritySettings.TwoFactorEnabled ? string.Empty : "hide")" id="setupAuthenticatorLink">
<small><a href="#" class="text-primary" id="SetupAuthenticator" data-toggle="modal" data-target="#authenticatorSetup"><i class="fa fa-lock"></i> Set Up Authenticator</a></small>
</p>
</div>
<div class="col-sm-4 text-left">
<label for="update_security_allow_trusted"><h4>Allow Trusted Device Saving</h4></label>
<div class="checkbox">
<label>
<input id="update_security_allow_trusted" name="update_security_allow_trusted" title="whether a device could be cached to bypass two factor authentication" type="checkbox" value="true" @(Model.SecuritySettings.AllowTrustedDevices ? "checked" : string.Empty) />
</label>
</div>
</div>
</div>
</div> </div>
<!-- Blog Settings --> <!-- Blog Settings -->
<div class="tab-pane" id="blog"> <div class="tab-pane" id="blog">

View File

@ -1,4 +1,4 @@
@model Teknik.Areas.Users.ViewModels.LoginViewModel @model Teknik.Areas.Users.ViewModels.TwoFactorViewModel
@using Teknik.Helpers @using Teknik.Helpers
@ -22,12 +22,23 @@
</div> </div>
</div> </div>
<form role="form" id="twoFactorCheckForm" action="##" method="post" accept-charset="UTF-8"> <form role="form" id="twoFactorCheckForm" action="##" method="post" accept-charset="UTF-8">
<input name="returnUrl" id="returnUrl" type="hidden" value="@Model.ReturnUrl" /> <input name="ReturnUrl" id="ReturnUrl" type="hidden" value="@Model.ReturnUrl" />
<input name="rememberMe" id="rememberMe" type="hidden" value="@Model.RememberMe" /> <input name="RememberMe" id="RememberMe" type="hidden" value="@Model.RememberMe" />
<div class="form-group text-left"> <div class="form-group text-left">
<label for="update_website">Authentication code</label> <label for="update_website">Authentication code</label>
<input type="text" class="form-control" id="code" name="code" data-val-required="The Authentication Code is required." data-val="true" /> <input type="text" class="form-control" id="Code" name="Code" data-val-required="The Authentication Code is required." data-val="true" />
</div> </div>
@if (Model.AllowTrustedDevice)
{
<div class="checkbox">
<label>
<input id="RememberDevice" type="checkbox" value="true" name="RememberDevice" /><input name="RememberDevice" type="hidden" value="false"> Remember Device
</label>
</div>
<small>Set this device as a trusted device. It is not advised to trust a public computer.</small>
<br />
<br />
}
<div class="form-group"> <div class="form-group">
<button class="btn btn-primary btn-block" id="verifyCodeSubmit" type="button" name="verifyCodeSubmit">Verify</button> <button class="btn btn-primary btn-block" id="verifyCodeSubmit" type="button" name="verifyCodeSubmit">Verify</button>
</div> </div>

View File

@ -9,6 +9,9 @@ namespace Teknik.Helpers
public static class Constants public static class Constants
{ {
public const string SERVERUSER = "Server Admin"; public const string SERVERUSER = "Server Admin";
public const string AUTHCOOKIE = "TeknikAuth";
public const string TRUSTEDDEVICECOOKIE = "TeknikTrustedDevice";
// Paste Constants // Paste Constants
public static Dictionary<string, string> HIGHLIGHTFORMATS = new Dictionary<string, string>() public static Dictionary<string, string> HIGHLIGHTFORMATS = new Dictionary<string, string>()
{ {

View File

@ -8,7 +8,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Web; using System.Web;
namespace Teknik namespace Teknik.Helpers
{ {
public static class Utility public static class Utility
{ {

View File

@ -22,7 +22,7 @@ namespace Teknik.Models
public DbSet<User> Users { get; set; } public DbSet<User> Users { get; set; }
public DbSet<Group> Groups { get; set; } public DbSet<Group> Groups { get; set; }
public DbSet<Role> Roles { get; set; } public DbSet<Role> Roles { get; set; }
public DbSet<UserDevice> UserDevices { get; set; } public DbSet<TrustedDevice> TrustedDevices { get; set; }
public DbSet<TransferType> TransferTypes { get; set; } public DbSet<TransferType> TransferTypes { get; set; }
// User Settings // User Settings
public DbSet<UserSettings> UserSettings { get; set; } public DbSet<UserSettings> UserSettings { get; set; }
@ -112,7 +112,7 @@ namespace Teknik.Models
modelBuilder.Entity<User>().ToTable("Users"); modelBuilder.Entity<User>().ToTable("Users");
modelBuilder.Entity<Group>().ToTable("Groups"); modelBuilder.Entity<Group>().ToTable("Groups");
modelBuilder.Entity<Role>().ToTable("Roles"); modelBuilder.Entity<Role>().ToTable("Roles");
modelBuilder.Entity<UserDevice>().ToTable("UserDevices"); modelBuilder.Entity<TrustedDevice>().ToTable("TrustedDevices");
modelBuilder.Entity<TransferType>().ToTable("TransferTypes"); modelBuilder.Entity<TransferType>().ToTable("TransferTypes");
modelBuilder.Entity<RecoveryEmailVerification>().ToTable("RecoveryEmailVerifications"); modelBuilder.Entity<RecoveryEmailVerification>().ToTable("RecoveryEmailVerifications");
modelBuilder.Entity<ResetPasswordVerification>().ToTable("ResetPasswordVerifications"); modelBuilder.Entity<ResetPasswordVerification>().ToTable("ResetPasswordVerifications");

View File

@ -224,7 +224,8 @@
<Compile Include="Areas\User\Models\ResetPasswordVerification.cs" /> <Compile Include="Areas\User\Models\ResetPasswordVerification.cs" />
<Compile Include="Areas\User\Models\RecoveryEmailVerification.cs" /> <Compile Include="Areas\User\Models\RecoveryEmailVerification.cs" />
<Compile Include="Areas\User\Models\SecuritySettings.cs" /> <Compile Include="Areas\User\Models\SecuritySettings.cs" />
<Compile Include="Areas\User\Models\UserDevice.cs" /> <Compile Include="Areas\User\Models\TrustedDevice.cs" />
<Compile Include="Areas\User\ViewModels\TwoFactorViewModel.cs" />
<Compile Include="Models\TransferTypes.cs" /> <Compile Include="Models\TransferTypes.cs" />
<Compile Include="Areas\User\Models\UploadSettings.cs" /> <Compile Include="Areas\User\Models\UploadSettings.cs" />
<Compile Include="Areas\User\Models\UserSettings.cs" /> <Compile Include="Areas\User\Models\UserSettings.cs" />