diff --git a/FIT5032-Assignment/App_Start/RouteConfig.cs b/FIT5032-Assignment/App_Start/RouteConfig.cs index 048910e..5900e43 100644 --- a/FIT5032-Assignment/App_Start/RouteConfig.cs +++ b/FIT5032-Assignment/App_Start/RouteConfig.cs @@ -22,6 +22,12 @@ namespace FIT5032_Assignment { url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); + + routes.MapRoute( + name: "Appointments", + url: "Appointments", + defaults: new { controller = "Appointments", action = "Index", id = UrlParameter.Optional } + ); } } } diff --git a/FIT5032-Assignment/Controllers/AppointmentsController.cs b/FIT5032-Assignment/Controllers/AppointmentsController.cs index 4378ae8..211a55f 100644 --- a/FIT5032-Assignment/Controllers/AppointmentsController.cs +++ b/FIT5032-Assignment/Controllers/AppointmentsController.cs @@ -7,12 +7,93 @@ using System.Web.Mvc; using FIT5032_Assignment.Models; using System.Data.Entity; using System.Diagnostics; +using Microsoft.IdentityModel.Tokens; +using System.IdentityModel.Tokens.Jwt; +using System.Text; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.OpenSsl; +using Org.BouncyCastle.Security; +using System.IO; +using System.Security.Cryptography; +using System.Globalization; namespace FIT5032_Assignment.Controllers { public class AppointmentsController : Controller { private Database1Entities db = new Database1Entities(); + + // Login check + private 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 psgCredentialVerify(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 Users loginInfo(string user) { + var db = new Database1Entities(); + var credential = db.Credentials.Where(res => (res.uniqueIdCode == user) && (res.provider == 0)); + if (credential.Count() == 0) { + return null; + } else { + var userUuid = credential.First().user; + var dbUser = db.Users.Where(res => res.uuid == userUuid); + if (dbUser.Count() == 0) { + return null; + } else { + return dbUser.First(); + } + } + } + // GET: Appointments public ActionResult Index() { + ViewBag.tip = TempData["tip"]; return View(); } @@ -23,6 +104,10 @@ namespace FIT5032_Assignment.Controllers { // GET: Appointments/Create public ActionResult Create(string id) { + if (Request.Cookies["psg_auth_token"] == null) { + // Redirect to home page + return RedirectToAction("Index"); + } if (id == null) { // Pass a dropdown list for all doctors List doctors = db.Doctors.ToList(); @@ -44,15 +129,75 @@ namespace FIT5032_Assignment.Controllers { } - // POST: Appointments/Create + // POST: Appointments/Create/doctorId [HttpPost] public ActionResult Create(FormCollection collection) { try { // TODO: Add insert logic here + Trace.WriteLine(collection["doctorUuid"]); - return RedirectToAction("Index"); + // Verify login + if (Request.Cookies["psg_auth_token"] == null) { + // Redirect to home page + return RedirectToAction("Index"); + } + Trace.WriteLine("User has cookie"); + var user = psgCredentialVerify(Request.Cookies["psg_auth_token"].Value); + if (user == null) { + // Redirect to home page + Response.Cookies["psg_auth_token"].Expires = DateTime.Now.AddDays(-1); + return RedirectToAction("Index"); + } + Trace.WriteLine("User has login"); + var userProfile = loginInfo(user); + if (userProfile == null) { + // Redirect to home page, and remove cookies + Response.Cookies["psg_auth_token"].Expires = DateTime.Now.AddDays(-1); + return RedirectToAction("Index"); + } + Trace.WriteLine("User login available"); + // Detect if user logined is patient + if (userProfile.role != 1) { + // Redirect to home page + return Redirect("/Appointments/Index"); + } + Trace.WriteLine("User is patient"); + + // Transfer MM/dd/yyyy (18/10/2023) to C# DateTime + Trace.WriteLine(collection["appointmentDate"]); + DateTime date = DateTime.ParseExact(collection["appointmentDate"], "dd/MM/yyyy", CultureInfo.InvariantCulture); + Trace.WriteLine(date); + + // The date cannot be earlier than today 0:00 + + if (date < DateTime.Today) { + ViewBag.tip = "The date cannot be earlier than today."; + ViewBag.doctorId = collection["doctorUuid"]; + ViewBag.doctorUser = db.Users.Find(collection["doctorUuid"]); + return View(); + } + + // Create appointment + var uuid = Guid.NewGuid().ToString(); + Appointments appointment = new Appointments { + uuid = uuid, + patient = userProfile.uuid, + responsibleBy = collection["doctorUuid"], + createdAt = DateTime.Now, + appointmentDate = date, + status = 0, + createdBy = 0 + }; + db.Appointments.Add(appointment); + db.SaveChanges(); + + TempData["tip"] = "The appointment has been booked."; + return Redirect("/Appointments/Index"); } catch { + ViewBag.tip = "System error"; + ViewBag.doctorId = collection["doctorUuid"]; + ViewBag.doctorUser = db.Users.Find(collection["doctorUuid"]); return View(); } } diff --git a/FIT5032-Assignment/Controllers/HomeController.cs b/FIT5032-Assignment/Controllers/HomeController.cs index 2def4ef..f8ed69a 100644 --- a/FIT5032-Assignment/Controllers/HomeController.cs +++ b/FIT5032-Assignment/Controllers/HomeController.cs @@ -51,6 +51,7 @@ namespace FIT5032_Assignment.Controllers { public DbSet Sessions { get; set; } public DbSet Patients { get; set; } public DbSet Doctors { get; set; } + public DbSet Appointments { get; set; } } public class HomeController : Controller { public static string GetMd5Hash(string input) { diff --git a/FIT5032-Assignment/Views/Appointments/Create.cshtml b/FIT5032-Assignment/Views/Appointments/Create.cshtml index 41d578d..7a3d16f 100644 --- a/FIT5032-Assignment/Views/Appointments/Create.cshtml +++ b/FIT5032-Assignment/Views/Appointments/Create.cshtml @@ -5,6 +5,12 @@ Layout = "~/Views/Shared/_Layout.cshtml"; } +@if (ViewBag.tip != null) { + +} +

Book an appointment


@@ -32,11 +38,12 @@ } } else {

Book an appointment with @ViewBag.doctorUser.displayName

-
+ +
- +
diff --git a/FIT5032-Assignment/Views/Appointments/Index.cshtml b/FIT5032-Assignment/Views/Appointments/Index.cshtml index 62e0a06..0de5b2d 100644 --- a/FIT5032-Assignment/Views/Appointments/Index.cshtml +++ b/FIT5032-Assignment/Views/Appointments/Index.cshtml @@ -4,6 +4,12 @@ Layout = "~/Views/Shared/_Layout.cshtml"; } +@if (ViewBag.tip != null) { + +} +

Appointments