Commit 8944e398 authored by Daniel Brüning's avatar Daniel Brüning Committed by Robert Czechowski
Browse files

Closes #23 Added a signup-page: You can now create a new accounnt without a login-code.

parent ae7ad8ae
Pipeline #795 passed with stage
in 9 minutes and 11 seconds
......@@ -14,7 +14,7 @@
use time;
use db_conn::MedalConnection;
use db_conn::{MedalConnection, SignupResult};
use db_objects::OptionSession;
use db_objects::SessionUser;
use db_objects::{Contest, Grade, Group, Participation, Submission, Taskgroup};
......@@ -533,6 +533,33 @@ pub fn logout<T: MedalConnection>(conn: &T, session_token: Option<String>) {
session_token.map(|token| conn.logout(&token));
}
pub fn signup<T: MedalConnection>(conn: &T, session_token:Option<String>, signup_data: (String, String, String)) -> MedalResult<SignupResult> {
let (username, email, password) = signup_data;
if username == "" || email == "" || password == "" {
return Ok(SignupResult::EmptyFields)
}
let salt = helpers::make_salt();
let hash = helpers::hash_password(&password, &salt)?;
let result = conn.signup(&session_token.unwrap(), &username, &email, hash, &salt);
Ok(result)
}
pub fn signupdata (query_string: Option<String>) -> json_val::Map<String, json_val::Value> {
let mut data = json_val::Map::new();
if let Some(query) = query_string {
if query.starts_with("status=") {
let status: &str = &query[7..];
if ["EmailTaken", "UsernameTaken", "UserLoggedIn", "EmptyFields"].contains(&status) {
data.insert((status).to_string(), to_json(&true));
}
}
}
data
}
pub fn load_submission<T: MedalConnection>(conn: &T, task_id: i32, session_token: &str, subtask: Option<String>)
-> MedalResult<String> {
let session = conn.get_session(&session_token).ensure_alive().ok_or(MedalError::NotLoggedIn)?;
......@@ -869,7 +896,7 @@ pub fn show_profile<T: MedalConnection>(conn: &T, session_token: &str, user_id:
if let Some(query) = query_string {
if query.starts_with("status=") {
let status: &str = &query[7..];
if ["NothingChanged", "DataChanged", "PasswordChanged", "PasswordMissmatch", "firstlogin"].contains(&status) {
if ["NothingChanged", "DataChanged", "PasswordChanged", "PasswordMissmatch", "firstlogin", "SignedUp"].contains(&status) {
data.insert((status).to_string(), to_json(&true));
}
}
......@@ -887,6 +914,7 @@ pub fn show_profile<T: MedalConnection>(conn: &T, session_token: &str, user_id:
}).collect();
data.insert("participations".into(), to_json(&participations));
}
// Case user_id: teacher modifing a students profile
Some(user_id) => {
// TODO: Add test to check if this access restriction works
let (user, opt_group) = conn.get_user_and_group_by_id(user_id).ok_or(MedalError::AccessDenied)?;
......
......@@ -270,8 +270,10 @@ impl MedalConnection for Connection {
sex = $11,
is_admin = $12,
is_teacher = $13,
permanent_login = $14
WHERE id = $15",
permanent_login = $14,
email = $15,
email_unconfirmed = $16
WHERE id = $17",
&[&session.username,
&session.password,
&session.salt,
......@@ -286,6 +288,8 @@ impl MedalConnection for Connection {
&session.is_admin,
&session.is_teacher,
&session.permanent_login,
&session.email,
&session.email_unconfirmed,
&session.id])
.unwrap();
}
......@@ -563,6 +567,35 @@ impl MedalConnection for Connection {
self.execute(query, &[&session]).unwrap();
}
fn signup(&self, session_token: &str, username: &str, email: &str, password_hash: String, salt: &str) -> SignupResult {
let mut session_user = self.get_session_or_new(&session_token);
if session_user.is_logged_in() {
return SignupResult::UserLoggedIn
}
if let Ok(None) = self.query_map_one("SELECT username FROM session WHERE username = $1",
&[&username],
|row| -> Option<String> { row.get(0) }) {} else {
//This username already exists!
return SignupResult::UsernameTaken
}
if let Ok(None) = self.query_map_one("SELECT email, email_unconfirmed FROM session WHERE email = $1 OR email_unconfirmed = $1",
&[&email],
|row| -> (Option<String>, Option<String>) { (row.get(0), row.get(1)) }) {} else {
//This email already exists!
return SignupResult::EmailTaken
}
session_user.username = Some(username.to_string());
session_user.email_unconfirmed = Some(email.to_string());
session_user.password = Some(password_hash);
session_user.salt = Some(salt.to_string());
self.save_session(session_user);
SignupResult::SignedUp
}
fn load_submission(&self, session: &SessionUser, task: i32, subtask: Option<&str>) -> Option<Submission> {
match subtask {
None => {
......
......@@ -14,6 +14,15 @@
use db_objects::*;
#[derive(Debug)]
pub enum SignupResult {
SignedUp,
EmailTaken,
UsernameTaken,
UserLoggedIn,
EmptyFields,
}
/// This trait abstracts the database connection and provides function for all actions to be performed on the database
/// in the medal platform.
pub trait MedalConnection {
......@@ -64,6 +73,8 @@ pub trait MedalConnection {
/// to `NULL`.
fn logout(&self, session: &str);
fn signup(&self, session_token: &str, username: &str, email: &str, password_hash: String, salt: &str) -> SignupResult;
fn load_submission(&self, session: &SessionUser, task: i32, subtask: Option<&str>) -> Option<Submission>;
fn get_all_submissions(&self, session_id: i32, task: i32, subtask: Option<&str>) -> Vec<Submission>;
fn submit_submission(&self, submission: Submission);
......
......@@ -20,7 +20,7 @@ use postgres::Connection;
use time;
use time::Duration;
use db_conn::{MedalConnection, MedalObject};
use db_conn::{MedalConnection, MedalObject, SignupResult};
use db_objects::*;
use helpers;
......
......@@ -33,7 +33,7 @@ use postgres::Connection;
use time;
use time::Duration;
use db_conn::{MedalConnection, MedalObject};
use db_conn::{MedalConnection, MedalObject, SignupResult};
use db_objects::*;
use helpers;
......@@ -382,8 +382,10 @@ impl MedalConnection for Connection {
sex = $11,
is_admin = $12,
is_teacher = $13,
permanent_login = $14
WHERE id = $15",
permanent_login = $14,
email = $15,
email_unconfirmed = $16
WHERE id = $17",
&[&session.username,
&session.password,
&session.salt,
......@@ -398,6 +400,8 @@ impl MedalConnection for Connection {
&session.is_admin,
&session.is_teacher,
&session.permanent_login,
&session.email,
&session.email_unconfirmed,
&session.id])
.unwrap();
}
......@@ -675,6 +679,35 @@ impl MedalConnection for Connection {
self.execute(query, &[&session]).unwrap();
}
fn signup(&self, session_token: &str, username: &str, email: &str, password_hash: String, salt: &str) -> SignupResult {
let mut session_user = self.get_session_or_new(&session_token);
if session_user.is_logged_in() {
return SignupResult::UserLoggedIn
}
if let Ok(None) = self.query_map_one("SELECT username FROM session WHERE username = $1",
&[&username],
|row| -> Option<String> { row.get(0) }) {} else {
//This username already exists!
return SignupResult::UsernameTaken
}
if let Ok(None) = self.query_map_one("SELECT email, email_unconfirmed FROM session WHERE email = $1 OR email_unconfirmed = $1",
&[&email],
|row| -> (Option<String>, Option<String>) { (row.get(0), row.get(1)) }) {} else {
//This email already exists!
return SignupResult::EmailTaken
}
session_user.username = Some(username.to_string());
session_user.email_unconfirmed = Some(email.to_string());
session_user.password = Some(password_hash);
session_user.salt = Some(salt.to_string());
self.save_session(session_user);
SignupResult::SignedUp
}
fn load_submission(&self, session: &SessionUser, task: i32, subtask: Option<&str>) -> Option<Submission> {
match subtask {
None => {
......
......@@ -20,7 +20,7 @@ use rusqlite::Connection;
use time;
use time::Duration;
use db_conn::{MedalConnection, MedalObject};
use db_conn::{MedalConnection, MedalObject, SignupResult};
use db_objects::*;
use helpers;
......
......@@ -33,7 +33,7 @@ use rusqlite::Connection;
use time;
use time::Duration;
use db_conn::{MedalConnection, MedalObject};
use db_conn::{MedalConnection, MedalObject, SignupResult};
use db_objects::*;
use helpers;
......@@ -382,8 +382,10 @@ impl MedalConnection for Connection {
sex = ?11,
is_admin = ?12,
is_teacher = ?13,
permanent_login = ?14
WHERE id = ?15",
permanent_login = ?14,
email = ?15,
email_unconfirmed = ?16
WHERE id = ?17",
&[&session.username,
&session.password,
&session.salt,
......@@ -398,6 +400,8 @@ impl MedalConnection for Connection {
&session.is_admin,
&session.is_teacher,
&session.permanent_login,
&session.email,
&session.email_unconfirmed,
&session.id])
.unwrap();
}
......@@ -675,6 +679,35 @@ impl MedalConnection for Connection {
self.execute(query, &[&session]).unwrap();
}
fn signup(&self, session_token: &str, username: &str, email: &str, password_hash: String, salt: &str) -> SignupResult {
let mut session_user = self.get_session_or_new(&session_token);
if session_user.is_logged_in() {
return SignupResult::UserLoggedIn
}
if let Ok(None) = self.query_map_one("SELECT username FROM session WHERE username = ?1",
&[&username],
|row| -> Option<String> { row.get(0) }) {} else {
//This username already exists!
return SignupResult::UsernameTaken
}
if let Ok(None) = self.query_map_one("SELECT email, email_unconfirmed FROM session WHERE email = ?1 OR email_unconfirmed = ?1",
&[&email],
|row| -> (Option<String>, Option<String>) { (row.get(0), row.get(1)) }) {} else {
//This email already exists!
return SignupResult::EmailTaken
}
session_user.username = Some(username.to_string());
session_user.email_unconfirmed = Some(email.to_string());
session_user.password = Some(password_hash);
session_user.salt = Some(salt.to_string());
self.save_session(session_user);
SignupResult::SignedUp
}
fn load_submission(&self, session: &SessionUser, task: i32, subtask: Option<&str>) -> Option<Submission> {
match subtask {
None => {
......
......@@ -41,6 +41,8 @@ use db_conn::MedalConnection;
use iron::typemap::Key;
pub use serde_json::value as json_val;
use db_conn::SignupResult;
static TASK_DIR: &str = "tasks";
macro_rules! mime {
......@@ -592,9 +594,43 @@ fn logout<C>(req: &mut Request) -> IronResult<Response>
Ok(Response::with((status::Found, Redirect(url_for!(req, "greet")))))
}
fn signup<C>(req: &mut Request) -> IronResult<Response>
where C: MedalConnection + std::marker::Send + 'static {
let query_string = req.url.query().map(|s| s.to_string());
let data = core::signupdata(query_string);
let mut resp = Response::new();
resp.set_mut(Template::new("signup", data)).set_mut(status::Ok);
Ok(resp)
}
fn signup_post<C>(req: &mut Request) -> IronResult<Response>
where C: MedalConnection + std::marker::Send + 'static {
let session_token = req.get_session_token();
let signupdata = {
let formdata = itry!(req.get_ref::<UrlEncodedBody>());
(iexpect!(formdata.get("username"))[0].to_owned(), iexpect!(formdata.get("email"))[0].to_owned(), iexpect!(formdata.get("password"))[0].to_owned())
};
let signupresult = with_conn![core::signup, C, req, session_token, signupdata].aug(req)?;
match signupresult {
SignupResult::SignedUp =>
Ok(Response::with((status::Found,
Redirect(iron::Url::parse(&format!("{}?status={:?}",
&url_for!(req, "profile"),
signupresult)).unwrap())))),
_ =>
Ok(Response::with((status::Found,
Redirect(iron::Url::parse(&format!("{}?status={:?}",
&url_for!(req, "signup"),
signupresult)).unwrap()))))
}
}
fn submission<C>(req: &mut Request) -> IronResult<Response>
where C: MedalConnection + std::marker::Send + 'static {
let task_id = req.expect_int::<i32>("taskid")?;
let session_token = req.expect_session_token()?;
let subtask: Option<String> = (|| -> Option<String> {
req.get_ref::<UrlEncodedQuery>().ok()?.get("subtask")?.get(0).map(|x| x.to_owned())
......@@ -1189,6 +1225,8 @@ pub fn start_server<C>(conn: C, config: Config) -> iron::error::HttpResult<iron:
login_post: post "/login" => login_post::<C>,
login_code_post: post "/clogin" => login_code_post::<C>,
logout: get "/logout" => logout::<C>,
signup: get "/signup" => signup::<C>,
signup_post: post "/signup" => signup_post::<C>,
subm: get "/submission/:taskid" => submission::<C>,
subm_post: post "/submission/:taskid" => submission_post::<C>,
subm_load: get "/load/:taskid" => submission::<C>,
......
......@@ -4,6 +4,11 @@
<h1>Benutzerdaten bearbeiten: {{profile_firstname}} {{profile_lastname}}</h1>
{{/if}}
{{#if SignedUp}}
<p style="color:green; font-weight:bold;">Du hast dir erfolgreich einen Account angelegt.<br/>
Auf dieser Seite kannst du weitere Angaben zu dir eintragen und dein Passwort ändern.</p>
{{/if}}
{{#if profile_username}}
<p>Benutzername: {{profile_username}}</p>
{{/if}}
......
<h1>Sign up</h1>
<div style="color:green; font-weight:bold">
{{#if UsernameTaken}}
<p>Dieser Benutzername existiert bereits. Bitte wähle einen anderen.</p>
{{/if}}
{{#if EmailTaken}}
<p>Diese E-Mail Adresse wird bereits von einem anderen Account verwendet. Bitte wähle eine andere.</p>
{{/if}}
{{#if UserLoggedIn}}
<p>Du bist bereits angemeldet. Wenn du einen neuen Account erstellen möchtest, dann melde dich zunächst ab.</p>
{{/if}}
{{#if EmptyFields}}
<p>Bitte fülle alle Felder aus, um dir einen Account anzulegen.</p>
{{/if}}
</div>
<p>
<form action="signup" method="post">
Beutzername:<br>
<input type="text" name="username" autofocus>
<br>
E-Mail Adresse:<br>
<input type="text" name="email">
<br>
Passwort:<br>
<input type="password" name="password" value="">
<br><br>
<input type="submit" value="sign up">
</form>
../default/signup.hbs
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment