5UNA2DEALCSRBINR27KSA6OMD6GQAXHYZ35ICQ7NB62G2XP4FT5QC {% extends "base" %}{% block body %}<div style="width:30%;" class="container-fluid"><form action="./sign_in" method="post"><div class="mb-3 row"><label for="user_name" class="col-sm-2 col-form-label">Username</label><div class="col-sm-10"><input type="text" class="form-control" name="user_name" required></div></div><div class="mb-3 row"><label for="password" class="col-sm-2 col-form-label">Password </label><div class="col-sm-10"><input type="password" class="form-control" name="password" required></div></div><button class="btn btn-primary" type="submit">Sign up</button></form></div>{% endblock body %}
{% extends "base" %}{% block body %}<div style="width:30%;" class="container-fluid"><form action="/users" method="post"><div class="mb-3 row"><label for="user_name" class="col-sm-2 col-form-label">Username</label><div class="col-sm-10"><input type="text" class="form-control" name="user_name" required></div></div><div class="mb-3 row"><label for="email" class="col-sm-2 col-form-label">Email</label><div class="col-sm-10"><input type="email" class="form-control" name="email" required></div></div><div class="mb-3 row"><label for="password" class="col-sm-2 col-form-label">Password </label><div class="col-sm-10"><input type="password" class="form-control" name="password" required></div></div><button class="btn btn-primary" type="submit">Sign up</button></form></div>{% endblock body %}
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Nidobyte</title></head><body>{% if flash %}<p>{{flash}}</p>{% endif %}{% block body %}{% endblock body %}</body></html>
use std::env;use rocket::{Rocket, Build};use rocket::fairing::{self, AdHoc};async fn init_db(rocket: Rocket<Build>) -> fairing::Result {let database_url = match env::var("DATABASE_URL") {Ok(val) => val,Err(e) => {error!("Failed to read DATABASE_URL environment variable: {}", e);return Err(rocket)}};let pool = sqlx::PgPool::connect(&database_url).await.expect("Failed to connect to the database");Ok(rocket.manage(pool))}pub fn stage() -> AdHoc {AdHoc::on_ignite("SQL Stage - PostgreSQL", |rocket| async {rocket.attach(AdHoc::try_on_ignite("Postgres Database", init_db))})}if let Err(e) = sqlx::migrate!("./migrations").run(&pool).await {error!("Failed to migrate PG database: {}", e);return Err(rocket);}
use rocket::{form::{Context, Form},http::{Cookie, CookieJar},response::{Flash, Redirect},State,};use rocket_dyn_templates::Template;use crate::database::Database;use crate::models;#[get("/new")]pub fn new_user() -> Template {Template::render("new_user", &Context::default())}#[derive(FromForm)]pub struct NewUser {// TODO validate against regexp: [a-z_][a-z0-9_-]*[$] - Needs a valid linux username#[field(validate = len(..20))]pub user_name: String,#[field(validate = len(3..))]pub email: String,#[field(validate = len(6..64))]pub password: String,}#[post("/", data = "<user>")]pub async fn create(db: &State<Database>,cookie: &CookieJar<'_>,user: Form<NewUser>,) -> Result<Flash<Redirect>, Flash<Redirect>> {// TODO When the form parsing fails, the users gets no feedbacklet mut new_user = models::User {id: -1,name: user.user_name.clone(),email: user.email.clone(),password: user.password.clone(),};match new_user.create(db).await {Ok(id) => {new_user.id = id;set_user_cookie(cookie, new_user);return Ok(Flash::success(Redirect::to("/"), "Signed up succesfully"));}Err(_e) => Err(Flash::error(Redirect::to("/users/new"),"Something went wrong",)), //TODO Show the error to the user in a flash message,}}#[derive(FromForm)]pub struct SignIn {#[field(validate = len(..20))]pub user_name: String,#[field(validate = len(6..64))]pub password: String,}#[get("/sign_in")]pub async fn get_sign_in() -> Template {Template::render("sign_in", &Context::default())}#[post("/sign_in", data = "<user>")]pub async fn sign_in(db: &State<Database>,jar: &CookieJar<'_>,user: Form<SignIn>,) -> Result<Flash<Redirect>, Flash<Redirect>> {// TODO figure out Rust and rewrite this without the nested matchingmatch models::User::authenticate(db, user.user_name.clone(), user.password.clone()).await {Ok(u) => match u {Some(u2) => {set_user_cookie(jar, u2);return Ok(Flash::success(Redirect::to("/"), "Signed in!"));}None => return Err(Flash::error(Redirect::to("./sign_in"), "Error signing in")),},Err(_e) => {return Err(Flash::error(Redirect::to("./sign_in"),"SQL Error signing in",))}}}static COOKIE_USER_ID_KEY: &str = "user_id";#[delete("/sign_out")]pub fn sign_out(jar: &CookieJar<'_>) -> Flash<Redirect> {jar.remove_private(Cookie::named(COOKIE_USER_ID_KEY));Flash::success(Redirect::to("/"), "Signed out succesfully")}fn set_user_cookie(jar: &CookieJar<'_>, user: models::User) {jar.add_private(Cookie::new(COOKIE_USER_ID_KEY, user.id.to_string()))}
use std::*;use rocket::{form::*, get, post, response::Redirect, routes, State};use rocket_auth::{prelude::Error, *};use rocket_dyn_templates::Template;use serde_json::json;use sqlx::*;use std::result::Result;use tokio::sync::*;#[get("/")]async fn index(user: Option<User>) -> Template {Template::render("index", json!({ "user": user }))}#[get("/login")]fn get_login() -> Template {Template::render("login", json!({}))}#[post("/login", data = "<form>")]async fn post_login(mut auth: Auth<'_>, form: Form<Login>) -> Result<Redirect, Error> {auth.login(&form).await?;Ok(Redirect::to("/"))}#[get("/signup")]async fn get_signup() -> Template {Template::render("signup", json!({}))}#[post("/signup", data = "<form>")]async fn post_signup(mut auth: Auth<'_>, form: Form<Signup>) -> Result<Redirect, Error> {auth.signup(&form).await?;auth.login(&form.into()).await?;Ok(Redirect::to("/"))}#[get("/logout")]fn logout(mut auth: Auth<'_>) -> Result<Template, Error> {auth.logout()?;Ok(Template::render("logout", json!({})))}fn routes() -> bool {routes![index,get_login,post_signup,get_signup,post_login,logout,]}
use crate::database::Database;use rocket::State;use sqlx::{query, query_as};use rocket::serde::{Deserialize, Serialize};#[derive(Debug, Clone, Deserialize, Serialize)]#[serde(crate = "rocket::serde")]pub struct User {pub id: i32,pub name: String,pub email: String,#[serde(skip_deserializing)]pub password: String,}impl User {pub async fn create(&self, db: &State<Database>) -> Result<i32, sqlx::Error> {let result = query!("INSERT INTO users (name, email, password) VALUES ($1, $2, crypt($3, gen_salt('bf'))) RETURNING id",&self.name,&self.email.to_lowercase(),&self.password).fetch_one(&**db).await?;// TODO when I figure out Rust, update self with this idOk(result.id)}/// Validates a user and password combination, returns a User struct when/// valid.pub async fn authenticate(db: &State<Database>,name: String,password: String,) -> Result<Option<User>, sqlx::Error> {let result = query_as!(User,"SELECT * FROM users WHERE name = $1 AND password = crypt($2, password)",name,password).fetch_optional(&**db).await?;Ok(result)}}
use std::env;use rocket::{fairing::{self, AdHoc},Build, Rocket,};pub(crate) type Database = sqlx::PgPool;async fn init_db(rocket: Rocket<Build>) -> fairing::Result {let database_url = match env::var("DATABASE_URL") {Ok(val) => val,Err(e) => {error!("Failed to read DATABASE_URL environment variable: {}", e);return Err(rocket);}};let pool = sqlx::PgPool::connect(&database_url).await.expect("Failed to connect to the database");if let Err(e) = sqlx::migrate!("./migrations").run(&pool).await {error!("Failed to migrate PG database: {}", e);return Err(rocket);}Ok(rocket.manage(pool))}pub fn stage() -> AdHoc {AdHoc::on_ignite("SQL Stage - PostgreSQL", |rocket| async {rocket.attach(AdHoc::try_on_ignite("Postgres Database", init_db))})}
-- Enable PG crypto helpers we need to store passwordsCREATE EXTENSION pgcrypto;CREATE TABLE users (id SERIAL PRIMARY KEY,name VARCHAR(64) NOT NULL,email TEXT NOT NULL,password TEXT NOT NULL);CREATE UNIQUE INDEX unique_user_name ON users(name);CREATE UNIQUE INDEX unique_user_email ON users(email);
- Test the sign up/sign in/sign out flow- Record changes
features = [ "runtime-tokio-rustls", "postgres", "macros", "migrate" ]
features = [ "runtime-tokio-rustls", "postgres", "macros", "migrate", "time", "chrono"][dependencies.rocket_sync_db_pools]version = "0.1.0-rc.1"default-features = falsefeatures = ["postgres_pool"][dependencies.rocket_dyn_templates]version = "0.1.0-rc.1"features = ["tera"]
name = "aead"version = "0.3.2"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331"dependencies = ["generic-array 0.14.4",][[package]]name = "aes"version = "0.6.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561"dependencies = ["aes-soft","aesni","cipher",][[package]]name = "aes-gcm"version = "0.8.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da"dependencies = ["aead","aes","cipher","ctr","ghash","subtle",][[package]]name = "aes-soft"version = "0.6.4"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072"dependencies = ["cipher","opaque-debug 0.3.0",][[package]]name = "aesni"version = "0.10.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce"dependencies = ["cipher","opaque-debug 0.3.0",][[package]]
"generic-array",
"generic-array 0.14.4",][[package]]name = "block-padding"version = "0.1.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"dependencies = ["byte-tools",][[package]]name = "bstr"version = "0.2.16"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279"dependencies = ["memchr",
[[package]]name = "chrono"version = "0.4.19"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"dependencies = ["libc","num-integer","num-traits","winapi 0.3.9",][[package]]name = "chrono-tz"version = "0.5.3"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "2554a3155fec064362507487171dcc4edc3df60cb10f3a1fb10ed8094822b120"dependencies = ["chrono","parse-zoneinfo",]
name = "fake-simd"version = "0.1.2"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"[[package]]name = "fallible-iterator"version = "0.2.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"[[package]]
][[package]]name = "fsevent"version = "0.4.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6"dependencies = ["bitflags","fsevent-sys",][[package]]name = "fsevent-sys"version = "2.0.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "f41b048a94555da0f42f1d632e2e19510084fb8e303b0daa2816e733fb3644a0"dependencies = ["libc",
name = "fuchsia-zircon"version = "0.3.3"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"dependencies = ["bitflags","fuchsia-zircon-sys",][[package]]name = "fuchsia-zircon-sys"version = "0.3.3"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"[[package]]
[[package]]name = "globset"version = "0.4.8"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd"dependencies = ["aho-corasick","bstr","fnv","log","regex",][[package]]name = "globwalk"version = "0.8.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc"dependencies = ["bitflags","ignore","walkdir",]
name = "ignore"version = "0.4.18"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d"dependencies = ["crossbeam-utils","globset","lazy_static","log","memchr","regex","same-file","thread_local","walkdir","winapi-util",][[package]]
version = "0.6.23"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4"dependencies = ["cfg-if 0.1.10","fuchsia-zircon","fuchsia-zircon-sys","iovec","kernel32-sys","libc","log","miow 0.2.2","net2","slab","winapi 0.2.8",][[package]]name = "mio"
"winapi",
"winapi 0.3.9",][[package]]name = "mio-extras"version = "2.0.6"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"dependencies = ["lazycell","log","mio 0.6.23","slab",][[package]]name = "miow"version = "0.2.2"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d"dependencies = ["kernel32-sys","net2","winapi 0.2.8","ws2_32-sys",
][[package]]name = "normpath"version = "0.3.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "27e6e8f70e9fbbe3752d330d769e3424f24b9458ce266df93a3b456902fd696a"dependencies = ["winapi 0.3.9",][[package]]name = "notify"version = "4.0.17"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "ae03c8c853dba7bfd23e571ff0cff7bc9dceb40a4cd684cd1681824183f45257"dependencies = ["bitflags","filetime","fsevent","fsevent-sys","inotify","libc","mio 0.6.23","mio-extras","walkdir","winapi 0.3.9",
"winapi",
"winapi 0.3.9",][[package]]name = "num-integer"version = "0.1.44"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"dependencies = ["autocfg","num-traits",
name = "pest_generator"version = "2.1.3"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"dependencies = ["pest","pest_meta","proc-macro2","quote","syn",][[package]]name = "pest_meta"version = "2.1.3"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"dependencies = ["maplit","pest","sha-1 0.8.2",][[package]]name = "phf"version = "0.8.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"dependencies = ["phf_shared",][[package]]name = "phf_shared"version = "0.8.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"dependencies = ["siphasher",][[package]]
[[package]]name = "polyval"version = "0.4.5"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd"dependencies = ["cpuid-bool","opaque-debug 0.3.0","universal-hash",][[package]]name = "postgres"version = "0.19.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "c7871ee579860d8183f542e387b176a25f2656b9fb5211e045397f745a68d1c2"dependencies = ["bytes","fallible-iterator","futures","log","tokio","tokio-postgres",]
name = "postgres-protocol"version = "0.6.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "ff3e0f70d32e20923cabf2df02913be7c1842d4c772db8065c00fcfdd1d1bff3"dependencies = ["base64","byteorder","bytes","fallible-iterator","hmac","md-5","memchr","rand","sha2","stringprep",][[package]]name = "postgres-types"version = "0.2.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "430f4131e1b7657b0cd9a2b0c3408d77c9a43a042d300b8c77f981dffcc43a2f"dependencies = ["bytes","fallible-iterator","postgres-protocol",][[package]]
][[package]]name = "r2d2"version = "0.8.9"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f"dependencies = ["log","parking_lot","scheduled-thread-pool",][[package]]name = "r2d2_postgres"version = "0.18.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "7029c56be658cb54f321e0bee597810ee16796b735fa2559d7056bf06b12230b"dependencies = ["postgres","r2d2",
name = "rocket_dyn_templates"version = "0.1.0-rc.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "c83f1287ad8fa034410928297a91db37518d5c46d7cc7e1e1b4a77aec0cd8807"dependencies = ["glob","normpath","notify","rocket","serde","serde_json","tera",][[package]]
name = "rocket_sync_db_pools"version = "0.1.0-rc.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "38cfdfebd552d075c368e641c88a5cd6ce1c58c5c710548aeb777abb48830f4b"dependencies = ["postgres","r2d2","r2d2_postgres","rocket","rocket_sync_db_pools_codegen","serde","tokio",][[package]]name = "rocket_sync_db_pools_codegen"version = "0.1.0-rc.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "5267808c094db5366e1d8925aaf9f2ce05ff9b3bd92cb18c7040a1fe219c2e25"dependencies = ["devise","quote",][[package]]
"winapi",
"winapi 0.3.9",][[package]]name = "tera"version = "1.12.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "bf95b0d8a46da5fe3ea119394a6c7f1e745f9de359081641c99946e2bf55d4f2"dependencies = ["chrono","chrono-tz","globwalk","humansize","lazy_static","percent-encoding","pest","pest_derive","rand","regex","serde","serde_json","slug","unic-segment",
name = "tokio-postgres"version = "0.7.2"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "2d2b1383c7e4fb9a09e292c7c6afb7da54418d53b045f1c1fac7a911411a2b8b"dependencies = ["async-trait","byteorder","bytes","fallible-iterator","futures","log","parking_lot","percent-encoding","phf","pin-project-lite","postgres-protocol","postgres-types","socket2","tokio","tokio-util",][[package]]
name = "unic-char-property"version = "0.9.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"dependencies = ["unic-char-range",][[package]]name = "unic-char-range"version = "0.9.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"[[package]]name = "unic-common"version = "0.9.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"[[package]]name = "unic-segment"version = "0.9.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23"dependencies = ["unic-ucd-segment",][[package]]name = "unic-ucd-segment"version = "0.9.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700"dependencies = ["unic-char-property","unic-char-range","unic-ucd-version",][[package]]name = "unic-ucd-version"version = "0.9.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"dependencies = ["unic-common",][[package]]