FIT5032-Assignment/FIT5032-Assignment/Controllers/AppointmentsController.cs
2023-10-19 10:31:41 +11:00

524 lines
20 KiB
C#

using System;
using System.Collections.Generic;
using System.Data.Entity.Core.Common.CommandTrees;
using System.Linq;
using System.Web;
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;
using RestSharp.Authenticators;
using RestSharp;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace FIT5032_Assignment.Controllers {
public class AppointmentsController : Controller {
public class PassageUserAPI {
public PassageUserEntity User { get; set; }
}
public class PassageUserEntity {
public string CreatedAt { get; set; }
public string Email { get; set; }
public bool EmailVerified { get; set; }
}
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() {
// Check login
if (Request.Cookies["psg_auth_token"] == null) {
// Redirect to home page
return RedirectToAction("Index");
}
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");
}
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");
}
// Detect user role
ViewBag.role = db.Users.Find(userProfile.uuid).role;
var appointments = new List<Tuple<Appointments, Users>>();
if (userProfile.role == 1) { // patient
var dbData = db.Appointments.Where(a => a.patient == userProfile.uuid).OrderByDescending(a => a.createdAt).ToList();
foreach (var item in dbData) {
Trace.WriteLine(item.uuid);
appointments.Add(new Tuple<Appointments, Users>(item, db.Users.Find(item.responsibleBy)));
}
} else if (userProfile.role == 2) { // doctor
var dbData = db.Appointments.Where(a => a.responsibleBy == userProfile.uuid).OrderByDescending(a => a.createdAt).ToList();
foreach (var item in dbData) {
Trace.WriteLine(item.uuid);
appointments.Add(new Tuple<Appointments, Users>(item, db.Users.Find(item.patient)));
}
}
ViewBag.appointments = appointments;
ViewBag.tip = TempData["tip"];
return View();
}
// GET: Appointments/Cancel/uuid
public ActionResult Cancel(string id) {
// Check login
if (Request.Cookies["psg_auth_token"] == null) {
// Redirect to home page
return RedirectToAction("Index");
}
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");
}
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");
}
// Check if the appointment is belong to the doctor or patient
var appointment = db.Appointments.Find(id);
if (appointment == null) {
TempData["tip"] = "The appointment does not exist.";
return Redirect("/Appointments/Index");
}
if (appointment.patient != userProfile.uuid && appointment.responsibleBy != userProfile.uuid) {
TempData["tip"] = "The appointment does not exist.";
return Redirect("/Appointments/Index");
}
// Check status == 0
if (appointment.status != 0) {
TempData["tip"] = "Operation invalid";
return Redirect("/Appointments/Index");
}
// Update status
appointment.status = -1;
db.Entry(appointment).State = EntityState.Modified;
db.SaveChanges();
TempData["tip"] = "The appointment has been cancelled.";
return Redirect("/Appointments/Index");
}
public ActionResult Approve(string id) {
// Check login
if (Request.Cookies["psg_auth_token"] == null) {
// Redirect to home page
return RedirectToAction("Index");
}
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");
}
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");
}
// Detect user role
ViewBag.role = db.Users.Find(userProfile.uuid).role;
// Only doctor can approve appointment
if (userProfile.role != 2) {
TempData["tip"] = "This operation is not allowed.";
return Redirect("/Appointments/Index");
}
// Check if the appointment is belong to the doctor or patient
var appointment = db.Appointments.Find(id);
if (appointment == null) {
TempData["tip"] = "The appointment does not exist.";
return Redirect("/Appointments/Index");
}
if (appointment.responsibleBy != userProfile.uuid) {
TempData["tip"] = "The appointment does not exist.";
return Redirect("/Appointments/Index");
}
// Check status == 0
if (appointment.status != 0) {
TempData["tip"] = "Operation invalid";
return Redirect("/Appointments/Index");
}
// Update status
appointment.status = 1;
db.Entry(appointment).State = EntityState.Modified;
db.SaveChanges();
TempData["tip"] = "The appointment has been approved.";
return Redirect("/Appointments/Index");
}
// 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();
List<Tuple<Users, Doctors>> usersList = new List<Tuple<Users, Doctors>>();
foreach (var doctor in doctors) {
// Fetch the user obj
Users user = db.Users.Find(doctor.user);
usersList.Add(new Tuple<Users, Doctors>(user, doctor));
}
ViewBag.doctorsList = usersList;
} else {
ViewBag.doctorId = id;
// Fetch doctor profile
ViewBag.doctor = db.Doctors.Where(d => d.user == id);
ViewBag.doctorUser = db.Users.Find(id);
}
return View();
}
// POST: Appointments/Create/doctorId
[HttpPost]
public ActionResult Create(FormCollection collection) {
try {
// TODO: Add insert logic here
Trace.WriteLine(collection["doctorUuid"]);
// 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();
}
}
// GET: Appointments/UploadImage/uuid
public ActionResult UploadImage(string id) {
// Check login
if (Request.Cookies["psg_auth_token"] == null) {
// Redirect to home page
return RedirectToAction("Index");
}
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");
}
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");
}
// Detect user role
ViewBag.role = db.Users.Find(userProfile.uuid).role;
if (userProfile.role != 2) {
TempData["tip"] = "This operation is not allowed.";
return Redirect("/Appointments/Index");
}
// Check if the appointment is belong to the doctor
var appointment = db.Appointments.Find(id);
if (appointment == null) {
TempData["tip"] = "The appointment does not exist.";
return Redirect("/Appointments/Index");
}
if (appointment.responsibleBy != userProfile.uuid) {
TempData["tip"] = "The appointment does not exist.";
return Redirect("/Appointments/Index");
}
// Check status == 1
if (appointment.status != 1) {
TempData["tip"] = "Operation invalid";
return Redirect("/Appointments/Index");
}
ViewBag.appointment = appointment;
ViewBag.patient = db.Users.Find(appointment.patient);
return View();
}
[HttpPost]
public async Task<ActionResult> UploadImage(HttpPostedFileBase image, string appointmentUuid) {
try {
// Check login
if (Request.Cookies["psg_auth_token"] == null) {
// Redirect to home page
return RedirectToAction("Index");
}
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");
}
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");
}
// Detect user role
ViewBag.role = db.Users.Find(userProfile.uuid).role;
if (userProfile.role != 2) {
TempData["tip"] = "This operation is not allowed.";
return Redirect("/Appointments/Index");
}
// Check the appointment exist & status == 1 & responsibleBy == doctor
var appointment = db.Appointments.Find(appointmentUuid);
if (appointment == null) {
TempData["tip"] = "The appointment does not exist.";
return Redirect("/Appointments/Index");
}
if (appointment.responsibleBy != userProfile.uuid) {
TempData["tip"] = "The appointment does not exist.";
return Redirect("/Appointments/Index");
}
if (appointment.status != 1) {
TempData["tip"] = "Operation invalid";
return Redirect("/Appointments/Index");
}
// check uploaded file
if (image == null) {
ViewBag.tip = "Please upload a file";
ViewBag.appointment = appointment;
ViewBag.patient = db.Users.Find(appointment.patient);
return View();
}
// check format: png, jpg, jpeg
string[] formats = new string[] { ".png", ".jpg", ".jpeg" };
string extension = Path.GetExtension(image.FileName);
if (!formats.Contains(extension)) {
ViewBag.tip = "Please upload a png, jpg or jpeg file";
ViewBag.appointment = appointment;
ViewBag.patient = db.Users.Find(appointment.patient);
return View();
}
// save file
var fileId = Guid.NewGuid().ToString();
var fileName = fileId + extension;
var filePath = Path.Combine(Server.MapPath("~/App_Data/upload_images"), fileName);
image.SaveAs(filePath);
// Create image entity to database
var appointmentId = appointment.uuid;
var imageId = Guid.NewGuid().ToString();
var patientUuid = appointment.patient;
var dbImage = new Images {
uuid = imageId,
appointment = appointmentId,
patient = patientUuid,
responsibleBy = userProfile.uuid,
createdAt = DateTime.Now,
file = fileName,
status = 0,
};
// Change appointment status to 2
appointment.status = 2;
db.Entry(appointment).State = EntityState.Modified;
db.SaveChanges();
// Find patient email from Passage service
var credential = db.Credentials.Where(res => (res.user == patientUuid) && (res.provider == 0));
Trace.WriteLine(credential.First().uniqueIdCode);
var user_id = credential.First().uniqueIdCode;
var app_id = "ZHM5whW5xsZEczTn2loffzjN";
RestClient passageClient = new RestClient(new RestClientOptions($"https://api.passage.id/v1/apps/{app_id}"));
passageClient.AddDefaultHeader("Authorization", "Bearer vF4ch1wUf8.1cqOms9JMmUbqMGohlkJLzGDVlbF51D03fJnLfxwkn8kyAaVVjfvySufW9vXb3p3");
var psgRequest = new RestRequest($"users/{user_id}", Method.Get);
// Send request
var passageResponse = await passageClient.ExecuteAsync(psgRequest);
Trace.WriteLine(passageResponse.Content);
// Transfer response
var passageResponseJson = JsonConvert.DeserializeObject<PassageUserAPI>(passageResponse.Content);
var patientEmail = passageResponseJson.User.Email;
// Send attached email with mailgun
var doctorName = userProfile.displayName;
var patient = db.Users.Find(patientUuid);
RestClient client = new RestClient(new RestClientOptions("https://api.mailgun.net/v3/test.astrian.moe") {
Authenticator = new HttpBasicAuthenticator("api", "365900a7818241eafcbbf82e59cf99e8-5465e583-b4966e64"),
});
var request = new RestRequest("messages", Method.Post);
request.AddParameter("from", "Xpectrum <xpectrum@test.astrian.moe>");
request.AddParameter("to", patientEmail);
request.AddParameter("subject", "Xpectrum: New image available");
request.AddParameter("text", $"Hi {patient.displayName},\n\nDr. {doctorName} has uploaded a new image for you.\n\nPlease check the attachment.\n\nBest regards,\nXpectrum");
request.AddFile("attachment", filePath);
// Send request
var response = await client.ExecuteAsync(request);
Trace.WriteLine(response.Content);
TempData["tip"] = "The image has been attached to the appointment.";
return Redirect("/Appointments/Index");
} catch {
TempData["tip"] = "System error";
return Redirect("/Appointments/Index");
}
}
}
}