diff --git a/ToDo.txt b/ToDo.txt index 7c36dd4..4b80527 100755 --- a/ToDo.txt +++ b/ToDo.txt @@ -28,4 +28,11 @@ Program DTO Cannot send dotnet List -> must be arrays - Need to update local server to use www-formdata instead of json \ No newline at end of file + Need to update local server to use www-formdata instead of json + +PaymentController + PaymentResponse is still tied to Stripe. + Need to change to Interface so different services can be interchanged + +ProductController + Need to figure out new way to download purchased items as there is currently no way \ No newline at end of file diff --git a/src/MistoxWebsite.Server/Controllers/AuthenticationController.cs b/src/MistoxWebsite.Server/Controllers/AuthenticationController.cs index 3d2f724..5d5e620 100755 --- a/src/MistoxWebsite.Server/Controllers/AuthenticationController.cs +++ b/src/MistoxWebsite.Server/Controllers/AuthenticationController.cs @@ -1,10 +1,10 @@ using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Mvc; -using MistoxWebsite.Shared; using System.Security.Claims; using MistoxWebsite.Server.Services; using MistoxWebsite.Server.Services.DatabaseService; -using Microsoft.AspNetCore.Authentication.Cookies; +using MistoxWebsite.Server.Entities; namespace MistoxWebsite.Server.Controllers { [ApiController] @@ -12,29 +12,27 @@ namespace MistoxWebsite.Server.Controllers { DatabaseService _accountContext; EmailService _emailContext; - - public AuthenticationController( DatabaseService DatabaseContext, EmailService emailContext ) { + + public AuthenticationController(DatabaseService DatabaseContext, EmailService emailContext) { _accountContext = DatabaseContext; _emailContext = emailContext; } - // In Account -> References UserName / PasswordHash - // Out Account - [Route( "api/account/login" )] + [Route("api/account/login")] [HttpPost] - public async Task> Login( [FromBody] Account request ) { + public async Task> Login([FromForm] string UserName, [FromForm] string PasswordHash, [FromForm] bool StayLoggedIn) { try { - Account? test = await _accountContext.GetAccount(request.UserName.ToLower()); - if( test != null ) { - if( test.EmailVerified == true ) { - if( test.SiteData.FailedPasswordLock ) { - if( test.SiteData.CurrentPasswordAttempts >= test.SiteData.PasswordAttempts ) { + Account? test = await _accountContext.GetAccount(UserName.ToLower()); + if (test != null) { + if (test.EmailVerified == true) { + if (test.SiteData.FailedPasswordLock) { + if (test.SiteData.CurrentPasswordAttempts >= test.SiteData.PasswordAttempts) { return new Account() { Error = "Too many failed password attempts. Please reset your password" }; } } - if( BCrypt.Net.BCrypt.Verify( request.PasswordHash, test.PasswordHash ) ) { + if (BCrypt.Net.BCrypt.Verify(PasswordHash, test.PasswordHash)) { test.SiteData.CurrentPasswordAttempts = 0; - await _accountContext.SetAccount( test ); + await _accountContext.SetAccount(test); AccountClaims aClaims = await getClaims(test.ID); List claims = new List() { @@ -48,64 +46,306 @@ namespace MistoxWebsite.Server.Controllers { await HttpContext.SignInAsync( CookieAuthenticationDefaults.AuthenticationScheme, - new ClaimsPrincipal( new ClaimsIdentity( claims, "Auth" ) ), + new ClaimsPrincipal(new ClaimsIdentity(claims, "Auth")), new AuthenticationProperties { ExpiresUtc = DateTime.UtcNow.AddYears(30), // Add 30 years with sliding on - IsPersistent = request.EmailVerified, // Is set from the StayLoggedIn + IsPersistent = StayLoggedIn, // Is set from the StayLoggedIn } ); return test; - } else { + } + else { test.SiteData.CurrentPasswordAttempts += 1; - await _accountContext.SetAccount( test ); + await _accountContext.SetAccount(test); return new Account() { Error = "Wrong password" }; } - }else{ - await SendVerify(test); + } + else { + await SendVerify(test.UserName); return new Account() { Error = "A new verify email has been sent. \n Note only 1 email send every 5 mintes" }; } } return new Account() { Error = "User doesn't exist" }; - } catch( Exception ex ) { + } catch (Exception ex) { return new Account() { Error = ex.Message }; } } - // In Account -> References UserName / PasswordHash - // Out Account - [Route( "api/account/session" )] + [Route("api/account/session")] [HttpPost] - public async Task> LoginSession( [FromBody] Account request ) { + public async Task> LoginSession([FromBody] Account request) { try { Account? test = await _accountContext.GetAccount(request.UserName.ToLower()); - if( test != null ) { - if( request.PasswordHash == test.PasswordHash ) { + if (test != null) { + if (request.PasswordHash == test.PasswordHash) { return test; - } else { + } + else { test.SiteData.CurrentPasswordAttempts += 1; - await _accountContext.SetAccount( test ); + await _accountContext.SetAccount(test); return new Account() { Error = "Wrong password" }; } } return new Account() { Error = "User doesn't exist" }; - } catch( Exception ex ) { + } catch (Exception ex) { return new Account() { Error = ex.Message }; } } - // In Account - // Out List - [Route( "api/account/claims" )] + [Route("api/account/claims")] [HttpPost] - public async Task> Claims( [FromBody] Account Account ) { + public async Task> Claims([FromBody] Account Account) { AccountClaims claims = await getClaims(Account.ID); return claims; } - async Task getClaims( int AccountID ) { + [Route("api/account/register")] + [HttpPost] + public async Task> Register([FromForm] string Email, [FromForm] string UserName, [FromForm] string PasswordHash) { + try { + if (await _accountContext.GetAccount(UserName.ToLower()) == null) { + if (await _accountContext.GetAccount(Email.ToLower()) == null) { + Account? created = new Account() { + UserName = UserName.ToLower(), + Email = Email.ToLower(), + EmailVerified = false, + PasswordHash = BCrypt.Net.BCrypt.HashPassword(PasswordHash), + }; + await _accountContext.NewAccount(created); + created = await _accountContext.GetAccount(Email.ToLower()); + if (created != null) { + AccountClaims aClaims = await getClaims(created.ID); + List claims = new List() { + new Claim(ClaimTypes.Name, aClaims.UserName), + new Claim(ClaimTypes.Email, aClaims.Email), + new Claim("emailverified", aClaims.EmailVerified), + new Claim(ClaimTypes.Role, aClaims.Role), + new Claim("LockAccount", aClaims.FailedPasswordLock) + }; + + await SendVerify(created.UserName); + return created; + } + return new Account() { Error = "Unknown Error" }; + } + else { + return new Account() { Error = "Email is already in use" }; + } + } + else { + return new Account() { Error = "UserName is taken" }; + } + } catch (Exception ex) { + Console.WriteLine("Error: " + ex.Message); + return new Account() { Error = ex.Message }; + } + + } + + [Route("api/account/changepassword")] + [HttpPost] + public async Task> ChangePassword([FromForm]string UserName, [FromForm]string OldPassword, [FromForm]string NewPassword) { + try { + Account? test = await _accountContext.GetAccount(UserName.ToLower()); + if (test != null) { + if (BCrypt.Net.BCrypt.Verify(OldPassword, test.PasswordHash)) { + test.PasswordHash = BCrypt.Net.BCrypt.HashPassword(NewPassword); + test.SiteData.CurrentPasswordAttempts = 0; + await _accountContext.SetAccount(test); + return true; + } + } + return false; + } catch { + return false; + } + } + + [Route("api/account/toggleAccountLock")] + [HttpPost] + public async Task> ToggleAccountLock([FromForm]string UserName, [FromForm]bool AccountLock) { + try { + Account? test = await _accountContext.GetAccount(UserName); + if (test != null) { + test.SiteData.FailedPasswordLock = AccountLock; + test.SiteData.CurrentPasswordAttempts = 0; + await _accountContext.SetAccount(test); + return "Account Lock Status Updated"; + } + return "Unknown Error Occurred"; + } catch (Exception ex) { + return ex.Message; + } + } + + [Route("api/account/get")] + [HttpPost] + public async Task> Get() { + try { + if (User.Identity != null && User.Identity.IsAuthenticated) { + string? email = User.FindFirstValue(ClaimTypes.Email); + if (!string.IsNullOrEmpty(email)) { + Account? test = await _accountContext.GetAccount(email); + if (test != null) { + return test; + } + } + } + return Ok(); + } catch { + return Ok(); + } + } + + [Route("api/account/logout")] + [HttpPost] + public async Task Logout() { + await HttpContext.SignOutAsync(); + } + + [Route("api/account/sendverifyemail")] + [HttpPost] + public async Task> SendVerify([FromForm]string UserName) { + try { + string key = "v" + UserName; + // Stop from sending multiple emails quickly + if (_emailContext._SentEmails.ContainsKey(key)) { + DateTime PreviousSentTime = _emailContext._SentEmails.GetValueOrDefault(key); + if (PreviousSentTime.AddMinutes(5) > DateTime.Now) { + return "Cannot sent another verify email until 5 minutes has elapsed "; + } + else { + _emailContext._SentEmails.Remove(key); + } + } + Account? test = await _accountContext.GetAccount(UserName.ToLower()); + if (test != null) { + test.SiteData.EmailToken = Guid.NewGuid().ToString(); + await _accountContext.SetAccount(test); + + string EmailContents = EmailService.VerifyEmailEmail; + EmailContents = Substitue(EmailContents, "@UserName", UserName); + EmailContents = Substitue(EmailContents, "@UserName", UserName); + EmailContents = Substitue(EmailContents, "@VerifyPassword", test.SiteData.EmailToken); + + string result = _emailContext.Send(test.Email, EmailService.VerifyEmailSubject, EmailContents); + _emailContext._SentEmails.Add(key, DateTime.Now); + return result; + } + return "Account not found"; + } catch (Exception) { + return "The connection couldn't be established to the email server"; + } + } + + [Route("api/account/verifyemail")] + [HttpPost] + public async Task> VerifyEmail([FromForm]string UserName, [FromForm]string EmailToken) { + try { + Account? test = await _accountContext.GetAccount(UserName.ToLower()); + if (test != null) { + if (test.SiteData.EmailToken == EmailToken) { + test.SiteData.EmailToken = ""; + test.EmailVerified = true; + await _accountContext.SetAccount(test); + return true; + } + } + return false; + } catch { + return false; + } + } + + [Route("api/account/sendresetpassword")] + [HttpPost] + public async Task> ResetPassword([FromForm] string Email) { + try { + string key = "p" + Email.ToLower(); + // Stop from sending multiple emails quickly + if (_emailContext._SentEmails.ContainsKey(key)) { + DateTime PreviousSentTime = _emailContext._SentEmails.GetValueOrDefault(key); + if (PreviousSentTime.AddMinutes(5) > DateTime.Now) { + return "Cannot sent another reset requests until 5 minutes has elapsed"; + } + else { + _emailContext._SentEmails.Remove(key); + } + } + Account? test = await _accountContext.GetAccount(Email.ToLower()); + if (test != null) { + test.SiteData.EmailToken = Guid.NewGuid().ToString(); + await _accountContext.SetAccount(test); + + string EmailContents = EmailService.ResetPasswordEmail; + EmailContents = Substitue(EmailContents, "@UserName", test.UserName); + EmailContents = Substitue(EmailContents, "@UserName", test.UserName); + EmailContents = Substitue(EmailContents, "@ResetPassWord", test.SiteData.EmailToken); + + string result = _emailContext.Send(test.Email, EmailService.VerifyEmailSubject, EmailContents); + _emailContext._SentEmails.Add(key, DateTime.Now); + return result; + } + return "Account Not Found"; + } catch (Exception) { + return "The connection couldn't be established to the email server"; + } + + } + + [Route("api/account/resetpassword")] + [HttpPost] + public async Task> ResetPwdVerify([FromForm] string UserName, [FromForm] string NewPassword, [FromForm] string ResetToken) { + try { + Account? test = await _accountContext.GetAccount(UserName.ToLower()); + if (test != null && !string.IsNullOrEmpty(test.SiteData.EmailToken)) { + if (test.SiteData.EmailToken == ResetToken) { + test.SiteData.CurrentPasswordAttempts = 0; + test.PasswordHash = BCrypt.Net.BCrypt.HashPassword(NewPassword); + await _accountContext.SetAccount(test); + return true; + } + } + return false; + } catch { + return false; + } + } + + [Route("api/account/delete")] + [HttpPost] + public async Task> delete([FromForm]string UserName, [FromForm]string Password) { + try { + Account? test = await _accountContext.GetAccount(UserName.ToLower()); + if (test != null) { + if (BCrypt.Net.BCrypt.Verify(Password, test.PasswordHash)) { + await _accountContext.DeleteAccount(test); + return true; + } + } + return false; + } catch { + return false; + } + } + + // Helper Functions + + string Substitue(string message, string subString, string Replacement) { + for (int i = 0; i < (message.Length - subString.Length); i++) { + if (message.Substring(i, subString.Length) == subString) { + string before = message.Substring(0, i); + string after = message.Substring(i + subString.Length); + return before + Replacement + after; + } + } + return message; + } + + async Task getClaims(int AccountID) { try { Account? test = await _accountContext.GetAccountByID(AccountID); - if( test != null ) { + if (test != null) { AccountClaims aClaims = new AccountClaims() { UserName = test.UserName, Email = test.Email, @@ -121,265 +361,5 @@ namespace MistoxWebsite.Server.Controllers { } } - - // In Account -> Full account - // Out Account - [Route( "api/account/register" )] - [HttpPost] - public async Task> Register( [FromBody] Account request ) { - try { - if( await _accountContext.GetAccount( request.UserName.ToLower() ) == null ) { - if( await _accountContext.GetAccount( request.Email.ToLower() ) == null ) { - Account? created = new Account(){ - UserName = request.UserName.ToLower(), - Email = request.Email.ToLower(), - EmailVerified = false, - PasswordHash = BCrypt.Net.BCrypt.HashPassword(request.PasswordHash), - }; - await _accountContext.NewAccount( created ); - created = await _accountContext.GetAccount( request.Email.ToLower() ); - if( created != null ) { - AccountClaims aClaims = await getClaims(created.ID); - List claims = new List() { - new Claim(ClaimTypes.Name, aClaims.UserName), - new Claim(ClaimTypes.Email, aClaims.Email), - new Claim("emailverified", aClaims.EmailVerified), - new Claim(ClaimTypes.Role, aClaims.Role), - new Claim("LockAccount", aClaims.FailedPasswordLock) - }; - - await SendVerify(created); - return created; - } - return new Account() { Error = "Unknown Error" }; - } else { - return new Account() { Error = "Email is already in use" }; - } - } else { - return new Account() { Error = "UserName is taken" }; - } - } catch( Exception ex ) { - Console.WriteLine("Error: " + ex.Message); - return new Account() { Error = ex.Message }; - } - - } - - // In Account -> References UserName / PasswordHash( Current Password ) / Error( New Password ) - // Out Bool - [Route( "api/account/changepassword" )] - [HttpPost] - public async Task> ChangePassword( [FromBody] Account request ) { - try { - Account? test = await _accountContext.GetAccount(request.UserName.ToLower()); - if( test != null ) { - if( BCrypt.Net.BCrypt.Verify( request.PasswordHash, test.PasswordHash ) ) { - test.PasswordHash = BCrypt.Net.BCrypt.HashPassword( request.Error ); - test.SiteData.CurrentPasswordAttempts = 0; - await _accountContext.SetAccount( test ); - return true; - } - } - return false; - } catch { - return false; - } - } - - // In Account -> References UserName / SiteData FailedPasswordLock / SiteData.PasswordAttempts - // Out Error String - [Route( "api/account/toggleAccountLock" )] - [HttpPost] - public async Task> ToggleAccountLock( [FromBody] Account request ) { - try { - Account? test = await _accountContext.GetAccount(request.UserName); - if( test != null ) { - test.SiteData.FailedPasswordLock = request.SiteData.FailedPasswordLock; - test.SiteData.CurrentPasswordAttempts = 0; - test.SiteData.PasswordAttempts = request.SiteData.PasswordAttempts; - await _accountContext.SetAccount( test ); - return "Account Lock Status Updated"; - } - return "Unknown Error Occurred"; - } catch( Exception ex ) { - return ex.Message; - } - } - - // Out Account -> only if logged in - [Route( "api/account/get" )] - [HttpPost] - public async Task> Get() { - try { - if( User.Identity != null && User.Identity.IsAuthenticated ) { - string? email = User.FindFirstValue(ClaimTypes.Email); - if( !string.IsNullOrEmpty( email ) ) { - Account? test = await _accountContext.GetAccount(email); - if( test != null ) { - return test; - } - } - } - return Ok(); - } catch { - return Ok(); - } - } - - // In Null - // Out Null - [Route( "api/account/logout" )] - [HttpPost] - public async Task Logout() { - await HttpContext.SignOutAsync(); - } - - string Substitue( string message, string subString, string Replacement ) { - for( int i = 0; i < (message.Length - subString.Length); i++ ) { - if( message.Substring( i, subString.Length ) == subString ) { - string before = message.Substring( 0, i ); - string after = message.Substring(i + subString.Length ); - return before + Replacement + after; - } - } - return message; - } - - - - // In Account -> References UserName - // Out Success bool - [Route( "api/account/sendverifyemail" )] - [HttpPost] - public async Task> SendVerify( [FromBody] Account request ) { - try { - string key = "v" + request.UserName; - // Stop from sending multiple emails quickly - if ( _emailContext._SentEmails.ContainsKey(key) ){ - DateTime PreviousSentTime = _emailContext._SentEmails.GetValueOrDefault(key); - if (PreviousSentTime.AddMinutes(5) > DateTime.Now){ - return "Cannot sent another verify email until 5 minutes has elapsed "; - }else{ - _emailContext._SentEmails.Remove(key); - } - } - Account? test = await _accountContext.GetAccount(request.UserName.ToLower()); - if( test != null ) { - test.SiteData.EmailToken = Guid.NewGuid().ToString(); - await _accountContext.SetAccount( test ); - - string EmailContents = EmailService.VerifyEmailEmail; - EmailContents = Substitue( EmailContents, "@UserName", request.UserName ); - EmailContents = Substitue( EmailContents, "@UserName", request.UserName ); - EmailContents = Substitue( EmailContents, "@VerifyPassword", test.SiteData.EmailToken ); - - string result = _emailContext.Send( test.Email, EmailService.VerifyEmailSubject, EmailContents ); - _emailContext._SentEmails.Add(key, DateTime.Now); - return result; - } - return "Account not found"; - } catch (Exception) { - return "The connection couldn't be established to the email server"; - } - } - - // In Account -> References UserName / Password( EmailToken ) - // Out Success bool - [Route( "api/account/verifyemail" )] - [HttpPost] - public async Task> VerifyEmail( [FromBody] Account request ) { - try { - Account? test = await _accountContext.GetAccount(request.UserName.ToLower()); - if( test != null ) { - if( test.SiteData.EmailToken == request.PasswordHash ) { - test.SiteData.EmailToken = ""; - test.EmailVerified = true; - await _accountContext.SetAccount( test ); - return true; - } - } - return false; - } catch { - return false; - } - } - - // In Account -> References Email - // Out Success bool - [Route( "api/account/sendresetpassword" )] - [HttpPost] - public async Task> ResetPassword( [FromBody] Account request ) { - try { - string key = "p" + request.Email.ToLower(); - // Stop from sending multiple emails quickly - if ( _emailContext._SentEmails.ContainsKey(key) ){ - DateTime PreviousSentTime = _emailContext._SentEmails.GetValueOrDefault(key); - if (PreviousSentTime.AddMinutes(5) > DateTime.Now){ - return "Cannot sent another reset requests until 5 minutes has elapsed"; - }else{ - _emailContext._SentEmails.Remove(key); - } - } - Account? test = await _accountContext.GetAccount(request.Email.ToLower()); - if( test != null ) { - test.SiteData.EmailToken = Guid.NewGuid().ToString(); - await _accountContext.SetAccount( test ); - - string EmailContents = EmailService.ResetPasswordEmail; - EmailContents = Substitue( EmailContents, "@UserName", test.UserName ); - EmailContents = Substitue( EmailContents, "@UserName", test.UserName ); - EmailContents = Substitue( EmailContents, "@ResetPassWord", test.SiteData.EmailToken ); - - string result = _emailContext.Send( test.Email, EmailService.VerifyEmailSubject, EmailContents ); - _emailContext._SentEmails.Add(key, DateTime.Now); - return result; - } - return "Account Not Found"; - } catch (Exception) { - return "The connection couldn't be established to the email server"; - } - - } - - // In Account -> References UserName / Password( NewPassword ) / Error( EmailToken ) - // Out Success bool - [Route( "api/account/resetpassword" )] - [HttpPost] - public async Task> ResetPwdVerify( [FromBody] Account request ) { - try { - Account? test = await _accountContext.GetAccount(request.UserName.ToLower()); - if( test != null && !string.IsNullOrEmpty(test.SiteData.EmailToken) ) { - if( test.SiteData.EmailToken == request.Error ) { - test.SiteData.CurrentPasswordAttempts = 0; - test.PasswordHash = BCrypt.Net.BCrypt.HashPassword( request.PasswordHash ); - await _accountContext.SetAccount( test ); - return true; - } - } - return false; - } catch { - return false; - } - } - - // In Account -> References UserName / Password( Password ) ) - // Out Success bool - [Route( "api/account/delete" )] - [HttpPost] - public async Task> delete( [FromBody] Account request ) { - try { - Account? test = await _accountContext.GetAccount(request.UserName.ToLower()); - if( test != null ) { - if( BCrypt.Net.BCrypt.Verify( request.PasswordHash, test.PasswordHash ) ) { - await _accountContext.DeleteAccount( test ); - return true; - } - } - return false; - } catch { - return false; - } - } - } }