From 35c458b2362dbac0475bd496490e0ffd63293312 Mon Sep 17 00:00:00 2001 From: Astrian Zheng Date: Wed, 11 Oct 2023 16:44:53 +1100 Subject: [PATCH] Add image upload page --- .../Controllers/HomeController.cs | 497 ++++++++---------- FIT5032-Assignment/FIT5032-Assignment.csproj | 1 + .../Views/Home/ImageUpload.cshtml | 8 + 3 files changed, 237 insertions(+), 269 deletions(-) create mode 100644 FIT5032-Assignment/Views/Home/ImageUpload.cshtml diff --git a/FIT5032-Assignment/Controllers/HomeController.cs b/FIT5032-Assignment/Controllers/HomeController.cs index b0cd8de..8a2fcf1 100644 --- a/FIT5032-Assignment/Controllers/HomeController.cs +++ b/FIT5032-Assignment/Controllers/HomeController.cs @@ -21,283 +21,242 @@ using System.Threading.Tasks; using Newtonsoft.Json; using System.Net.Http.Headers; -namespace FIT5032_Assignment.Controllers -{ - - // Endpoint Response - public class PassageUserReply - { - public PassageUserReplyUser User { get; set; } +namespace FIT5032_Assignment.Controllers { + + // Endpoint Response + public class PassageUserReply { + public PassageUserReplyUser User { get; set; } + } + + public class PassageUserReplyUser { + public string Email { get; set; } + } + + // Database + public class Database1Entities : DbContext { + public DbSet Users { get; set; } + public DbSet Credentials { get; set; } + public DbSet Sessions { get; set; } + public DbSet Patients { get; set; } + public DbSet Doctors { get; set; } + } + public class HomeController : Controller { + public static string GetMd5Hash(string input) { + using (MD5 md5 = MD5.Create()) { + byte[] data = md5.ComputeHash(Encoding.UTF8.GetBytes(input)); + + StringBuilder sBuilder = new StringBuilder(); + for (int i = 0; i < data.Length; i++) { + sBuilder.Append(data[i].ToString("x2")); + } + + return sBuilder.ToString(); + } + } + private static readonly HttpClient httpClient = new HttpClient(); + public static RsaSecurityKey LoadRsaSecurityKeyFromPem(string pem) { + TextReader textReader = new StringReader(pem); + PemReader pemReader = new PemReader(textReader); + AsymmetricKeyParameter keyParameter = (AsymmetricKeyParameter)pemReader.ReadObject(); + + RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters((Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters)keyParameter); + RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); + rsa.ImportParameters(rsaParameters); + + return new RsaSecurityKey(rsa); + } + private String loginVerify(string token) { + + var jwtHandler = new JwtSecurityTokenHandler(); + var jwtToken = jwtHandler.ReadJwtToken(token); + + string base64Publickey = "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJDZ0tDQVFFQTRUWEQwVEh4NnJjNXlQcXM0Skw5M01nVEgvTS95Z2s3V1pYWWsrS01XTTA0bDdzM3owRlMKODlDRE56SFJkbVpJb3RCbDgrcUJ5TUwvck5VcUhXMnJ1Uzg0dmxFaWdza2djK2RsaitCZXFsaGsySFRpQitpegpQcGdCU1FJc2YrZjdSU3dkYktFS2hRQm1La3MxVGF4YWNDUndPVWJKT1VQbjJXZmhVSHhRd0FwZGNCQWdNdHVNCld3QzJZRThGblFRZDhxc3dMTTBGQWhoSzUrdXRXY0s0bHdCVlFxUGJRaUJZYnZmWXkwYVF6UFB2V2NMR1JvR00KUVA1b1JCTmRuRzQ4Sm9Eb2tCSEJkbCt4RzM1L1U2N1BvejFKY0VVSnpWTHdIUFNHa0xyRU1OYlFrbnJSK2tHZwpnS1dWNFpvYWVOSHZVeFE3YVg3SElFMlc1UnIwRmxGUG1RSURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K"; + RsaSecurityKey rsaKey = LoadRsaSecurityKeyFromPem(Encoding.UTF8.GetString(Convert.FromBase64String(base64Publickey))); + // Valid time 3600s + var validationParameters = new TokenValidationParameters() { + ValidIssuer = "https://auth.passage.id/v1/apps/ZHM5whW5xsZEczTn2loffzjN", + ValidateAudience = false, + IssuerSigningKey = rsaKey, + ValidateLifetime = true, + ClockSkew = TimeSpan.FromSeconds(3600) + }; + + try { + var claimsPrincipal = jwtHandler.ValidateToken(token, validationParameters, out var rawValidatedToken); + } catch (SecurityTokenExpiredException) { + Trace.WriteLine("Token has expired"); + return null; + } catch (SecurityTokenInvalidSignatureException) { + Trace.WriteLine("Token has invalid signature"); + return null; + } catch (SecurityTokenInvalidIssuerException) { + Trace.WriteLine("Token has invalid issuer"); + return null; + } catch (SecurityTokenInvalidAudienceException) { + Trace.WriteLine("Token has invalid audience"); + return null; + } catch (SecurityTokenValidationException) { + Trace.WriteLine("Token failed validation"); + return null; + } catch (ArgumentException) { + Trace.WriteLine("Token was empty or null"); + return null; + } + + string sub = jwtToken.Claims.First(claim => claim.Type == "sub").Value; + + return sub; } - public class PassageUserReplyUser - { - public string Email { get; set; } + private Database1Entities db = new Database1Entities(); + + private string GenerateRandomString(int length) { + const string validChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + var rng = new RNGCryptoServiceProvider(); + var bytes = new byte[length]; + rng.GetBytes(bytes); + return new string(bytes.Select(x => validChars[x % validChars.Length]).ToArray()); } - // Database - public class Database1Entities : DbContext - { - public DbSet Users { get; set; } - public DbSet Credentials { get; set; } - public DbSet Sessions { get; set; } - public DbSet Patients { get; set; } - public DbSet Doctors { get; set; } + public HomeController() { + // if auth token setted, ignore + if (httpClient.DefaultRequestHeaders.Authorization == null) { + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "vF4ch1wUf8.1cqOms9JMmUbqMGohlkJLzGDVlbF51D03fJnLfxwkn8kyAaVVjfvySufW9vXb3p3"); + } } - public class HomeController : Controller - { - public static string GetMd5Hash(string input) - { - using (MD5 md5 = MD5.Create()) - { - byte[] data = md5.ComputeHash(Encoding.UTF8.GetBytes(input)); - StringBuilder sBuilder = new StringBuilder(); - for (int i = 0; i < data.Length; i++) - { - sBuilder.Append(data[i].ToString("x2")); - } - - return sBuilder.ToString(); + public ActionResult Index() { + // If user logged in, show user name + if (Request.Cookies["psg_auth_token"] != null) { + var user = loginVerify(Request.Cookies["psg_auth_token"].Value); + if (user != null) { + var db = new Database1Entities(); + var credential = db.Credentials.Where(res => (res.uniqueIdCode == user) && (res.provider == 0)); + if (credential.Count() != 0) { + var userUuid = credential.First().user; + var dbUser = db.Users.Where(res => res.uuid == userUuid); + if (dbUser.Count() != 0) { + ViewBag.displayname = dbUser.First().displayName; + ViewBag.avatar = dbUser.First().avatar; + ViewBag.role = dbUser.First().role == 1 ? "Patient" : "Doctor"; } + } } - private static readonly HttpClient httpClient = new HttpClient(); - public static RsaSecurityKey LoadRsaSecurityKeyFromPem(string pem) - { - TextReader textReader = new StringReader(pem); - PemReader pemReader = new PemReader(textReader); - AsymmetricKeyParameter keyParameter = (AsymmetricKeyParameter)pemReader.ReadObject(); - - RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters((Org.BouncyCastle.Crypto.Parameters.RsaKeyParameters)keyParameter); - RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); - rsa.ImportParameters(rsaParameters); - - return new RsaSecurityKey(rsa); - } - private String loginVerify(string token) - { - - var jwtHandler = new JwtSecurityTokenHandler(); - var jwtToken = jwtHandler.ReadJwtToken(token); - - string base64Publickey = "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJDZ0tDQVFFQTRUWEQwVEh4NnJjNXlQcXM0Skw5M01nVEgvTS95Z2s3V1pYWWsrS01XTTA0bDdzM3owRlMKODlDRE56SFJkbVpJb3RCbDgrcUJ5TUwvck5VcUhXMnJ1Uzg0dmxFaWdza2djK2RsaitCZXFsaGsySFRpQitpegpQcGdCU1FJc2YrZjdSU3dkYktFS2hRQm1La3MxVGF4YWNDUndPVWJKT1VQbjJXZmhVSHhRd0FwZGNCQWdNdHVNCld3QzJZRThGblFRZDhxc3dMTTBGQWhoSzUrdXRXY0s0bHdCVlFxUGJRaUJZYnZmWXkwYVF6UFB2V2NMR1JvR00KUVA1b1JCTmRuRzQ4Sm9Eb2tCSEJkbCt4RzM1L1U2N1BvejFKY0VVSnpWTHdIUFNHa0xyRU1OYlFrbnJSK2tHZwpnS1dWNFpvYWVOSHZVeFE3YVg3SElFMlc1UnIwRmxGUG1RSURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K"; - RsaSecurityKey rsaKey = LoadRsaSecurityKeyFromPem(Encoding.UTF8.GetString(Convert.FromBase64String(base64Publickey))); - // Valid time 3600s - var validationParameters = new TokenValidationParameters() - { - ValidIssuer = "https://auth.passage.id/v1/apps/ZHM5whW5xsZEczTn2loffzjN", - ValidateAudience = false, - IssuerSigningKey = rsaKey, - ValidateLifetime = true, - ClockSkew = TimeSpan.FromSeconds(3600) - }; - - try - { - var claimsPrincipal = jwtHandler.ValidateToken(token, validationParameters, out var rawValidatedToken); - } - catch (SecurityTokenExpiredException) - { - Trace.WriteLine("Token has expired"); - return null; - } - catch (SecurityTokenInvalidSignatureException) - { - Trace.WriteLine("Token has invalid signature"); - return null; - } - catch (SecurityTokenInvalidIssuerException) - { - Trace.WriteLine("Token has invalid issuer"); - return null; - } - catch (SecurityTokenInvalidAudienceException) - { - Trace.WriteLine("Token has invalid audience"); - return null; - } - catch (SecurityTokenValidationException) - { - Trace.WriteLine("Token failed validation"); - return null; - } - catch (ArgumentException) - { - Trace.WriteLine("Token was empty or null"); - return null; - } - - string sub = jwtToken.Claims.First(claim => claim.Type == "sub").Value; - - return sub; - } - - private Database1Entities db = new Database1Entities(); - - private string GenerateRandomString(int length) - { - const string validChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - var rng = new RNGCryptoServiceProvider(); - var bytes = new byte[length]; - rng.GetBytes(bytes); - return new string(bytes.Select(x => validChars[x % validChars.Length]).ToArray()); - } - - public HomeController() - { - // if auth token setted, ignore - if (httpClient.DefaultRequestHeaders.Authorization == null) - { - httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "vF4ch1wUf8.1cqOms9JMmUbqMGohlkJLzGDVlbF51D03fJnLfxwkn8kyAaVVjfvySufW9vXb3p3"); - } - } - - public ActionResult Index() - { - // If user logged in, show user name - if (Request.Cookies["psg_auth_token"] != null) - { - var user = loginVerify(Request.Cookies["psg_auth_token"].Value); - if (user != null) - { - var db = new Database1Entities(); - var credential = db.Credentials.Where(res => (res.uniqueIdCode == user) && (res.provider == 0)); - if (credential.Count() != 0) - { - var userUuid = credential.First().user; - var dbUser = db.Users.Where(res => res.uuid == userUuid); - if (dbUser.Count() != 0) - { - ViewBag.displayname = dbUser.First().displayName; - ViewBag.avatar = dbUser.First().avatar; - ViewBag.role = dbUser.First().role == 1 ? "Patient" : "Doctor"; - } - } - } - } - return View(); - } - - public ActionResult CreateAccount() - { - return View(); - } - - public ActionResult LoginRedirect() - { - // See cookies - var psg_auth_token = Request.Cookies["psg_auth_token"]; - Trace.WriteLine(psg_auth_token.Value); - // JWT Verify - string sub = loginVerify(psg_auth_token.Value); - if (sub == null) - { - return RedirectToAction("Login"); - } - else - { - var db = new Database1Entities(); - var credential = db.Credentials.Where(res => (res.uniqueIdCode == sub) && (res.provider == 0)); - // Register: if no credential, redirect to create account - if (credential.Count() == 0) - { - return RedirectToAction("CompleteProfile"); - } - else - { - // Successful login - return RedirectToAction("Index"); - } - } - } - - // POST /ComplteteProfile - [HttpPost] - public async Task CompleteProfile(Models.CompleteProfileForm model) - { - if (!ModelState.IsValid) - { - ModelState.AddModelError("fullname", "Form not valid"); - return View(model); - } - - // Verify user is logged in - var psg_auth_token = Request.Cookies["psg_auth_token"]; - var user = loginVerify(psg_auth_token.Value); - if (user == null) - { - return RedirectToAction("Login"); - } - - // Get users email from Passage API - var app_id = "ZHM5whW5xsZEczTn2loffzjN"; - var user_id = user; - var url = $"https://api.passage.id/v1/apps/{app_id}/users/{user_id}"; - Trace.WriteLine(url); - Trace.WriteLine(httpClient.DefaultRequestHeaders.Authorization); - var res = await httpClient.GetStringAsync(url); - string emailaddress = JsonConvert.DeserializeObject(res).User.Email; - - // MD5 hash email to get avatar from gravatar - string md5Email = GetMd5Hash(emailaddress); - string avatarUrl = "https://www.gravatar.com/avatar/" + md5Email + "?s=200"; - - // Create a new credential and a new user - string userUuid = Guid.NewGuid().ToString(); - Users newDbUser = new Users - { - uuid = userUuid, - displayName = model.fullname, - role = Int16.Parse(model.role), - avatar = avatarUrl - }; - Credentials credential = new Credentials - { - uuid = Guid.NewGuid().ToString(), - user = userUuid, - uniqueIdCode = user_id, - provider = 0, - }; - - // Add them into database - db.Users.Add(newDbUser); - db.Credentials.Add(credential); - db.SaveChanges(); - - if (model.role == "1") - { - // Create patient profile - db.Patients.Add(new Patients - { - phone = "", - address = "", - user = userUuid, - notes = "" - }); - db.SaveChanges(); - } - else - { - db.Doctors.Add(new Doctors - { - user = userUuid, - bio = "", - }); - db.SaveChanges(); - } - - return RedirectToAction("Index"); - } - - public ActionResult InitialPasskey() - { - return View(); - } - - public ActionResult CompleteProfile() - { - return View(); - } + } + return View(); } + public ActionResult CreateAccount() { + return View(); + } + + public ActionResult LoginRedirect() { + // See cookies + var psg_auth_token = Request.Cookies["psg_auth_token"]; + Trace.WriteLine(psg_auth_token.Value); + // JWT Verify + string sub = loginVerify(psg_auth_token.Value); + if (sub == null) { + return RedirectToAction("Login"); + } else { + var db = new Database1Entities(); + var credential = db.Credentials.Where(res => (res.uniqueIdCode == sub) && (res.provider == 0)); + // Register: if no credential, redirect to create account + if (credential.Count() == 0) { + return RedirectToAction("CompleteProfile"); + } else { + // Successful login + return RedirectToAction("Index"); + } + } + } + + // POST /ComplteteProfile + [HttpPost] + public async Task CompleteProfile(Models.CompleteProfileForm model) { + if (!ModelState.IsValid) { + ModelState.AddModelError("fullname", "Form not valid"); + return View(model); + } + + // Verify user is logged in + var psg_auth_token = Request.Cookies["psg_auth_token"]; + var user = loginVerify(psg_auth_token.Value); + if (user == null) { + return RedirectToAction("Login"); + } + + // Get users email from Passage API + var app_id = "ZHM5whW5xsZEczTn2loffzjN"; + var user_id = user; + var url = $"https://api.passage.id/v1/apps/{app_id}/users/{user_id}"; + Trace.WriteLine(url); + Trace.WriteLine(httpClient.DefaultRequestHeaders.Authorization); + var res = await httpClient.GetStringAsync(url); + string emailaddress = JsonConvert.DeserializeObject(res).User.Email; + + // MD5 hash email to get avatar from gravatar + string md5Email = GetMd5Hash(emailaddress); + string avatarUrl = "https://www.gravatar.com/avatar/" + md5Email + "?s=200"; + + // Create a new credential and a new user + string userUuid = Guid.NewGuid().ToString(); + Users newDbUser = new Users { + uuid = userUuid, + displayName = model.fullname, + role = Int16.Parse(model.role), + avatar = avatarUrl + }; + Credentials credential = new Credentials { + uuid = Guid.NewGuid().ToString(), + user = userUuid, + uniqueIdCode = user_id, + provider = 0, + }; + + // Add them into database + db.Users.Add(newDbUser); + db.Credentials.Add(credential); + db.SaveChanges(); + + if (model.role == "1") { + // Create patient profile + db.Patients.Add(new Patients { + phone = "", + address = "", + user = userUuid, + notes = "" + }); + db.SaveChanges(); + } else { + db.Doctors.Add(new Doctors + { + user = userUuid, + bio = "", + }); + db.SaveChanges(); + } + + return RedirectToAction("Index"); + } + + public ActionResult InitialPasskey() { + return View(); + } + + public ActionResult CompleteProfile() { + return View(); + } + + public ActionResult ImageUpload() { + var user = loginVerify(Request.Cookies["psg_auth_token"].Value); + if (user != null) { + // Redirect to home page + return RedirectToAction("Index"); + } else { + return View(); + } + } + } + } \ No newline at end of file diff --git a/FIT5032-Assignment/FIT5032-Assignment.csproj b/FIT5032-Assignment/FIT5032-Assignment.csproj index 12f8ac6..92b8884 100644 --- a/FIT5032-Assignment/FIT5032-Assignment.csproj +++ b/FIT5032-Assignment/FIT5032-Assignment.csproj @@ -310,6 +310,7 @@ + diff --git a/FIT5032-Assignment/Views/Home/ImageUpload.cshtml b/FIT5032-Assignment/Views/Home/ImageUpload.cshtml new file mode 100644 index 0000000..c5600dc --- /dev/null +++ b/FIT5032-Assignment/Views/Home/ImageUpload.cshtml @@ -0,0 +1,8 @@ +@{ + Layout = "~/Views/Shared/_Layout.cshtml"; +} + + +
+ image upload +
\ No newline at end of file