From 8815009b04eea472b7abcfbb2ab1cb8d660c27bd Mon Sep 17 00:00:00 2001 From: "DESKTOP-T0O5CDB\\DESK-555BD" Date: Fri, 12 Jan 2024 18:37:51 -0700 Subject: [PATCH] added token based registration. --- Controllers/AdminController.cs | 31 +++ Controllers/LoginController.cs | 74 +++---- .../Implementations/TokenRecordDataAccess.cs | 48 +++++ .../Implementations/UserRecordDataAccess.cs | 57 +++++ External/Interfaces/ITokenRecordDataAccess.cs | 12 ++ External/Interfaces/IUserRecordDataAccess.cs | 13 ++ Helper/LoginHelper.cs | 47 ---- Logic/LoginLogic.cs | 201 ++++++++++++++++++ Middleware/Authen.cs | 69 +++--- Models/Admin/AdminViewModel.cs | 8 + Models/Login/AuthCookie.cs | 3 +- Models/Login/LoginModel.cs | 1 + Models/Login/Token.cs | 8 + Models/Login/UserData.cs | 10 + Models/OperationResponse.cs | 8 + Program.cs | 7 +- Views/Admin/Index.cshtml | 62 ++++++ Views/Login/Index.cshtml | 3 + Views/Login/Registration.cshtml | 31 +++ wwwroot/js/login.js | 13 ++ 20 files changed, 581 insertions(+), 125 deletions(-) create mode 100644 Controllers/AdminController.cs create mode 100644 External/Implementations/TokenRecordDataAccess.cs create mode 100644 External/Implementations/UserRecordDataAccess.cs create mode 100644 External/Interfaces/ITokenRecordDataAccess.cs create mode 100644 External/Interfaces/IUserRecordDataAccess.cs delete mode 100644 Helper/LoginHelper.cs create mode 100644 Logic/LoginLogic.cs create mode 100644 Models/Admin/AdminViewModel.cs create mode 100644 Models/Login/Token.cs create mode 100644 Models/Login/UserData.cs create mode 100644 Models/OperationResponse.cs create mode 100644 Views/Admin/Index.cshtml create mode 100644 Views/Login/Registration.cshtml diff --git a/Controllers/AdminController.cs b/Controllers/AdminController.cs new file mode 100644 index 0000000..be5dbf4 --- /dev/null +++ b/Controllers/AdminController.cs @@ -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); + } + } +} diff --git a/Controllers/LoginController.cs b/Controllers/LoginController.cs index fc9733e..97a0840 100644 --- a/Controllers/LoginController.cs +++ b/Controllers/LoginController.cs @@ -1,4 +1,5 @@ using CarCareTracker.Helper; +using CarCareTracker.Logic; using CarCareTracker.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.DataProtection; @@ -13,22 +14,26 @@ namespace CarCareTracker.Controllers public class LoginController : Controller { private IDataProtector _dataProtector; - private ILoginHelper _loginHelper; + private ILoginLogic _loginLogic; private readonly ILogger _logger; public LoginController( ILogger logger, IDataProtectionProvider securityProvider, - ILoginHelper loginHelper + ILoginLogic loginLogic ) { _dataProtector = securityProvider.CreateProtector("login"); _logger = logger; - _loginHelper = loginHelper; + _loginLogic = loginLogic; } public IActionResult Index() { return View(); } + public IActionResult Registration() + { + return View(); + } [HttpPost] public IActionResult Login(LoginModel credentials) { @@ -40,13 +45,12 @@ namespace CarCareTracker.Controllers //compare it against hashed credentials try { - var loginIsValid = _loginHelper.ValidateUserCredentials(credentials); - if (loginIsValid) + var userData = _loginLogic.ValidateUserCredentials(credentials); + if (userData.Id != default) { AuthCookie authCookie = new AuthCookie { - Id = 1, //this is hardcoded for now - UserName = credentials.UserName, + UserData = userData, ExpiresOn = DateTime.Now.AddDays(credentials.IsPersistent ? 30 : 1) }; var serializedCookie = JsonSerializer.Serialize(authCookie); @@ -61,26 +65,21 @@ namespace CarCareTracker.Controllers } 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. [HttpPost] public IActionResult CreateLoginCreds(LoginModel credentials) { try { - var configFileContents = System.IO.File.ReadAllText(StaticHelper.UserConfigPath); - var existingUserConfig = JsonSerializer.Deserialize(configFileContents); - 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); + var result = _loginLogic.CreateRootUserCredentials(credentials); + return Json(result); } catch (Exception ex) { @@ -94,19 +93,13 @@ namespace CarCareTracker.Controllers { try { - var configFileContents = System.IO.File.ReadAllText(StaticHelper.UserConfigPath); - var existingUserConfig = JsonSerializer.Deserialize(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)); + var result = _loginLogic.DeleteRootUserCredentials(); //destroy any login cookies. - Response.Cookies.Delete("ACCESS_TOKEN"); - return Json(true); + if (result) + { + Response.Cookies.Delete("ACCESS_TOKEN"); + } + return Json(result); } catch (Exception ex) { @@ -121,20 +114,5 @@ namespace CarCareTracker.Controllers Response.Cookies.Delete("ACCESS_TOKEN"); 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(); - } } } diff --git a/External/Implementations/TokenRecordDataAccess.cs b/External/Implementations/TokenRecordDataAccess.cs new file mode 100644 index 0000000..289a1c0 --- /dev/null +++ b/External/Implementations/TokenRecordDataAccess.cs @@ -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 GetTokens() + { + using (var db = new LiteDatabase(dbName)) + { + var table = db.GetCollection(tableName); + return table.FindAll().ToList(); + }; + } + public Token GetTokenRecordByBody(string tokenBody) + { + using (var db = new LiteDatabase(dbName)) + { + var table = db.GetCollection(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(tableName); + table.Insert(token); + return true; + }; + } + public bool DeleteToken(int tokenId) + { + using (var db = new LiteDatabase(dbName)) + { + var table = db.GetCollection(tableName); + table.Delete(tokenId); + return true; + }; + } + } +} \ No newline at end of file diff --git a/External/Implementations/UserRecordDataAccess.cs b/External/Implementations/UserRecordDataAccess.cs new file mode 100644 index 0000000..1586389 --- /dev/null +++ b/External/Implementations/UserRecordDataAccess.cs @@ -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 GetUsers() + { + using (var db = new LiteDatabase(dbName)) + { + var table = db.GetCollection(tableName); + return table.FindAll().ToList(); + }; + } + public UserData GetUserRecordByUserName(string userName) + { + using (var db = new LiteDatabase(dbName)) + { + var table = db.GetCollection(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(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(tableName); + table.Upsert(userRecord); + return true; + }; + } + public bool DeleteUserRecord(int userId) + { + using (var db = new LiteDatabase(dbName)) + { + var table = db.GetCollection(tableName); + table.Delete(userId); + return true; + }; + } + } +} \ No newline at end of file diff --git a/External/Interfaces/ITokenRecordDataAccess.cs b/External/Interfaces/ITokenRecordDataAccess.cs new file mode 100644 index 0000000..231fa5c --- /dev/null +++ b/External/Interfaces/ITokenRecordDataAccess.cs @@ -0,0 +1,12 @@ +using CarCareTracker.Models; + +namespace CarCareTracker.External.Interfaces +{ + public interface ITokenRecordDataAccess + { + public List GetTokens(); + public Token GetTokenRecordByBody(string tokenBody); + public bool CreateNewToken(Token token); + public bool DeleteToken(int tokenId); + } +} diff --git a/External/Interfaces/IUserRecordDataAccess.cs b/External/Interfaces/IUserRecordDataAccess.cs new file mode 100644 index 0000000..3ba8ae9 --- /dev/null +++ b/External/Interfaces/IUserRecordDataAccess.cs @@ -0,0 +1,13 @@ +using CarCareTracker.Models; + +namespace CarCareTracker.External.Interfaces +{ + public interface IUserRecordDataAccess + { + public List GetUsers(); + public UserData GetUserRecordByUserName(string userName); + public UserData GetUserRecordById(int userId); + public bool SaveUserRecord(UserData userRecord); + public bool DeleteUserRecord(int userId); + } +} \ No newline at end of file diff --git a/Helper/LoginHelper.cs b/Helper/LoginHelper.cs deleted file mode 100644 index a43b7c6..0000000 --- a/Helper/LoginHelper.cs +++ /dev/null @@ -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(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(); - } - } -} diff --git a/Logic/LoginLogic.cs b/Logic/LoginLogic.cs new file mode 100644 index 0000000..b2aa44f --- /dev/null +++ b/Logic/LoginLogic.cs @@ -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 GetAllUsers(); + List 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." }; + } + } + /// + /// Returns an empty user if can't auth against neither root nor db user. + /// + /// credentials from login page + /// + 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 GetAllUsers() + { + var result = _userData.GetUsers(); + return result; + } + public List 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(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(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(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(); + } + } +} diff --git a/Middleware/Authen.cs b/Middleware/Authen.cs index fb10b03..b486b59 100644 --- a/Middleware/Authen.cs +++ b/Middleware/Authen.cs @@ -1,4 +1,4 @@ -using CarCareTracker.Helper; +using CarCareTracker.Logic; using CarCareTracker.Models; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Components.Web; @@ -15,20 +15,20 @@ namespace CarCareTracker.Middleware { private IHttpContextAccessor _httpContext; private IDataProtector _dataProtector; - private ILoginHelper _loginHelper; + private ILoginLogic _loginLogic; private bool enableAuth; public Authen( IOptionsMonitor options, UrlEncoder encoder, ILoggerFactory logger, IConfiguration configuration, - ILoginHelper loginHelper, + ILoginLogic loginLogic, IDataProtectionProvider securityProvider, IHttpContextAccessor httpContext) : base(options, logger, encoder) { _httpContext = httpContext; _dataProtector = securityProvider.CreateProtector("login"); - _loginHelper = loginHelper; + _loginLogic = loginLogic; enableAuth = bool.Parse(configuration["EnableAuth"]); } protected override async Task HandleAuthenticateAsync() @@ -66,14 +66,18 @@ namespace CarCareTracker.Middleware return AuthenticateResult.Fail("Invalid credentials"); } else { - var validUser = _loginHelper.ValidateUserCredentials(new LoginModel { UserName = splitString[0], Password = splitString[1] }); - if (validUser) + var userData = _loginLogic.ValidateUserCredentials(new LoginModel { UserName = splitString[0], Password = splitString[1] }); + if (userData.Id != default) { var appIdentity = new ClaimsIdentity("Custom"); var userIdentity = new List { new(ClaimTypes.Name, splitString[0]) }; + if (userData.IsAdmin) + { + 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); @@ -82,33 +86,44 @@ namespace CarCareTracker.Middleware } else if (!string.IsNullOrWhiteSpace(access_token)) { - //decrypt the access token. - var decryptedCookie = _dataProtector.Unprotect(access_token); - AuthCookie authCookie = JsonSerializer.Deserialize(decryptedCookie); - if (authCookie != null) + try { - //validate auth cookie - if (authCookie.ExpiresOn < DateTime.Now) + //decrypt the access token. + var decryptedCookie = _dataProtector.Unprotect(access_token); + AuthCookie authCookie = JsonSerializer.Deserialize(decryptedCookie); + if (authCookie != null) { - //if cookie is expired - return AuthenticateResult.Fail("Expired credentials"); - } - else if (authCookie.Id == default || string.IsNullOrWhiteSpace(authCookie.UserName)) - { - return AuthenticateResult.Fail("Corrupted credentials"); - } - else - { - var appIdentity = new ClaimsIdentity("Custom"); - var userIdentity = new List + //validate auth cookie + if (authCookie.ExpiresOn < DateTime.Now) { - 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 + { + new(ClaimTypes.Name, authCookie.UserData.UserName) }; - appIdentity.AddClaims(userIdentity); - AuthenticationTicket ticket = new AuthenticationTicket(new ClaimsPrincipal(appIdentity), this.Scheme.Name); - return AuthenticateResult.Success(ticket); + if (authCookie.UserData.IsAdmin) + { + 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"); } diff --git a/Models/Admin/AdminViewModel.cs b/Models/Admin/AdminViewModel.cs new file mode 100644 index 0000000..2aeba42 --- /dev/null +++ b/Models/Admin/AdminViewModel.cs @@ -0,0 +1,8 @@ +namespace CarCareTracker.Models +{ + public class AdminViewModel + { + public List Users { get; set; } + public List Tokens { get; set; } + } +} diff --git a/Models/Login/AuthCookie.cs b/Models/Login/AuthCookie.cs index 0a3356a..24b4d96 100644 --- a/Models/Login/AuthCookie.cs +++ b/Models/Login/AuthCookie.cs @@ -2,8 +2,7 @@ { public class AuthCookie { - public int Id { get; set; } - public string UserName { get; set; } + public UserData UserData { get; set; } public DateTime ExpiresOn { get; set; } } } diff --git a/Models/Login/LoginModel.cs b/Models/Login/LoginModel.cs index 8afa0fb..a5424ed 100644 --- a/Models/Login/LoginModel.cs +++ b/Models/Login/LoginModel.cs @@ -4,6 +4,7 @@ { public string UserName { get; set; } public string Password { get; set; } + public string Token { get; set; } public bool IsPersistent { get; set; } = false; } } diff --git a/Models/Login/Token.cs b/Models/Login/Token.cs new file mode 100644 index 0000000..1b438e1 --- /dev/null +++ b/Models/Login/Token.cs @@ -0,0 +1,8 @@ +namespace CarCareTracker.Models +{ + public class Token + { + public int Id { get; set; } + public string Body { get; set; } + } +} diff --git a/Models/Login/UserData.cs b/Models/Login/UserData.cs new file mode 100644 index 0000000..8160473 --- /dev/null +++ b/Models/Login/UserData.cs @@ -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; } + } +} \ No newline at end of file diff --git a/Models/OperationResponse.cs b/Models/OperationResponse.cs new file mode 100644 index 0000000..a5be6c7 --- /dev/null +++ b/Models/OperationResponse.cs @@ -0,0 +1,8 @@ +namespace CarCareTracker.Models +{ + public class OperationResponse + { + public bool Success { get; set; } + public string Message { get; set; } + } +} diff --git a/Program.cs b/Program.cs index e729b1c..c521fbe 100644 --- a/Program.cs +++ b/Program.cs @@ -1,6 +1,7 @@ using CarCareTracker.External.Implementations; using CarCareTracker.External.Interfaces; using CarCareTracker.Helper; +using CarCareTracker.Logic; using CarCareTracker.Middleware; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; @@ -17,14 +18,18 @@ builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); //configure helpers builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); -builder.Services.AddSingleton(); builder.Services.AddSingleton(); +//configur logic +builder.Services.AddSingleton(); + if (!Directory.Exists("data")) { Directory.CreateDirectory("data"); diff --git a/Views/Admin/Index.cshtml b/Views/Admin/Index.cshtml new file mode 100644 index 0000000..6ea13d0 --- /dev/null +++ b/Views/Admin/Index.cshtml @@ -0,0 +1,62 @@ +@model AdminViewModel +
+
+
+
+ +
+ + + + + + + + + @foreach (Token token in Model.Tokens) + { + + + + + } + +
TokenDelete
@token.Body@token.Id
+
+
+ + + + + + + + + + + @foreach (UserData userData in Model.Users) + { + + + + + + + } + +
UserNameIs AdminReset PasswordDelete
@userData.UserName@userData.Id@userData.Id@userData.Id
+
+
+
+ \ No newline at end of file diff --git a/Views/Login/Index.cshtml b/Views/Login/Index.cshtml index e7bc17e..ab462d3 100644 --- a/Views/Login/Index.cshtml +++ b/Views/Login/Index.cshtml @@ -23,6 +23,9 @@
+
+ Register +
\ No newline at end of file diff --git a/Views/Login/Registration.cshtml b/Views/Login/Registration.cshtml new file mode 100644 index 0000000..2e12031 --- /dev/null +++ b/Views/Login/Registration.cshtml @@ -0,0 +1,31 @@ +@{ + ViewData["Title"] = "LubeLogger - Register"; +} +@section Scripts { + +} +
+
+
+ +
+ + +
+
+ + +
+
+ + +
+
+ +
+ +
+
+
\ No newline at end of file diff --git a/wwwroot/js/login.js b/wwwroot/js/login.js index cd06122..b2f002e 100644 --- a/wwwroot/js/login.js +++ b/wwwroot/js/login.js @@ -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) { if (event.keyCode == 13) { performLogin();