make email address required for token generation and user registration.
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
1
External/Interfaces/IUserRecordDataAccess.cs
vendored
1
External/Interfaces/IUserRecordDataAccess.cs
vendored
@@ -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);
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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">
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user