make email address required for token generation and user registration.

This commit is contained in:
DESKTOP-GENO133\IvanPlex
2024-01-12 23:54:12 -07:00
parent 8815009b04
commit 03b89786ec
13 changed files with 119 additions and 24 deletions

View File

@@ -22,9 +22,14 @@ namespace CarCareTracker.Controllers
}; };
return View(viewModel); return View(viewModel);
} }
public IActionResult GenerateNewToken() public IActionResult GenerateNewToken(string emailAddress)
{ {
var result = _loginLogic.GenerateUserToken(); var result = _loginLogic.GenerateUserToken(emailAddress);
return Json(result);
}
public IActionResult DeleteToken(int tokenId)
{
var result = _loginLogic.DeleteUserToken(tokenId);
return Json(result); return Json(result);
} }
} }

View File

@@ -26,6 +26,15 @@ namespace CarCareTracker.External.Implementations
return tokenRecord ?? new Token(); return tokenRecord ?? new Token();
}; };
} }
public Token GetTokenRecordByEmailAddress(string emailAddress)
{
using (var db = new LiteDatabase(dbName))
{
var table = db.GetCollection<Token>(tableName);
var tokenRecord = table.FindOne(Query.EQ(nameof(Token.EmailAddress), emailAddress));
return tokenRecord ?? new Token();
};
}
public bool CreateNewToken(Token token) public bool CreateNewToken(Token token)
{ {
using (var db = new LiteDatabase(dbName)) using (var db = new LiteDatabase(dbName))

View File

@@ -26,6 +26,15 @@ namespace CarCareTracker.External.Implementations
return userRecord ?? new UserData(); return userRecord ?? new UserData();
}; };
} }
public UserData GetUserRecordByEmailAddress(string emailAddress)
{
using (var db = new LiteDatabase(dbName))
{
var table = db.GetCollection<UserData>(tableName);
var userRecord = table.FindOne(Query.EQ(nameof(UserData.EmailAddress), emailAddress));
return userRecord ?? new UserData();
};
}
public UserData GetUserRecordById(int userId) public UserData GetUserRecordById(int userId)
{ {
using (var db = new LiteDatabase(dbName)) using (var db = new LiteDatabase(dbName))

View File

@@ -6,6 +6,7 @@ namespace CarCareTracker.External.Interfaces
{ {
public List<Token> GetTokens(); public List<Token> GetTokens();
public Token GetTokenRecordByBody(string tokenBody); public Token GetTokenRecordByBody(string tokenBody);
public Token GetTokenRecordByEmailAddress(string emailAddress);
public bool CreateNewToken(Token token); public bool CreateNewToken(Token token);
public bool DeleteToken(int tokenId); public bool DeleteToken(int tokenId);
} }

View File

@@ -6,6 +6,7 @@ namespace CarCareTracker.External.Interfaces
{ {
public List<UserData> GetUsers(); public List<UserData> GetUsers();
public UserData GetUserRecordByUserName(string userName); public UserData GetUserRecordByUserName(string userName);
public UserData GetUserRecordByEmailAddress(string emailAddress);
public UserData GetUserRecordById(int userId); public UserData GetUserRecordById(int userId);
public bool SaveUserRecord(UserData userRecord); public bool SaveUserRecord(UserData userRecord);
public bool DeleteUserRecord(int userId); public bool DeleteUserRecord(int userId);

View File

@@ -10,7 +10,8 @@ namespace CarCareTracker.Logic
{ {
public interface ILoginLogic public interface ILoginLogic
{ {
bool GenerateUserToken(); bool GenerateUserToken(string emailAddress);
bool DeleteUserToken(int tokenId);
OperationResponse RegisterNewUser(LoginModel credentials); OperationResponse RegisterNewUser(LoginModel credentials);
OperationResponse ResetUserPassword(LoginModel credentials); OperationResponse ResetUserPassword(LoginModel credentials);
UserData ValidateUserCredentials(LoginModel credentials); UserData ValidateUserCredentials(LoginModel credentials);
@@ -33,12 +34,12 @@ namespace CarCareTracker.Logic
{ {
//validate their token. //validate their token.
var existingToken = _tokenData.GetTokenRecordByBody(credentials.Token); var existingToken = _tokenData.GetTokenRecordByBody(credentials.Token);
if (existingToken.Id == default) if (existingToken.Id == default || existingToken.EmailAddress != credentials.EmailAddress)
{ {
return new OperationResponse { Success = false, Message = "Invalid Token" }; return new OperationResponse { Success = false, Message = "Invalid Token" };
} }
//token is valid, check if username and password is acceptable and that username is unique. //token is valid, check if username and password is acceptable and that username is unique.
if (string.IsNullOrWhiteSpace(credentials.UserName) || string.IsNullOrWhiteSpace(credentials.Password)) if (string.IsNullOrWhiteSpace(credentials.EmailAddress) || string.IsNullOrWhiteSpace(credentials.UserName) || string.IsNullOrWhiteSpace(credentials.Password))
{ {
return new OperationResponse { Success = false, Message = "Neither username nor password can be blank" }; return new OperationResponse { Success = false, Message = "Neither username nor password can be blank" };
} }
@@ -47,6 +48,11 @@ namespace CarCareTracker.Logic
{ {
return new OperationResponse { Success = false, Message = "Username already taken" }; return new OperationResponse { Success = false, Message = "Username already taken" };
} }
var existingUserWithEmail = _userData.GetUserRecordByEmailAddress(credentials.EmailAddress);
if (existingUserWithEmail.Id != default)
{
return new OperationResponse { Success = false, Message = "A user with that email already exists" };
}
//username is unique then we delete the token and create the user. //username is unique then we delete the token and create the user.
_tokenData.DeleteToken(existingToken.Id); _tokenData.DeleteToken(existingToken.Id);
var newUser = new UserData() var newUser = new UserData()
@@ -58,7 +64,8 @@ namespace CarCareTracker.Logic
if (result) if (result)
{ {
return new OperationResponse { Success = true, Message = "You will be redirected to the login page briefly." }; return new OperationResponse { Success = true, Message = "You will be redirected to the login page briefly." };
} else }
else
{ {
return new OperationResponse { Success = false, Message = "Something went wrong, please try again later." }; return new OperationResponse { Success = false, Message = "Something went wrong, please try again later." };
} }
@@ -78,7 +85,8 @@ namespace CarCareTracker.Logic
UserName = credentials.UserName, UserName = credentials.UserName,
IsAdmin = true IsAdmin = true
}; };
} else }
else
{ {
//authenticate via DB. //authenticate via DB.
var result = _userData.GetUserRecordByUserName(credentials.UserName); var result = _userData.GetUserRecordByUserName(credentials.UserName);
@@ -86,7 +94,8 @@ namespace CarCareTracker.Logic
{ {
result.Password = string.Empty; result.Password = string.Empty;
return result; return result;
} else }
else
{ {
return new UserData(); return new UserData();
} }
@@ -103,15 +112,27 @@ namespace CarCareTracker.Logic
var result = _tokenData.GetTokens(); var result = _tokenData.GetTokens();
return result; return result;
} }
public bool GenerateUserToken() public bool GenerateUserToken(string emailAddress)
{ {
//check if email address already has a token attached to it.
var existingToken = _tokenData.GetTokenRecordByEmailAddress(emailAddress);
if (existingToken.Id != default)
{
return false;
}
var token = new Token() var token = new Token()
{ {
Body = Guid.NewGuid().ToString().Substring(0, 8) Body = Guid.NewGuid().ToString().Substring(0, 8),
EmailAddress = emailAddress
}; };
var result = _tokenData.CreateNewToken(token); var result = _tokenData.CreateNewToken(token);
return result; return result;
} }
public bool DeleteUserToken(int tokenId)
{
var result = _tokenData.DeleteToken(tokenId);
return result;
}
public OperationResponse ResetUserPassword(LoginModel credentials) public OperationResponse ResetUserPassword(LoginModel credentials)
{ {
//user might have forgotten their password. //user might have forgotten their password.
@@ -126,7 +147,8 @@ namespace CarCareTracker.Logic
if (result) if (result)
{ {
return new OperationResponse { Success = true, Message = newPassword }; return new OperationResponse { Success = true, Message = newPassword };
} else }
else
{ {
return new OperationResponse { Success = false, Message = "Something went wrong, please try again later." }; return new OperationResponse { Success = false, Message = "Something went wrong, please try again later." };
} }
@@ -150,7 +172,8 @@ namespace CarCareTracker.Logic
File.WriteAllText(StaticHelper.UserConfigPath, JsonSerializer.Serialize(existingUserConfig)); File.WriteAllText(StaticHelper.UserConfigPath, JsonSerializer.Serialize(existingUserConfig));
return true; return true;
} }
public bool DeleteRootUserCredentials() { public bool DeleteRootUserCredentials()
{
var configFileContents = File.ReadAllText(StaticHelper.UserConfigPath); var configFileContents = File.ReadAllText(StaticHelper.UserConfigPath);
var existingUserConfig = JsonSerializer.Deserialize<UserConfig>(configFileContents); var existingUserConfig = JsonSerializer.Deserialize<UserConfig>(configFileContents);
if (existingUserConfig is not null) if (existingUserConfig is not null)

View File

@@ -99,7 +99,7 @@ namespace CarCareTracker.Middleware
//if cookie is expired //if cookie is expired
return AuthenticateResult.Fail("Expired credentials"); return AuthenticateResult.Fail("Expired credentials");
} }
else if (authCookie.UserData.Id == default || string.IsNullOrWhiteSpace(authCookie.UserData.UserName)) else if (authCookie.UserData is null || authCookie.UserData.Id == default || string.IsNullOrWhiteSpace(authCookie.UserData.UserName))
{ {
return AuthenticateResult.Fail("Corrupted credentials"); return AuthenticateResult.Fail("Corrupted credentials");
} }

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 EmailAddress { get; set; }
public string Token { get; set; } public string Token { get; set; }
public bool IsPersistent { get; set; } = false; public bool IsPersistent { get; set; } = false;
} }

View File

@@ -4,5 +4,6 @@
{ {
public int Id { get; set; } public int Id { get; set; }
public string Body { get; set; } public string Body { get; set; }
public string EmailAddress { get; set; }
} }
} }

View File

@@ -4,6 +4,7 @@
{ {
public int Id { get; set; } public int Id { get; set; }
public string UserName { get; set; } public string UserName { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; } public string Password { get; set; }
public bool IsAdmin { get; set; } public bool IsAdmin { get; set; }
} }

View File

@@ -1,29 +1,36 @@
@model AdminViewModel @{
ViewData["Title"] = "Admin";
}
@model AdminViewModel
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-3"> <div class="col-5">
<div class="row"> <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> <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> </div>
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr class="d-flex"> <tr class="d-flex">
<th scope="col" class="col-8">Token</th> <th scope="col" class="col-4">Token</th>
<th scope="col" class="col-4">Delete</th> <th scope="col" class="col-6">Issued To</th>
<th scope="col" class="col-2">Delete</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@foreach (Token token in Model.Tokens) @foreach (Token token in Model.Tokens)
{ {
<tr class="d-flex" style="cursor:pointer;"> <tr class="d-flex">
<td class="col-8">@token.Body</td> <td class="col-4" style="cursor:pointer;" onclick="copyToClipboard(this)">@token.Body</td>
<td class="col-4">@token.Id</td> <td class="col-6 text-truncate">@token.EmailAddress</td>
<td class="col-2">
<button type="button" class="btn btn-danger" onclick="deleteToken(@token.Id, this)"><i class="bi bi-trash"></i></button>
</td>
</tr> </tr>
} }
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="col-9"> <div class="col-7">
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr class="d-flex"> <tr class="d-flex">
@@ -52,11 +59,43 @@
function reloadPage() { function reloadPage() {
window.location.reload(); window.location.reload();
} }
function generateNewToken(){ function deleteToken(tokenId) {
$.get('/Admin/GenerateNewToken', function (data) { $.post(`/Admin/DeleteToken?tokenId=${tokenId}`, function (data) {
if (data) { if (data) {
reloadPage(); reloadPage();
} }
}); });
} }
function copyToClipboard(e) {
var textToCopy = e.textContent;
navigator.clipboard.writeText(textToCopy);
successToast("Copied to Clipboard");
}
function generateNewToken(){
Swal.fire({
title: 'Generate Token',
html: `
<input type="text" id="inputEmail" class="swal2-input" placeholder="Email Address">
`,
confirmButtonText: 'Generate',
focusConfirm: false,
preConfirm: () => {
const emailAddress = $("#inputEmail").val();
if (!emailAddress) {
Swal.showValidationMessage(`Please enter an email address`)
}
return { emailAddress }
},
}).then(function (result) {
if (result.isConfirmed) {
$.get('/Admin/GenerateNewToken', {emailAddress: result.value.emailAddress}, function (data) {
if (data) {
reloadPage();
} else {
errorToast("An error occurred, make sure the email address doesn't already have a token generated for it.")
}
});
}
});
}
</script> </script>

View File

@@ -12,6 +12,10 @@
<label for="inputToken">Token</label> <label for="inputToken">Token</label>
<input type="text" id="inputToken" class="form-control"> <input type="text" id="inputToken" class="form-control">
</div> </div>
<div class="form-group">
<label for="inputUserName">Email Address</label>
<input type="text" id="inputEmail" class="form-control">
</div>
<div class="form-group"> <div class="form-group">
<label for="inputUserName">Username</label> <label for="inputUserName">Username</label>
<input type="text" id="inputUserName" class="form-control"> <input type="text" id="inputUserName" class="form-control">

View File

@@ -14,7 +14,8 @@ function performRegistration() {
var token = $("#inputToken").val(); var token = $("#inputToken").val();
var userName = $("#inputUserName").val(); var userName = $("#inputUserName").val();
var userPassword = $("#inputUserPassword").val(); var userPassword = $("#inputUserPassword").val();
$.post('/Login/Register', { userName: userName, password: userPassword, token: token }, function (data) { var userEmail = $("#inputEmail").val();
$.post('/Login/Register', { userName: userName, password: userPassword, token: token, emailAddress: userEmail }, function (data) {
if (data.success) { if (data.success) {
successToast(data.message); successToast(data.message);
setTimeout(function () { window.location.href = '/Login/Index' }, 500); setTimeout(function () { window.location.href = '/Login/Index' }, 500);