diff --git a/Rb.yaml b/Rb.yaml index db78338..e3f980a 100644 --- a/Rb.yaml +++ b/Rb.yaml @@ -23,21 +23,13 @@ debug: url: "postgres://rb:rb@localhost:5432/rb" release: - keep_alive: 5 - read_timeout: 5 - write_timeout: 5 - log_level: "normal" - limits: - forms: 32768 - admin_user: "admin" admin_pass: "password" jwt: key: "secret" refresh_token_size: 64 - # Just 5 seconds for debugging - refresh_token_expire: 60 + refresh_token_expire: 86400 databases: postgres_rb: - url: "postgres://rb:rb@localhost:5432/rb" + url: "postgres://rb:rb@db:5432/rb" diff --git a/migrations/2021-09-13-143540_sections/down.sql b/migrations/2021-09-13-143540_sections/down.sql deleted file mode 100644 index 7af43ff..0000000 --- a/migrations/2021-09-13-143540_sections/down.sql +++ /dev/null @@ -1,7 +0,0 @@ --- This file should undo anything in `up.sql` -drop trigger insert_enforce_post_titles on posts; -drop trigger update_enforce_post_titles on posts; -drop function enforce_post_titles; - -drop table posts cascade; -drop table sections cascade; diff --git a/migrations/2021-09-13-143540_sections/up.sql b/migrations/2021-09-13-143540_sections/up.sql deleted file mode 100644 index 0c5ca76..0000000 --- a/migrations/2021-09-13-143540_sections/up.sql +++ /dev/null @@ -1,56 +0,0 @@ --- Your SQL goes here -create table sections ( - id uuid DEFAULT gen_random_uuid() PRIMARY KEY, - - -- Title of the section - title varchar(255) UNIQUE NOT NULL, - -- Optional description of the section - description text, - -- Wether to show the section in the default list on the homepage - is_default boolean NOT NULL DEFAULT false, - -- Wether the posts should contain titles or not - has_titles boolean NOT NULL DEFAULT true -); - -create table posts ( - id uuid DEFAULT gen_random_uuid() PRIMARY KEY, - - section_id uuid NOT NULL REFERENCES sections(id) ON DELETE CASCADE, - -- Title of the post - -- Wether this is NULL or not is enforced using the enforce_post_titles trigger - title varchar(255), - -- Post date, defaults to today - publish_date date NOT NULL DEFAULT now(), - -- Content of the post - content text NOT NULL -); - -create function enforce_post_titles() returns trigger as $enforce_post_titles$ - begin - -- Check for a wrongfully null title - if new.title is null and exists ( - select 1 from sections where id = new.section_id and has_titles - ) then - raise exception 'Expected a post title, but got null.'; - end if; - - if new.title is not null and exists ( - select 1 from sections where id = new.section_id and not has_titles - ) then - raise exception 'Expected an empty post title, but got a value.'; - end if; - - return new; - end; -$enforce_post_titles$ language plpgsql; - -create trigger insert_enforce_post_titles - before insert on posts - for each row - execute function enforce_post_titles(); - -create trigger update_enforce_post_titles - before update of title on posts - for each row - when (old.title is distinct from new.title) - execute function enforce_post_titles(); diff --git a/rustfmt.toml b/rustfmt.toml index 8e8627b..5e52857 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -36,7 +36,7 @@ license_template_path = "" make_backup = false match_arm_blocks = true match_arm_leading_pipes = "Never" -match_block_trailing_comma = true +match_block_trailing_comma = false max_width = 100 merge_derives = true newline_style = "Auto" diff --git a/src/db/mod.rs b/src/db/mod.rs index 35e4995..9c831dd 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,11 +1,5 @@ -//! The db module contains all Diesel-related logic. This is to prevent the various Diesel imports -//! from poluting other modules' namespaces. - -pub mod posts; -pub mod sections; pub mod tokens; pub mod users; -pub use sections::{NewSection, Section}; pub use tokens::{NewRefreshToken, RefreshToken}; pub use users::{NewUser, User}; diff --git a/src/db/posts.rs b/src/db/posts.rs deleted file mode 100644 index 163902e..0000000 --- a/src/db/posts.rs +++ /dev/null @@ -1,58 +0,0 @@ -use chrono::NaiveDate; -use diesel::{insert_into, prelude::*, Insertable, PgConnection, Queryable}; -use uuid::Uuid; - -use crate::{ - errors::{RbError, RbResult}, - schema::{posts, posts::dsl::*}, -}; - -#[derive(Queryable)] -pub struct Post -{ - pub id: Uuid, - pub section_id: Uuid, - pub title: Option, - pub publish_date: NaiveDate, - pub content: String, -} - -#[derive(Insertable)] -#[table_name = "posts"] -pub struct NewPost -{ - pub section_id: Uuid, - pub title: Option, - pub publish_date: NaiveDate, -} - -/// Returns all posts in the database; should be used with care as this method could quickly return -/// a large amount of data. -/// -/// # Arguments -/// -/// * `conn` - a reference to a database connection -pub fn all(conn: &PgConnection) -> RbResult> -{ - posts - .load::(conn) - .map_err(|_| RbError::DbError("Couldn't get all posts.")) -} - -/// Insert a new post into the database. -/// -/// # Arguments -/// -/// * `conn` - reference to a database connection -/// * `new_post` - the new post object to insert -pub fn create(conn: &PgConnection, new_post: &NewPost) -> RbResult<()> -{ - insert_into(posts) - .values(new_post) - .execute(conn) - .map_err(|_| RbError::DbError("Couldn't insert post."))?; - - // TODO check for conflict? - - Ok(()) -} diff --git a/src/db/sections.rs b/src/db/sections.rs deleted file mode 100644 index b429c85..0000000 --- a/src/db/sections.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! Handles all section-related database operations. - -use diesel::{insert_into, prelude::*, Insertable, PgConnection, Queryable}; -use serde::Deserialize; -use uuid::Uuid; - -use crate::{ - errors::{RbError, RbResult}, - schema::{sections, sections::dsl::*}, -}; - -/// Represents a section contained in the database. -#[derive(Queryable)] -pub struct Section -{ - pub id: Uuid, - pub title: String, - pub description: Option, - pub is_default: bool, - pub has_titles: bool, -} - -/// A new section to be added into the database. -#[derive(Deserialize, Insertable)] -#[table_name = "sections"] -#[serde(rename_all = "camelCase")] -pub struct NewSection -{ - title: String, - description: Option, - is_default: Option, - has_titles: Option, -} - -/// Returns all sections in the database. -/// -/// # Arguments -/// -/// * `conn` - reference to a database connection -pub fn all(conn: &PgConnection) -> RbResult> -{ - sections - .load::
(conn) - .map_err(|_| RbError::DbError("Couldn't get all sections")) -} - -/// Inserts a new section into the database. -/// -/// # Arguments -/// -/// * `conn` - reference to a database connection -/// * `new_section` - the new section to be added -pub fn create(conn: &PgConnection, new_section: &NewSection) -> RbResult<()> -{ - insert_into(sections) - .values(new_section) - .execute(conn) - .map_err(|_| RbError::DbError("Couldn't insert section."))?; - - // TODO check for conflict? - - Ok(()) -} diff --git a/src/db/tokens.rs b/src/db/tokens.rs index cbb8898..8940721 100644 --- a/src/db/tokens.rs +++ b/src/db/tokens.rs @@ -1,5 +1,3 @@ -//! Handles refresh token-related database operations. - use diesel::{insert_into, prelude::*, Insertable, PgConnection, Queryable}; use uuid::Uuid; @@ -8,7 +6,6 @@ use crate::{ schema::{refresh_tokens, refresh_tokens::dsl::*}, }; -/// A refresh token as stored in the database #[derive(Queryable)] pub struct RefreshToken { @@ -18,7 +15,6 @@ pub struct RefreshToken pub last_used_at: Option, } -/// A new refresh token to be added into the database #[derive(Insertable)] #[table_name = "refresh_tokens"] pub struct NewRefreshToken @@ -28,12 +24,6 @@ pub struct NewRefreshToken pub expires_at: chrono::NaiveDateTime, } -// TODO add pagination as this could grow very quickly -/// Returns all refresh tokens contained in the database. -/// -/// # Arguments -/// -/// * `conn` - database connection to use pub fn all(conn: &PgConnection) -> RbResult> { refresh_tokens @@ -41,30 +31,18 @@ pub fn all(conn: &PgConnection) -> RbResult> .map_err(|_| RbError::DbError("Couldn't get all refresh tokens.")) } -/// Insert a new refresh token into the database. -/// -/// # Arguments -/// -/// * `conn` - database connection to use -/// * `new_refresh_token` - token to insert pub fn create(conn: &PgConnection, new_refresh_token: &NewRefreshToken) -> RbResult<()> { insert_into(refresh_tokens) .values(new_refresh_token) .execute(conn) - .map_err(|_| RbError::DbError("Couldn't insert refresh token."))?; + .map_err(|_| RbError::Custom("Couldn't insert refresh token."))?; // TODO check for conflict? Ok(()) } -/// Returns the token & user data associated with the given refresh token value. -/// -/// # Arguments -/// -/// * `conn` - database connection to use -/// * `token_val` - token value to search for pub fn find_with_user( conn: &PgConnection, token_val: &[u8], @@ -75,20 +53,10 @@ pub fn find_with_user( .inner_join(crate::schema::users::dsl::users) .filter(token.eq(token_val)) .first::<(RefreshToken, super::users::User)>(conn) - .map_err(|_| RbError::DbError("Couldn't get refresh token & user.")) + .map_err(|_| RbError::Custom("Couldn't get refresh token & user.")) .ok() } -/// Updates a token's `last_used_at` column value. -/// -/// # Arguments -/// -/// * `conn` - database connection to use -/// * `token_` - value of the refresh token to update -/// * `last_used_at_` - date value to update column with -/// -/// **NOTE**: argument names use trailing underscores as to not conflict with Diesel's imported dsl -/// names. pub fn update_last_used_at( conn: &PgConnection, token_: &[u8], diff --git a/src/db/users.rs b/src/db/users.rs index 37ef9c2..efc74db 100644 --- a/src/db/users.rs +++ b/src/db/users.rs @@ -1,5 +1,3 @@ -//! Handles user-related database operations. - use diesel::{prelude::*, AsChangeset, Insertable, Queryable}; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -9,7 +7,6 @@ use crate::{ schema::{users, users::dsl::*}, }; -/// A user as stored in the database. #[derive(Queryable, Serialize)] pub struct User { @@ -21,7 +18,6 @@ pub struct User pub admin: bool, } -/// A new user to add to the database. #[derive(Insertable, AsChangeset, Deserialize)] #[table_name = "users"] pub struct NewUser @@ -31,11 +27,6 @@ pub struct NewUser pub admin: bool, } -/// Returns all users in the database. -/// -/// # Arguments -/// -/// * `conn` - database connection to use pub fn all(conn: &PgConnection) -> RbResult> { users @@ -43,23 +34,11 @@ pub fn all(conn: &PgConnection) -> RbResult> .map_err(|_| RbError::DbError("Couldn't get all users.")) } -/// Find a user with a given ID. -/// -/// # Arguments -/// -/// * `conn` - database connection to use -/// * `user_id` - ID to search for pub fn find(conn: &PgConnection, user_id: Uuid) -> Option { users.find(user_id).first::(conn).ok() } -/// Find a user with a given username. -/// -/// # Arguments -/// -/// * `conn` - database connection to use -/// * `username_` - username to search for pub fn find_by_username(conn: &PgConnection, username_: &str) -> RbResult { Ok(users @@ -68,12 +47,6 @@ pub fn find_by_username(conn: &PgConnection, username_: &str) -> RbResult .map_err(|_| RbError::DbError("Couldn't find users by username."))?) } -/// Insert a new user into the database -/// -/// # Arguments -/// -/// * `conn` - database connection to use -/// * `new_user` - user to insert pub fn create(conn: &PgConnection, new_user: &NewUser) -> RbResult<()> { let count = diesel::insert_into(users) @@ -88,12 +61,6 @@ pub fn create(conn: &PgConnection, new_user: &NewUser) -> RbResult<()> Ok(()) } -/// Either create a new user or update an existing one on conflict. -/// -/// # Arguments -/// -/// * `conn` - database connection to use -/// * `new_user` - user to insert/update pub fn create_or_update(conn: &PgConnection, new_user: &NewUser) -> RbResult<()> { diesel::insert_into(users) @@ -107,12 +74,6 @@ pub fn create_or_update(conn: &PgConnection, new_user: &NewUser) -> RbResult<()> Ok(()) } -/// Delete the user with the given ID. -/// -/// # Arguments -/// -/// `conn` - database connection to use -/// `user_id` - ID of user to delete pub fn delete(conn: &PgConnection, user_id: Uuid) -> RbResult<()> { diesel::delete(users.filter(id.eq(user_id))) @@ -122,14 +83,6 @@ pub fn delete(conn: &PgConnection, user_id: Uuid) -> RbResult<()> Ok(()) } -/// Block a user given an ID. -/// In practice, this means updating the user's entry so that the `blocked` column is set to -/// `true`. -/// -/// # Arguments -/// -/// `conn` - database connection to use -/// `user_id` - ID of user to block pub fn block(conn: &PgConnection, user_id: Uuid) -> RbResult<()> { diesel::update(users.filter(id.eq(user_id))) diff --git a/src/errors.rs b/src/errors.rs index 1f9aff3..bb7856a 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -61,7 +61,7 @@ impl RbError RbError::AuthInvalidRefreshToken => "This refresh token is not valid.", RbError::AuthDuplicateRefreshToken => { "This refresh token has already been used. The user has been blocked." - }, + } RbError::AuthMissingHeader => "Missing Authorization header.", RbError::UMDuplicateUser => "This user already exists.", diff --git a/src/guards.rs b/src/guards.rs index 3510163..7b40bdd 100644 --- a/src/guards.rs +++ b/src/guards.rs @@ -10,7 +10,7 @@ use sha2::Sha256; use crate::{auth::jwt::Claims, errors::RbError, RbConfig}; -/// Extracts an "Authorization: Bearer" string from the headers. +/// Extracts a "Authorization: Bearer" string from the headers. pub struct Bearer<'a>(&'a str); #[rocket::async_trait] @@ -22,7 +22,7 @@ impl<'r> FromRequest<'r> for Bearer<'r> { // If the header isn't present, just forward to the next route let header = match req.headers().get_one("Authorization") { - None => return Outcome::Forward(()), + None => return Outcome::Failure((Status::BadRequest, Self::Error::AuthMissingHeader)), Some(val) => val, }; @@ -31,10 +31,12 @@ impl<'r> FromRequest<'r> for Bearer<'r> } // Extract the jwt token from the header - match header.get(7..) { - Some(s) => Outcome::Success(Self(s)), - None => Outcome::Failure((Status::Unauthorized, Self::Error::AuthUnauthorized)), - } + let auth_string = match header.get(7..) { + Some(s) => s, + None => return Outcome::Failure((Status::Unauthorized, Self::Error::AuthUnauthorized)), + }; + + Outcome::Success(Self(auth_string)) } } @@ -61,14 +63,14 @@ impl<'r> FromRequest<'r> for Jwt Status::InternalServerError, Self::Error::Custom("Failed to do Hmac thing."), )) - }, + } }; // Verify token using key let claims: Claims = match bearer.verify_with_key(&key) { Ok(claims) => claims, Err(_) => { return Outcome::Failure((Status::Unauthorized, Self::Error::AuthUnauthorized)) - }, + } }; Outcome::Success(Self(claims)) diff --git a/src/main.rs b/src/main.rs index 4a6db39..2c0c7c4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,7 +27,6 @@ pub mod db; pub mod errors; pub mod guards; pub(crate) mod schema; -pub mod sections; #[global_allocator] static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; @@ -111,5 +110,4 @@ fn rocket() -> _ "/api/admin", routes![admin::get_users, admin::create_user, admin::get_user_info], ) - .mount("/api/sections", routes![sections::create_section]) } diff --git a/src/schema.rs b/src/schema.rs index 45b9813..e3854e3 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -1,13 +1,3 @@ -table! { - posts (id) { - id -> Uuid, - section_id -> Uuid, - title -> Nullable, - publish_date -> Date, - content -> Text, - } -} - table! { refresh_tokens (token) { token -> Bytea, @@ -17,16 +7,6 @@ table! { } } -table! { - sections (id) { - id -> Uuid, - title -> Varchar, - description -> Nullable, - is_default -> Bool, - has_titles -> Bool, - } -} - table! { users (id) { id -> Uuid, @@ -37,7 +17,6 @@ table! { } } -joinable!(posts -> sections (section_id)); joinable!(refresh_tokens -> users (user_id)); -allow_tables_to_appear_in_same_query!(posts, refresh_tokens, sections, users,); +allow_tables_to_appear_in_same_query!(refresh_tokens, users,); diff --git a/src/sections.rs b/src/sections.rs deleted file mode 100644 index e4def24..0000000 --- a/src/sections.rs +++ /dev/null @@ -1,24 +0,0 @@ -//! This module handles management of site sections (aka blogs). - -use rocket::serde::json::Json; - -use crate::{db, errors::RbResult, guards::Admin, RbDbConn}; - -/// Route for creating a new section. -/// -/// # Arguments -/// -/// * `_admin` - guard ensuring user is admin -/// * `conn` - guard providing a connection to the database -/// * `new_section` - Json-encoded NewSection object -#[post("/", data = "")] -pub async fn create_section( - _admin: Admin, - conn: RbDbConn, - new_section: Json, -) -> RbResult<()> -{ - Ok(conn - .run(move |c| db::sections::create(c, &new_section.into_inner())) - .await?) -} diff --git a/tests/admin.py b/tests/admin.py index 069c2dd..19a1c5b 100644 --- a/tests/admin.py +++ b/tests/admin.py @@ -2,7 +2,7 @@ import requests class RbClient: - def __init__(self, username = "admin", password = "password", base_url = "http://localhost:8000/api"): + def __init__(self, username, password, base_url = "http://localhost:8000/api"): self.username = username self.password = password self.base_url = base_url @@ -17,7 +17,6 @@ class RbClient: }) if r.status_code != 200: - print(r.text) raise Exception("Couldn't login") res = r.json() @@ -57,15 +56,9 @@ class RbClient: def get(self, url, *args, **kwargs): return self._request("GET", f"{self.base_url}{url}", *args, **kwargs) - def post(self, url, *args, **kwargs): - return self._request("POST", f"{self.base_url}{url}", *args, **kwargs) - if __name__ == "__main__": - client = RbClient() + client = RbClient("admin", "password") - # print(client.get("/admin/users").json()) - client.post("/sections", json={ - "title": "this is a title" - }) + print(client.get("/admin/users").json()) diff --git a/web/package.json b/web/package.json index b1070aa..6dfbfa6 100644 --- a/web/package.json +++ b/web/package.json @@ -7,9 +7,7 @@ "build": "astro build" }, "devDependencies": { - "@astrojs/renderer-svelte": "^0.1.1", "astro": "0.19.0-next.2", - "miragejs": "^0.1.41", - "typescript": "^4.4.3" + "@astrojs/renderer-svelte": "^0.1.1" } } diff --git a/web/src/components/MirageTest.svelte b/web/src/components/MirageTest.svelte deleted file mode 100644 index ac3676b..0000000 --- a/web/src/components/MirageTest.svelte +++ /dev/null @@ -1,19 +0,0 @@ - - - diff --git a/web/src/components/SvelteCounter.svelte b/web/src/components/SvelteCounter.svelte index ea6cf52..f493c25 100644 --- a/web/src/components/SvelteCounter.svelte +++ b/web/src/components/SvelteCounter.svelte @@ -1,4 +1,4 @@ -