diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..307363d Binary files /dev/null and b/.DS_Store differ diff --git a/README.md b/README.md index 26ef349..2915b36 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ # websubmit-rs: a simple class submission system This is a fork for websubmit-rs, a web application for collecting student homework -submissions, written using [Rocket](https://rocket.rs) for a MySQL backend. +submissions, written using [Rocket](https://rocket.rs) running on a GDPR compliant Pelton backend. -To run it, you need to run a MySQL server deployment. +To run it, you need to run a Pelton server deployment. Then you can run the web application, which will automatically connect -to MySQL database `myclass`: +to Pelton database `myclass`: ``` websubmit-rs$ cargo run --release -- -i myclass ``` diff --git a/sample-config.toml b/sample-config.toml index cb2ef3e..de48448 100644 --- a/sample-config.toml +++ b/sample-config.toml @@ -11,6 +11,6 @@ resource_dir = "/path/to/resources" # a secret that will be hashed into user's API keys to make them unforgeable secret = "SECRET" # whether to send emails (set to false for development) -send_emails = true +send_emails = false # whether to reset the db (set to false for production) prime = true diff --git a/src/admin.rs b/src/admin.rs index a357e21..40fe0f8 100644 --- a/src/admin.rs +++ b/src/admin.rs @@ -94,7 +94,7 @@ pub(crate) fn lec_add_submit( pub(crate) fn lec(_adm: Admin, num: u8, backend: &State>>) -> Template { let mut bg = backend.lock().unwrap(); let res = bg.prep_exec( - "SELECT * FROM questions WHERE lec = ?", + "SELECT lec , q , question FROM questions WHERE lec = ?", vec![(num as u64).into()], ); drop(bg); @@ -130,6 +130,7 @@ pub(crate) fn addq( bg.insert( "questions", vec![ + format!("{}-{}", num, data.q_id).into(), (num as u64).into(), (data.q_id as u64).into(), data.q_prompt.to_string().into(), @@ -149,7 +150,7 @@ pub(crate) fn editq( ) -> Template { let mut bg = backend.lock().unwrap(); let res = bg.prep_exec( - "SELECT * FROM questions WHERE lec = ?", + "SELECT lec , q , question FROM questions WHERE lec = ?", vec![(num as u64).into()], ); drop(bg); @@ -194,7 +195,7 @@ pub(crate) fn get_registered_users( config: &State, ) -> Template { let mut bg = backend.lock().unwrap(); - let res = bg.prep_exec("SELECT email, is_admin, apikey FROM users", vec![]); + let res = bg.prep_exec("SELECT PII_email, is_admin, apikey FROM users", vec![]); drop(bg); let users: Vec<_> = res diff --git a/src/backend.rs b/src/backend.rs index a3df7e6..42eb2e5 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -4,6 +4,7 @@ pub use mysql::Value; use mysql::*; use std::collections::HashMap; + pub struct MySqlBackend { pub handle: mysql::Conn, pub log: slog::Logger, @@ -25,19 +26,14 @@ impl MySqlBackend { "Connecting to MySql DB and initializing schema {}...", dbname ); let mut db = mysql::Conn::new( - Opts::from_url(&format!("mysql://root:password@127.0.0.1/{}", dbname)).unwrap(), + Opts::from_url(&format!("mysql://pelton:password@127.0.0.1:10001/{}", dbname)).unwrap(), ) .unwrap(); assert_eq!(db.ping(), true); if prime { - db.query_drop(format!("DROP DATABASE IF EXISTS {};", dbname)) - .unwrap(); - db.query_drop(format!("CREATE DATABASE {};", dbname)) - .unwrap(); - // reconnect db = mysql::Conn::new( - Opts::from_url(&format!("mysql://root:password@127.0.0.1/{}", dbname)).unwrap(), + Opts::from_url(&format!("mysql://pelton:password@127.0.0.1:10001/{}", dbname)).unwrap(), ) .unwrap(); for line in schema.lines() { diff --git a/src/questions.rs b/src/questions.rs index ab18f10..fad847d 100644 --- a/src/questions.rs +++ b/src/questions.rs @@ -115,7 +115,7 @@ pub(crate) fn answers( ) -> Template { let mut bg = backend.lock().unwrap(); let key: Value = (num as u64).into(); - let res = bg.prep_exec("SELECT * FROM answers WHERE lec = ?", vec![key]); + let res = bg.prep_exec("SELECT email, lec, q, answer, submitted_at FROM answers WHERE lec = ?", vec![key]); drop(bg); let answers: Vec<_> = res .into_iter() @@ -123,14 +123,9 @@ pub(crate) fn answers( id: from_value(r[2].clone()), user: from_value(r[0].clone()), answer: from_value(r[3].clone()), - time: if let Value::Time(..) = r[4] { - Some(from_value::(r[4].clone())) - } else { - None - }, + time: NaiveDateTime::parse_from_str(&String::from_utf8(from_value::>(r[4].clone())).unwrap().chars().collect::>()[..19].iter().collect::(), "%Y-%m-%d %H:%M:%S").ok() }) .collect(); - let ctx = LectureAnswersContext { lec_id: num, answers: answers, @@ -151,7 +146,7 @@ pub(crate) fn questions( let key: Value = (num as u64).into(); let answers_res = bg.prep_exec( - "SELECT answers.* FROM answers WHERE answers.lec = ? AND answers.email = ?", + "SELECT answers.email, answers.lec, answers.q, answers.answer, answers.submitted_at FROM answers WHERE answers.lec = ? AND answers.email = ?", vec![(num as u64).into(), apikey.user.clone().into()], ); let mut answers = HashMap::new(); @@ -161,7 +156,7 @@ pub(crate) fn questions( let atext: String = from_value(r[3].clone()); answers.insert(id, atext); } - let res = bg.prep_exec("SELECT * FROM questions WHERE lec = ?", vec![key]); + let res = bg.prep_exec("SELECT lec , q , question FROM questions WHERE lec = ?", vec![key]); drop(bg); let mut qs: Vec<_> = res .into_iter() @@ -195,10 +190,11 @@ pub(crate) fn questions_submit( ) -> Redirect { let mut bg = backend.lock().unwrap(); let vnum: Value = (num as u64).into(); - let ts: Value = Local::now().naive_local().into(); + let ts: Value = Local::now().naive_local().to_string().into(); for (id, answer) in &data.answers { let rec: Vec = vec![ + format!("{}-{}-{}", apikey.user.clone(), num, (*id)).into(), apikey.user.clone().into(), vnum.clone(), (*id).into(), diff --git a/src/schema.sql b/src/schema.sql index 340ea14..6d037dd 100644 --- a/src/schema.sql +++ b/src/schema.sql @@ -1,6 +1,7 @@ -CREATE TABLE users (email varchar(255), apikey varchar(255), is_admin tinyint, PRIMARY KEY (apikey)); +SET echo; +CREATE TABLE users (PII_email varchar(255), apikey varchar(255), is_admin int, PRIMARY KEY (apikey)); CREATE TABLE lectures (id int, label varchar(255), PRIMARY KEY (id)); -CREATE TABLE questions (lec int, q int, question text, PRIMARY KEY (lec, q)); -CREATE TABLE answers (email varchar(255), lec int, q int, answer text, submitted_at datetime, PRIMARY KEY (email, lec, q)); +CREATE TABLE questions (id text, lec int, q int, question text, PRIMARY KEY (id)); +CREATE TABLE answers (id text, email varchar(255), lec int, q int, answer text, submitted_at datetime, FOREIGN KEY (email) REFERENCES users(PII_email), PRIMARY KEY (id)); -CREATE VIEW lec_qcount as SELECT questions.lec, COUNT(questions.q) AS qcount FROM questions GROUP BY questions.lec; +CREATE VIEW lec_qcount as '"SELECT questions.lec, COUNT(questions.q) AS qcount FROM questions GROUP BY questions.lec"';