From 59d0ed12c14beab56170c5e9ce2e807db4e7473c Mon Sep 17 00:00:00 2001 From: SadlyNotSappho Date: Tue, 6 Feb 2024 11:35:25 -0800 Subject: [PATCH] create admin user, partially rebrand to login template --- .env.example | 8 ++ .gitignore | 3 +- README.md | 10 +-- ...-compose.yml.example => docker-compose.yml | 19 ++--- src/main.rs | 80 +++++++++++-------- src/tables.rs | 67 +++++++++------- 6 files changed, 104 insertions(+), 83 deletions(-) create mode 100644 .env.example rename docker-compose.yml.example => docker-compose.yml (77%) diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e7c55a3 --- /dev/null +++ b/.env.example @@ -0,0 +1,8 @@ +POSTGRES_DB=fossil_postgres +POSTGRES_PASSWORD=password + +ROCKET_SECRET_KEY=openssl rand -base64 32 + +ADMIN_USERNAME=admin +ADMIN_PASSWORD=password + diff --git a/.gitignore b/.gitignore index 5afd3f7..304cedc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ /target -docker-compose.yml db/password.txt - +.env diff --git a/README.md b/README.md index fe56121..5bcfa93 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ -# fossil +# login template -Free and Open Source Software - Library +this is pretty much just some shitty code that'll handle logging into and out of a website, with the default features being a blog, but it should be easy to change to something else -yeah yeah there's no i in the name, i just wanted the acronym to be an actual word - -setup: remove .example from `docker-compose.yml.example` and `db/password.txt.example` -\nfill out those files with the correct values +setup: remove .example from `.env.example` +\nfill it with the correct values (for `ROCKET_SECRET_KEY`, run that command to generate it) \nrun `docker compose build` to build it, `docker compose run` to run, and `docker compose run -d` to run it in the background diff --git a/docker-compose.yml.example b/docker-compose.yml similarity index 77% rename from docker-compose.yml.example rename to docker-compose.yml index bb0cb8a..b73a58b 100644 --- a/docker-compose.yml.example +++ b/docker-compose.yml @@ -15,14 +15,16 @@ services: ports: - 8000:8000 environment: - # name in main.rs db.user POSTGRES_PASSWORD POSTGRES_DB - - ROCKET_DATABASES={diesel_postgres={url="postgres://postgres:passwordpasswordpassword@db/example"}} + - ROCKET_DATABASES={fossil_postgres={url="postgres://postgres:$POSTGRES_PASSWORD@db/$POSTGRES_DB", max_connections=10, connect_timeout=10}} - ROCKET_ADDRESS=0.0.0.0 - - ROCKET_PORT=8000 + - ROCKET_PORT=$PORT - RUST_LOG=debug - - ROCKET_SECRET_KEY=openssl rand -base64 32 + - ROCKET_SECRET_KEY=$ROCKET_SECRET_KEY + + - ADMIN_USERNAME=$ADMIN_USERNAME + - ADMIN_PASSWORD=$ADMIN_PASSWORD volumes: - type: bind source: ./web @@ -40,13 +42,11 @@ services: image: postgres restart: always user: postgres - secrets: - - db-password volumes: - db-data:/var/lib/postgresql/data environment: - - POSTGRES_DB=example - - POSTGRES_PASSWORD=passwordpasswordpassword + - POSTGRES_DB=$POSTGRES_DB + - POSTGRES_PASSWORD=$POSTGRES_PASSWORD expose: - 5432 healthcheck: @@ -56,6 +56,3 @@ services: retries: 5 volumes: db-data: -secrets: - db-password: - file: db/password.txt diff --git a/src/main.rs b/src/main.rs index 71b00e1..1a71bec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -69,34 +69,18 @@ async fn createuser( } } -#[get("/getuser/")] -async fn getuser(db: Connection, username: String) -> String { - let user = User::get_by_username(db, &username).await; - match user { - Some(user) => format!( - "id: {}\nusername: {}\nhashed password: {}\ntoken: {}", - user.id, - user.username, - user.password, - match user.token { - Some(t) => t, - None => "no token".to_string() - } - ), - None => format!("User {} doesn't exist.", &username), - } -} - #[get("/account")] async fn account(db: Connection, cookies: &CookieJar<'_>) -> String { let token = cookies.get_private("token"); match token { Some(t) => { - let user = User::get_by_token(db, t.to_string().split("=").collect::>()[1].to_string() /*GOD I LOVE RUST*/).await; - format!("Username: {}", user.username) + match User::get_by_token(db, t.to_string().split('=').collect::>()[1].to_string() /*GOD I LOVE RUST*/).await { + Some(user) => format!("Username: {}", user.username), + None => "User doesn't exist.".to_string() + } }, None => { - format!("Not logged in") + "Not logged in".to_string() } } } @@ -105,12 +89,8 @@ async fn account(db: Connection, cookies: &CookieJar<'_>) -> String { async fn login(db: Connection, db2: Connection, info: Json, cookies: &CookieJar<'_>) -> String { let token = cookies.get_private("token"); match token { - Some(t) => { - if User::get_by_token(db, t.to_string()).await.exists() /*god i fucking love rust, this function literally just returns true*/ { - "logged in with token".to_string() - } else { - "unknown token".to_string() - } + Some(_) => { + "already logged in".to_string() } None => { match User::get_by_username(db, &info.username).await { @@ -140,12 +120,12 @@ async fn login(db: Connection, db2: Connection, info: Json, c } #[post("/logout")] async fn logout(cookies: &CookieJar<'_>) -> &'static str { - let token = cookies.get_private("token"); - if token.is_some() { - cookies.remove_private("token"); - "logged out" - } else { - "not logged in" + match cookies.get_private("token") { + Some(_) => { + cookies.remove_private("token"); + "Logged out." + }, + None => "Not logged in." } } @@ -192,7 +172,7 @@ async fn migrate(rocket: Rocket) -> Rocket { )", )) .await; - let _2 = conn + let _ = conn .fetch_one(sqlx::query( "CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, @@ -203,6 +183,36 @@ async fn migrate(rocket: Rocket) -> Rocket { )) .await; + match conn.fetch_one(sqlx::query("SELECT * from users WHERE username = $1").bind(std::env::var("ADMIN_USERNAME").expect("make ADMIN_USERNAME env var"))).await { + Ok(_) => (), + Err(_) => { + // yes, User::create() exists. no, conn isn't the right type. no, i won't even attempt + // to fix it. yes, this works just as well as that. fuck you. + let username = std::env::var("ADMIN_USERNAME").expect("make ADMIN_USERNAME env var"); + let password = sha256::digest(std::env::var("ADMIN_PASSWORD").expect("make ADMIN_PASSWORD env var")); + + eprintln!("{username}\n{password}"); + + conn.fetch_one(sqlx::query( + + + + + + + r#" + INSERT INTO users (username, password) + VALUES ($1, $2); + "#, + ) + .bind(username) + .bind(password) + ) + .await + .expect("couldn't create admin user"); + } + } + rocket } @@ -214,7 +224,7 @@ async fn main() { .attach(AdHoc::on_ignite("DB Migrations", migrate)) .mount( "/", - routes![hello, get_book, delay, login, logout, dbtest, dbcreate, createuser, getuser, account], + routes![hello, get_book, delay, login, logout, dbtest, dbcreate, createuser, account], ) .register("/", catchers![default_catcher]) .mount("/login", FileServer::from("/srv/web")) diff --git a/src/tables.rs b/src/tables.rs index a5b4919..98329bd 100644 --- a/src/tables.rs +++ b/src/tables.rs @@ -1,4 +1,4 @@ -use rand::{SeedableRng, RngCore}; +use rand::{RngCore, SeedableRng}; use rocket_db_pools::sqlx::Executor; use rocket_db_pools::Connection; use sqlx::FromRow; @@ -82,42 +82,44 @@ impl User { } } } - pub async fn get_by_id(mut db: Connection, id: i32) -> User { - let res = db + pub async fn get_by_id(mut db: Connection, id: i32) -> Option { + match db .fetch_one(sqlx::query("SELECT * FROM users WHERE id = $1;").bind(id)) .await - .unwrap(); - User { - id: res.get::("id"), - username: res.get::("username"), - password: res.get::("password"), - token: res.get::, _>("token"), + { + Ok(user) => Some(User { + id: user.get::("id"), + username: user.get::("username"), + password: user.get::("password"), + token: user.get::, _>("token"), + }), + Err(_) => None, } } - pub async fn get_by_token(mut db: Connection, token: String) -> User { - let res = db + pub async fn get_by_token(mut db: Connection, token: String) -> Option { + match db .fetch_one(sqlx::query("SELECT * FROM users WHERE token = $1;").bind(token)) .await - // TODO: this errors sometimes i dont know why - .unwrap(); - User { - id: res.get::("id"), - username: res.get::("username"), - password: res.get::("password"), - token: res.get::, _>("token"), + { + Ok(user) => Some(User { + id: user.get::("id"), + username: user.get::("username"), + password: user.get::("password"), + token: user.get::, _>("token"), + }), + Err(_) => None, } } pub async fn get_by_username(mut db: Connection, username: &String) -> Option { - let res = db + match db .fetch_one(sqlx::query("SELECT * FROM users WHERE username = $1;").bind(username)) - .await; - - match res { - Ok(res) => Some(User { - id: res.get::("id"), - username: res.get::("username"), - password: res.get::("password"), - token: res.get::, _>("token"), + .await + { + Ok(user) => Some(User { + id: user.get::("id"), + username: user.get::("username"), + password: user.get::("password"), + token: user.get::, _>("token"), }), Err(_) => None, } @@ -128,9 +130,16 @@ impl User { let token = sha256::digest(format!("{token_start}-{token_end}")); - match db.fetch_one(sqlx::query("UPDATE users SET token = $1 WHERE id = $2").bind(&token).bind(self.id)).await { + match db + .fetch_one( + sqlx::query("UPDATE users SET token = $1 WHERE id = $2") + .bind(&token) + .bind(self.id), + ) + .await + { Ok(_) => Ok(token), - Err(why) => Err(why.to_string()) + Err(why) => Err(why.to_string()), } }