Book appointment

This commit is contained in:
Astrian Zheng 2023-10-18 11:55:37 +11:00
parent a107386ecd
commit c3c09f8966
5 changed files with 169 additions and 4 deletions

View File

@ -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 }
);
}
}
}

View File

@ -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> 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();
}
}

View File

@ -51,6 +51,7 @@ namespace FIT5032_Assignment.Controllers {
public DbSet<Sessions> Sessions { get; set; }
public DbSet<Patients> Patients { get; set; }
public DbSet<Doctors> Doctors { get; set; }
public DbSet<Appointments> Appointments { get; set; }
}
public class HomeController : Controller {
public static string GetMd5Hash(string input) {

View File

@ -5,6 +5,12 @@
Layout = "~/Views/Shared/_Layout.cshtml";
}
@if (ViewBag.tip != null) {
<div class="alert alert-success" role="alert">
@ViewBag.tip
</div>
}
<h2>Book an appointment</h2>
<hr />
@ -32,11 +38,12 @@
}
} else {
<h3>Book an appointment with @ViewBag.doctorUser.displayName</h3>
<form>
<form method="post">
<input type="hidden" name="doctorUuid" value="@ViewBag.doctorUser.uuid" />
<div class="form-group">
<label for="datepicker">Select the appointment date</label>
<div class='input-group'>
<input data-provide="datepicker" id="datepicker" class="form-control" />
<input data-provide="datepicker" id="datepicker" class="form-control" name="appointmentDate" />
</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>

View File

@ -4,6 +4,12 @@
Layout = "~/Views/Shared/_Layout.cshtml";
}
@if (ViewBag.tip != null) {
<div class="alert alert-success" role="alert">
@ViewBag.tip
</div>
}
<h2>Appointments</h2>
<ul>