added token based registration.

This commit is contained in:
DESKTOP-T0O5CDB\DESK-555BD
2024-01-12 18:37:51 -07:00
parent bb4a8f7f83
commit 8815009b04
20 changed files with 581 additions and 125 deletions

View File

@@ -0,0 +1,31 @@
using CarCareTracker.Logic;
using CarCareTracker.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace CarCareTracker.Controllers
{
[Authorize(Roles = nameof(UserData.IsAdmin))]
public class AdminController : Controller
{
private ILoginLogic _loginLogic;
public AdminController(ILoginLogic loginLogic)
{
_loginLogic = loginLogic;
}
public IActionResult Index()
{
var viewModel = new AdminViewModel
{
Users = _loginLogic.GetAllUsers(),
Tokens = _loginLogic.GetAllTokens()
};
return View(viewModel);
}
public IActionResult GenerateNewToken()
{
var result = _loginLogic.GenerateUserToken();
return Json(result);
}
}
}

View File

@@ -1,4 +1,5 @@
using CarCareTracker.Helper; using CarCareTracker.Helper;
using CarCareTracker.Logic;
using CarCareTracker.Models; using CarCareTracker.Models;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
@@ -13,22 +14,26 @@ namespace CarCareTracker.Controllers
public class LoginController : Controller public class LoginController : Controller
{ {
private IDataProtector _dataProtector; private IDataProtector _dataProtector;
private ILoginHelper _loginHelper; private ILoginLogic _loginLogic;
private readonly ILogger<LoginController> _logger; private readonly ILogger<LoginController> _logger;
public LoginController( public LoginController(
ILogger<LoginController> logger, ILogger<LoginController> logger,
IDataProtectionProvider securityProvider, IDataProtectionProvider securityProvider,
ILoginHelper loginHelper ILoginLogic loginLogic
) )
{ {
_dataProtector = securityProvider.CreateProtector("login"); _dataProtector = securityProvider.CreateProtector("login");
_logger = logger; _logger = logger;
_loginHelper = loginHelper; _loginLogic = loginLogic;
} }
public IActionResult Index() public IActionResult Index()
{ {
return View(); return View();
} }
public IActionResult Registration()
{
return View();
}
[HttpPost] [HttpPost]
public IActionResult Login(LoginModel credentials) public IActionResult Login(LoginModel credentials)
{ {
@@ -40,13 +45,12 @@ namespace CarCareTracker.Controllers
//compare it against hashed credentials //compare it against hashed credentials
try try
{ {
var loginIsValid = _loginHelper.ValidateUserCredentials(credentials); var userData = _loginLogic.ValidateUserCredentials(credentials);
if (loginIsValid) if (userData.Id != default)
{ {
AuthCookie authCookie = new AuthCookie AuthCookie authCookie = new AuthCookie
{ {
Id = 1, //this is hardcoded for now UserData = userData,
UserName = credentials.UserName,
ExpiresOn = DateTime.Now.AddDays(credentials.IsPersistent ? 30 : 1) ExpiresOn = DateTime.Now.AddDays(credentials.IsPersistent ? 30 : 1)
}; };
var serializedCookie = JsonSerializer.Serialize(authCookie); var serializedCookie = JsonSerializer.Serialize(authCookie);
@@ -61,26 +65,21 @@ namespace CarCareTracker.Controllers
} }
return Json(false); return Json(false);
} }
[HttpPost]
public IActionResult Register(LoginModel credentials)
{
var result = _loginLogic.RegisterNewUser(credentials);
return Json(result);
}
[Authorize] //User must already be logged in to do this. [Authorize] //User must already be logged in to do this.
[HttpPost] [HttpPost]
public IActionResult CreateLoginCreds(LoginModel credentials) public IActionResult CreateLoginCreds(LoginModel credentials)
{ {
try try
{ {
var configFileContents = System.IO.File.ReadAllText(StaticHelper.UserConfigPath); var result = _loginLogic.CreateRootUserCredentials(credentials);
var existingUserConfig = JsonSerializer.Deserialize<UserConfig>(configFileContents); return Json(result);
if (existingUserConfig is not null)
{
//create hashes of the login credentials.
var hashedUserName = Sha256_hash(credentials.UserName);
var hashedPassword = Sha256_hash(credentials.Password);
//copy over settings that are off limits on the settings page.
existingUserConfig.EnableAuth = true;
existingUserConfig.UserNameHash = hashedUserName;
existingUserConfig.UserPasswordHash = hashedPassword;
}
System.IO.File.WriteAllText(StaticHelper.UserConfigPath, JsonSerializer.Serialize(existingUserConfig));
return Json(true);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -94,19 +93,13 @@ namespace CarCareTracker.Controllers
{ {
try try
{ {
var configFileContents = System.IO.File.ReadAllText(StaticHelper.UserConfigPath); var result = _loginLogic.DeleteRootUserCredentials();
var existingUserConfig = JsonSerializer.Deserialize<UserConfig>(configFileContents);
if (existingUserConfig is not null)
{
//copy over settings that are off limits on the settings page.
existingUserConfig.EnableAuth = false;
existingUserConfig.UserNameHash = string.Empty;
existingUserConfig.UserPasswordHash = string.Empty;
}
System.IO.File.WriteAllText(StaticHelper.UserConfigPath, JsonSerializer.Serialize(existingUserConfig));
//destroy any login cookies. //destroy any login cookies.
Response.Cookies.Delete("ACCESS_TOKEN"); if (result)
return Json(true); {
Response.Cookies.Delete("ACCESS_TOKEN");
}
return Json(result);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -121,20 +114,5 @@ namespace CarCareTracker.Controllers
Response.Cookies.Delete("ACCESS_TOKEN"); Response.Cookies.Delete("ACCESS_TOKEN");
return Json(true); return Json(true);
} }
private static string Sha256_hash(string value)
{
StringBuilder Sb = new StringBuilder();
using (var hash = SHA256.Create())
{
Encoding enc = Encoding.UTF8;
byte[] result = hash.ComputeHash(enc.GetBytes(value));
foreach (byte b in result)
Sb.Append(b.ToString("x2"));
}
return Sb.ToString();
}
} }
} }

View File

@@ -0,0 +1,48 @@
using CarCareTracker.External.Interfaces;
using CarCareTracker.Helper;
using CarCareTracker.Models;
using LiteDB;
namespace CarCareTracker.External.Implementations
{
public class TokenRecordDataAccess : ITokenRecordDataAccess
{
private static string dbName = StaticHelper.DbName;
private static string tableName = "tokenrecords";
public List<Token> GetTokens()
{
using (var db = new LiteDatabase(dbName))
{
var table = db.GetCollection<Token>(tableName);
return table.FindAll().ToList();
};
}
public Token GetTokenRecordByBody(string tokenBody)
{
using (var db = new LiteDatabase(dbName))
{
var table = db.GetCollection<Token>(tableName);
var tokenRecord = table.FindOne(Query.EQ(nameof(Token.Body), tokenBody));
return tokenRecord ?? new Token();
};
}
public bool CreateNewToken(Token token)
{
using (var db = new LiteDatabase(dbName))
{
var table = db.GetCollection<Token>(tableName);
table.Insert(token);
return true;
};
}
public bool DeleteToken(int tokenId)
{
using (var db = new LiteDatabase(dbName))
{
var table = db.GetCollection<Token>(tableName);
table.Delete(tokenId);
return true;
};
}
}
}

View File

@@ -0,0 +1,57 @@
using CarCareTracker.External.Interfaces;
using CarCareTracker.Helper;
using CarCareTracker.Models;
using LiteDB;
namespace CarCareTracker.External.Implementations
{
public class UserRecordDataAccess : IUserRecordDataAccess
{
private static string dbName = StaticHelper.DbName;
private static string tableName = "userrecords";
public List<UserData> GetUsers()
{
using (var db = new LiteDatabase(dbName))
{
var table = db.GetCollection<UserData>(tableName);
return table.FindAll().ToList();
};
}
public UserData GetUserRecordByUserName(string userName)
{
using (var db = new LiteDatabase(dbName))
{
var table = db.GetCollection<UserData>(tableName);
var userRecord = table.FindOne(Query.EQ(nameof(UserData.UserName), userName));
return userRecord ?? new UserData();
};
}
public UserData GetUserRecordById(int userId)
{
using (var db = new LiteDatabase(dbName))
{
var table = db.GetCollection<UserData>(tableName);
var userRecord = table.FindById(userId);
return userRecord ?? new UserData();
};
}
public bool SaveUserRecord(UserData userRecord)
{
using (var db = new LiteDatabase(dbName))
{
var table = db.GetCollection<UserData>(tableName);
table.Upsert(userRecord);
return true;
};
}
public bool DeleteUserRecord(int userId)
{
using (var db = new LiteDatabase(dbName))
{
var table = db.GetCollection<UserData>(tableName);
table.Delete(userId);
return true;
};
}
}
}

View File

@@ -0,0 +1,12 @@
using CarCareTracker.Models;
namespace CarCareTracker.External.Interfaces
{
public interface ITokenRecordDataAccess
{
public List<Token> GetTokens();
public Token GetTokenRecordByBody(string tokenBody);
public bool CreateNewToken(Token token);
public bool DeleteToken(int tokenId);
}
}

View File

@@ -0,0 +1,13 @@
using CarCareTracker.Models;
namespace CarCareTracker.External.Interfaces
{
public interface IUserRecordDataAccess
{
public List<UserData> GetUsers();
public UserData GetUserRecordByUserName(string userName);
public UserData GetUserRecordById(int userId);
public bool SaveUserRecord(UserData userRecord);
public bool DeleteUserRecord(int userId);
}
}

View File

@@ -1,47 +0,0 @@
using CarCareTracker.Models;
using System.Security.Cryptography;
using System.Text;
namespace CarCareTracker.Helper
{
public interface ILoginHelper
{
bool ValidateUserCredentials(LoginModel credentials);
}
public class LoginHelper: ILoginHelper
{
public bool ValidateUserCredentials(LoginModel credentials)
{
var configFileContents = System.IO.File.ReadAllText(StaticHelper.UserConfigPath);
var existingUserConfig = System.Text.Json.JsonSerializer.Deserialize<UserConfig>(configFileContents);
if (existingUserConfig is not null)
{
//create hashes of the login credentials.
var hashedUserName = Sha256_hash(credentials.UserName);
var hashedPassword = Sha256_hash(credentials.Password);
//compare against stored hash.
if (hashedUserName == existingUserConfig.UserNameHash &&
hashedPassword == existingUserConfig.UserPasswordHash)
{
return true;
}
}
return false;
}
private static string Sha256_hash(string value)
{
StringBuilder Sb = new StringBuilder();
using (var hash = SHA256.Create())
{
Encoding enc = Encoding.UTF8;
byte[] result = hash.ComputeHash(enc.GetBytes(value));
foreach (byte b in result)
Sb.Append(b.ToString("x2"));
}
return Sb.ToString();
}
}
}

201
Logic/LoginLogic.cs Normal file
View File

@@ -0,0 +1,201 @@
using CarCareTracker.External.Interfaces;
using CarCareTracker.Helper;
using CarCareTracker.Models;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
namespace CarCareTracker.Logic
{
public interface ILoginLogic
{
bool GenerateUserToken();
OperationResponse RegisterNewUser(LoginModel credentials);
OperationResponse ResetUserPassword(LoginModel credentials);
UserData ValidateUserCredentials(LoginModel credentials);
bool CreateRootUserCredentials(LoginModel credentials);
bool DeleteRootUserCredentials();
List<UserData> GetAllUsers();
List<Token> GetAllTokens();
}
public class LoginLogic : ILoginLogic
{
private readonly IUserRecordDataAccess _userData;
private readonly ITokenRecordDataAccess _tokenData;
public LoginLogic(IUserRecordDataAccess userData, ITokenRecordDataAccess tokenData)
{
_userData = userData;
_tokenData = tokenData;
}
public OperationResponse RegisterNewUser(LoginModel credentials)
{
//validate their token.
var existingToken = _tokenData.GetTokenRecordByBody(credentials.Token);
if (existingToken.Id == default)
{
return new OperationResponse { Success = false, Message = "Invalid Token" };
}
//token is valid, check if username and password is acceptable and that username is unique.
if (string.IsNullOrWhiteSpace(credentials.UserName) || string.IsNullOrWhiteSpace(credentials.Password))
{
return new OperationResponse { Success = false, Message = "Neither username nor password can be blank" };
}
var existingUser = _userData.GetUserRecordByUserName(credentials.UserName);
if (existingUser.Id != default)
{
return new OperationResponse { Success = false, Message = "Username already taken" };
}
//username is unique then we delete the token and create the user.
_tokenData.DeleteToken(existingToken.Id);
var newUser = new UserData()
{
UserName = credentials.UserName,
Password = GetHash(credentials.Password)
};
var result = _userData.SaveUserRecord(newUser);
if (result)
{
return new OperationResponse { Success = true, Message = "You will be redirected to the login page briefly." };
} else
{
return new OperationResponse { Success = false, Message = "Something went wrong, please try again later." };
}
}
/// <summary>
/// Returns an empty user if can't auth against neither root nor db user.
/// </summary>
/// <param name="credentials">credentials from login page</param>
/// <returns></returns>
public UserData ValidateUserCredentials(LoginModel credentials)
{
if (UserIsRoot(credentials))
{
return new UserData()
{
Id = -1,
UserName = credentials.UserName,
IsAdmin = true
};
} else
{
//authenticate via DB.
var result = _userData.GetUserRecordByUserName(credentials.UserName);
if (GetHash(credentials.Password) == result.Password)
{
result.Password = string.Empty;
return result;
} else
{
return new UserData();
}
}
}
#region "Admin Functions"
public List<UserData> GetAllUsers()
{
var result = _userData.GetUsers();
return result;
}
public List<Token> GetAllTokens()
{
var result = _tokenData.GetTokens();
return result;
}
public bool GenerateUserToken()
{
var token = new Token()
{
Body = Guid.NewGuid().ToString().Substring(0, 8)
};
var result = _tokenData.CreateNewToken(token);
return result;
}
public OperationResponse ResetUserPassword(LoginModel credentials)
{
//user might have forgotten their password.
var existingUser = _userData.GetUserRecordByUserName(credentials.UserName);
if (existingUser.Id == default)
{
return new OperationResponse { Success = false, Message = "Unable to find user" };
}
var newPassword = Guid.NewGuid().ToString().Substring(0, 8);
existingUser.Password = GetHash(newPassword);
var result = _userData.SaveUserRecord(existingUser);
if (result)
{
return new OperationResponse { Success = true, Message = newPassword };
} else
{
return new OperationResponse { Success = false, Message = "Something went wrong, please try again later." };
}
}
#endregion
#region "Root User"
public bool CreateRootUserCredentials(LoginModel credentials)
{
var configFileContents = File.ReadAllText(StaticHelper.UserConfigPath);
var existingUserConfig = JsonSerializer.Deserialize<UserConfig>(configFileContents);
if (existingUserConfig is not null)
{
//create hashes of the login credentials.
var hashedUserName = GetHash(credentials.UserName);
var hashedPassword = GetHash(credentials.Password);
//copy over settings that are off limits on the settings page.
existingUserConfig.EnableAuth = true;
existingUserConfig.UserNameHash = hashedUserName;
existingUserConfig.UserPasswordHash = hashedPassword;
}
File.WriteAllText(StaticHelper.UserConfigPath, JsonSerializer.Serialize(existingUserConfig));
return true;
}
public bool DeleteRootUserCredentials() {
var configFileContents = File.ReadAllText(StaticHelper.UserConfigPath);
var existingUserConfig = JsonSerializer.Deserialize<UserConfig>(configFileContents);
if (existingUserConfig is not null)
{
//copy over settings that are off limits on the settings page.
existingUserConfig.EnableAuth = false;
existingUserConfig.UserNameHash = string.Empty;
existingUserConfig.UserPasswordHash = string.Empty;
}
File.WriteAllText(StaticHelper.UserConfigPath, JsonSerializer.Serialize(existingUserConfig));
return true;
}
private bool UserIsRoot(LoginModel credentials)
{
var configFileContents = File.ReadAllText(StaticHelper.UserConfigPath);
var existingUserConfig = JsonSerializer.Deserialize<UserConfig>(configFileContents);
if (existingUserConfig is not null)
{
//create hashes of the login credentials.
var hashedUserName = GetHash(credentials.UserName);
var hashedPassword = GetHash(credentials.Password);
//compare against stored hash.
if (hashedUserName == existingUserConfig.UserNameHash &&
hashedPassword == existingUserConfig.UserPasswordHash)
{
return true;
}
}
return false;
}
#endregion
private static string GetHash(string value)
{
StringBuilder Sb = new StringBuilder();
using (var hash = SHA256.Create())
{
Encoding enc = Encoding.UTF8;
byte[] result = hash.ComputeHash(enc.GetBytes(value));
foreach (byte b in result)
Sb.Append(b.ToString("x2"));
}
return Sb.ToString();
}
}
}

View File

@@ -1,4 +1,4 @@
using CarCareTracker.Helper; using CarCareTracker.Logic;
using CarCareTracker.Models; using CarCareTracker.Models;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.Web;
@@ -15,20 +15,20 @@ namespace CarCareTracker.Middleware
{ {
private IHttpContextAccessor _httpContext; private IHttpContextAccessor _httpContext;
private IDataProtector _dataProtector; private IDataProtector _dataProtector;
private ILoginHelper _loginHelper; private ILoginLogic _loginLogic;
private bool enableAuth; private bool enableAuth;
public Authen( public Authen(
IOptionsMonitor<AuthenticationSchemeOptions> options, IOptionsMonitor<AuthenticationSchemeOptions> options,
UrlEncoder encoder, UrlEncoder encoder,
ILoggerFactory logger, ILoggerFactory logger,
IConfiguration configuration, IConfiguration configuration,
ILoginHelper loginHelper, ILoginLogic loginLogic,
IDataProtectionProvider securityProvider, IDataProtectionProvider securityProvider,
IHttpContextAccessor httpContext) : base(options, logger, encoder) IHttpContextAccessor httpContext) : base(options, logger, encoder)
{ {
_httpContext = httpContext; _httpContext = httpContext;
_dataProtector = securityProvider.CreateProtector("login"); _dataProtector = securityProvider.CreateProtector("login");
_loginHelper = loginHelper; _loginLogic = loginLogic;
enableAuth = bool.Parse(configuration["EnableAuth"]); enableAuth = bool.Parse(configuration["EnableAuth"]);
} }
protected override async Task<AuthenticateResult> HandleAuthenticateAsync() protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
@@ -66,14 +66,18 @@ namespace CarCareTracker.Middleware
return AuthenticateResult.Fail("Invalid credentials"); return AuthenticateResult.Fail("Invalid credentials");
} else } else
{ {
var validUser = _loginHelper.ValidateUserCredentials(new LoginModel { UserName = splitString[0], Password = splitString[1] }); var userData = _loginLogic.ValidateUserCredentials(new LoginModel { UserName = splitString[0], Password = splitString[1] });
if (validUser) if (userData.Id != default)
{ {
var appIdentity = new ClaimsIdentity("Custom"); var appIdentity = new ClaimsIdentity("Custom");
var userIdentity = new List<Claim> var userIdentity = new List<Claim>
{ {
new(ClaimTypes.Name, splitString[0]) new(ClaimTypes.Name, splitString[0])
}; };
if (userData.IsAdmin)
{
userIdentity.Add(new(ClaimTypes.Role, nameof(UserData.IsAdmin)));
}
appIdentity.AddClaims(userIdentity); appIdentity.AddClaims(userIdentity);
AuthenticationTicket ticket = new AuthenticationTicket(new ClaimsPrincipal(appIdentity), this.Scheme.Name); AuthenticationTicket ticket = new AuthenticationTicket(new ClaimsPrincipal(appIdentity), this.Scheme.Name);
return AuthenticateResult.Success(ticket); return AuthenticateResult.Success(ticket);
@@ -82,33 +86,44 @@ namespace CarCareTracker.Middleware
} }
else if (!string.IsNullOrWhiteSpace(access_token)) else if (!string.IsNullOrWhiteSpace(access_token))
{ {
//decrypt the access token. try
var decryptedCookie = _dataProtector.Unprotect(access_token);
AuthCookie authCookie = JsonSerializer.Deserialize<AuthCookie>(decryptedCookie);
if (authCookie != null)
{ {
//validate auth cookie //decrypt the access token.
if (authCookie.ExpiresOn < DateTime.Now) var decryptedCookie = _dataProtector.Unprotect(access_token);
AuthCookie authCookie = JsonSerializer.Deserialize<AuthCookie>(decryptedCookie);
if (authCookie != null)
{ {
//if cookie is expired //validate auth cookie
return AuthenticateResult.Fail("Expired credentials"); if (authCookie.ExpiresOn < DateTime.Now)
}
else if (authCookie.Id == default || string.IsNullOrWhiteSpace(authCookie.UserName))
{
return AuthenticateResult.Fail("Corrupted credentials");
}
else
{
var appIdentity = new ClaimsIdentity("Custom");
var userIdentity = new List<Claim>
{ {
new(ClaimTypes.Name, authCookie.UserName) //if cookie is expired
return AuthenticateResult.Fail("Expired credentials");
}
else if (authCookie.UserData.Id == default || string.IsNullOrWhiteSpace(authCookie.UserData.UserName))
{
return AuthenticateResult.Fail("Corrupted credentials");
}
else
{
var appIdentity = new ClaimsIdentity("Custom");
var userIdentity = new List<Claim>
{
new(ClaimTypes.Name, authCookie.UserData.UserName)
}; };
appIdentity.AddClaims(userIdentity); if (authCookie.UserData.IsAdmin)
AuthenticationTicket ticket = new AuthenticationTicket(new ClaimsPrincipal(appIdentity), this.Scheme.Name); {
return AuthenticateResult.Success(ticket); userIdentity.Add(new(ClaimTypes.Role, nameof(UserData.IsAdmin)));
}
appIdentity.AddClaims(userIdentity);
AuthenticationTicket ticket = new AuthenticationTicket(new ClaimsPrincipal(appIdentity), this.Scheme.Name);
return AuthenticateResult.Success(ticket);
}
} }
} }
catch (Exception ex)
{
return AuthenticateResult.Fail("Corrupted credentials");
}
} }
return AuthenticateResult.Fail("Invalid credentials"); return AuthenticateResult.Fail("Invalid credentials");
} }

View File

@@ -0,0 +1,8 @@
namespace CarCareTracker.Models
{
public class AdminViewModel
{
public List<UserData> Users { get; set; }
public List<Token> Tokens { get; set; }
}
}

View File

@@ -2,8 +2,7 @@
{ {
public class AuthCookie public class AuthCookie
{ {
public int Id { get; set; } public UserData UserData { get; set; }
public string UserName { get; set; }
public DateTime ExpiresOn { get; set; } public DateTime ExpiresOn { get; set; }
} }
} }

View File

@@ -4,6 +4,7 @@
{ {
public string UserName { get; set; } public string UserName { get; set; }
public string Password { get; set; } public string Password { get; set; }
public string Token { get; set; }
public bool IsPersistent { get; set; } = false; public bool IsPersistent { get; set; } = false;
} }
} }

8
Models/Login/Token.cs Normal file
View File

@@ -0,0 +1,8 @@
namespace CarCareTracker.Models
{
public class Token
{
public int Id { get; set; }
public string Body { get; set; }
}
}

10
Models/Login/UserData.cs Normal file
View File

@@ -0,0 +1,10 @@
namespace CarCareTracker.Models
{
public class UserData
{
public int Id { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public bool IsAdmin { get; set; }
}
}

View File

@@ -0,0 +1,8 @@
namespace CarCareTracker.Models
{
public class OperationResponse
{
public bool Success { get; set; }
public string Message { get; set; }
}
}

View File

@@ -1,6 +1,7 @@
using CarCareTracker.External.Implementations; using CarCareTracker.External.Implementations;
using CarCareTracker.External.Interfaces; using CarCareTracker.External.Interfaces;
using CarCareTracker.Helper; using CarCareTracker.Helper;
using CarCareTracker.Logic;
using CarCareTracker.Middleware; using CarCareTracker.Middleware;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
@@ -17,14 +18,18 @@ builder.Services.AddSingleton<ICollisionRecordDataAccess, CollisionRecordDataAcc
builder.Services.AddSingleton<ITaxRecordDataAccess, TaxRecordDataAccess>(); builder.Services.AddSingleton<ITaxRecordDataAccess, TaxRecordDataAccess>();
builder.Services.AddSingleton<IReminderRecordDataAccess, ReminderRecordDataAccess>(); builder.Services.AddSingleton<IReminderRecordDataAccess, ReminderRecordDataAccess>();
builder.Services.AddSingleton<IUpgradeRecordDataAccess, UpgradeRecordDataAccess>(); builder.Services.AddSingleton<IUpgradeRecordDataAccess, UpgradeRecordDataAccess>();
builder.Services.AddSingleton<IUserRecordDataAccess, UserRecordDataAccess>();
builder.Services.AddSingleton<ITokenRecordDataAccess, TokenRecordDataAccess>();
//configure helpers //configure helpers
builder.Services.AddSingleton<IFileHelper, FileHelper>(); builder.Services.AddSingleton<IFileHelper, FileHelper>();
builder.Services.AddSingleton<IGasHelper, GasHelper>(); builder.Services.AddSingleton<IGasHelper, GasHelper>();
builder.Services.AddSingleton<IReminderHelper, ReminderHelper>(); builder.Services.AddSingleton<IReminderHelper, ReminderHelper>();
builder.Services.AddSingleton<ILoginHelper, LoginHelper>();
builder.Services.AddSingleton<IReportHelper, ReportHelper>(); builder.Services.AddSingleton<IReportHelper, ReportHelper>();
//configur logic
builder.Services.AddSingleton<ILoginLogic, LoginLogic>();
if (!Directory.Exists("data")) if (!Directory.Exists("data"))
{ {
Directory.CreateDirectory("data"); Directory.CreateDirectory("data");

62
Views/Admin/Index.cshtml Normal file
View File

@@ -0,0 +1,62 @@
@model AdminViewModel
<div class="container">
<div class="row">
<div class="col-3">
<div class="row">
<button onclick="generateNewToken()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>Generate User Token</button>
</div>
<table class="table table-hover">
<thead>
<tr class="d-flex">
<th scope="col" class="col-8">Token</th>
<th scope="col" class="col-4">Delete</th>
</tr>
</thead>
<tbody>
@foreach (Token token in Model.Tokens)
{
<tr class="d-flex" style="cursor:pointer;">
<td class="col-8">@token.Body</td>
<td class="col-4">@token.Id</td>
</tr>
}
</tbody>
</table>
</div>
<div class="col-9">
<table class="table table-hover">
<thead>
<tr class="d-flex">
<th scope="col" class="col-4">UserName</th>
<th scope="col" class="col-2">Is Admin</th>
<th scope="col" class="col-4">Reset Password</th>
<th scope="col" class="col-2">Delete</th>
</tr>
</thead>
<tbody>
@foreach (UserData userData in Model.Users)
{
<tr class="d-flex" style="cursor:pointer;">
<td class="col-4">@userData.UserName</td>
<td class="col-2">@userData.Id</td>
<td class="col-4">@userData.Id</td>
<td class="col-2">@userData.Id</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
<script>
function reloadPage() {
window.location.reload();
}
function generateNewToken(){
$.get('/Admin/GenerateNewToken', function (data) {
if (data) {
reloadPage();
}
});
}
</script>

View File

@@ -23,6 +23,9 @@
<div class="d-grid"> <div class="d-grid">
<button type="button" class="btn btn-warning mt-2" onclick="performLogin()"><i class="bi bi-box-arrow-in-right me-2"></i>Login</button> <button type="button" class="btn btn-warning mt-2" onclick="performLogin()"><i class="bi bi-box-arrow-in-right me-2"></i>Login</button>
</div> </div>
<div class="d-grid">
<a href="/Login/Registration" class="btn btn-link mt-2">Register</a>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,31 @@
@{
ViewData["Title"] = "LubeLogger - Register";
}
@section Scripts {
<script src="~/js/login.js" asp-append-version="true"></script>
}
<div class="container d-flex align-items-center justify-content-center" style="height:100vh">
<div class="row">
<div class="col-12">
<img src="/defaults/lubelogger_logo.png" />
<div class="form-group">
<label for="inputToken">Token</label>
<input type="text" id="inputToken" class="form-control">
</div>
<div class="form-group">
<label for="inputUserName">Username</label>
<input type="text" id="inputUserName" class="form-control">
</div>
<div class="form-group">
<label for="inputUserPassword">Password</label>
<input type="password" id="inputUserPassword" onkeyup="handlePasswordKeyPress(event)" class="form-control">
</div>
<div class="d-grid">
<button type="button" class="btn btn-warning mt-2" onclick="performRegistration()"><i class="bi bi-box-arrow-in-right me-2"></i>Register</button>
</div>
<div class="d-grid">
<a href="/Login/Index" class="btn btn-link mt-2">Back to Login</a>
</div>
</div>
</div>
</div>

View File

@@ -10,6 +10,19 @@
} }
}) })
} }
function performRegistration() {
var token = $("#inputToken").val();
var userName = $("#inputUserName").val();
var userPassword = $("#inputUserPassword").val();
$.post('/Login/Register', { userName: userName, password: userPassword, token: token }, function (data) {
if (data.success) {
successToast(data.message);
setTimeout(function () { window.location.href = '/Login/Index' }, 500);
} else {
errorToast(data.message);
}
});
}
function handlePasswordKeyPress(event) { function handlePasswordKeyPress(event) {
if (event.keyCode == 13) { if (event.keyCode == 13) {
performLogin(); performLogin();