From 3f0838ce8072a2a436f6a515898ba5e98180903f Mon Sep 17 00:00:00 2001 From: Dessalines Date: Tue, 21 Sep 2021 15:33:06 -0400 Subject: [PATCH] Making Secrets a Singleton. --- crates/api/src/lib.rs | 8 ++-- crates/api/src/local_user.rs | 20 +++++----- crates/api_common/src/lib.rs | 12 +++--- crates/api_crud/src/user/create.rs | 8 ++-- crates/db_queries/src/source/mod.rs | 2 +- crates/db_queries/src/source/secret.rs | 37 +++++++++++++++++++ crates/db_queries/src/source/secrets.rs | 31 ---------------- crates/db_schema/src/schema.rs | 2 +- crates/db_schema/src/source/mod.rs | 2 +- crates/db_schema/src/source/secret.rs | 8 ++++ crates/db_schema/src/source/secrets.rs | 8 ---- crates/routes/src/feeds.rs | 12 +++--- crates/routes/src/images.rs | 11 ++---- crates/utils/src/claims.rs | 8 ++-- .../2021-09-20-112945_jwt-secret/down.sql | 2 +- .../2021-09-20-112945_jwt-secret/up.sql | 13 +++---- 16 files changed, 93 insertions(+), 91 deletions(-) create mode 100644 crates/db_queries/src/source/secret.rs delete mode 100644 crates/db_queries/src/source/secrets.rs create mode 100644 crates/db_schema/src/source/secret.rs delete mode 100644 crates/db_schema/src/source/secrets.rs diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs index 3969570f9..1146975f0 100644 --- a/crates/api/src/lib.rs +++ b/crates/api/src/lib.rs @@ -190,13 +190,13 @@ mod tests { use lemmy_api_common::check_validator_time; use lemmy_db_queries::{ establish_unpooled_connection, - source::{local_user::LocalUser_, secrets::Secrets_}, + source::{local_user::LocalUser_, secret::SecretSingleton}, Crud, }; use lemmy_db_schema::source::{ local_user::{LocalUser, LocalUserForm}, person::{Person, PersonForm}, - secrets::Secrets, + secret::Secret, }; use lemmy_utils::claims::Claims; @@ -219,8 +219,8 @@ mod tests { let inserted_local_user = LocalUser::create(&conn, &local_user_form).unwrap(); - let jwt_secret = Secrets::read_jwt_secret(&conn).unwrap(); - let jwt = Claims::jwt(inserted_local_user.id.0, jwt_secret.as_ref()).unwrap(); + let jwt_secret = Secret::get().jwt_secret; + let jwt = Claims::jwt(inserted_local_user.id.0, &jwt_secret).unwrap(); let claims = Claims::decode(&jwt, jwt_secret.as_ref()).unwrap().claims; let check = check_validator_time(&inserted_local_user.validator_time, &claims); assert!(check.is_ok()); diff --git a/crates/api/src/local_user.rs b/crates/api/src/local_user.rs index 03522301a..df03e3020 100644 --- a/crates/api/src/local_user.rs +++ b/crates/api/src/local_user.rs @@ -25,7 +25,7 @@ use lemmy_db_queries::{ person_mention::PersonMention_, post::Post_, private_message::PrivateMessage_, - secrets::Secrets_, + secret::SecretSingleton, }, Blockable, Crud, @@ -44,7 +44,7 @@ use lemmy_db_schema::{ person_mention::*, post::Post, private_message::PrivateMessage, - secrets::Secrets, + secret::Secret, site::*, }, }; @@ -105,9 +105,9 @@ impl Perform for Login { } // Return the jwt - let jwt_secret = blocking(context.pool(), move |conn| Secrets::read_jwt_secret(conn)).await??; + let jwt_secret = Secret::get().jwt_secret; Ok(LoginResponse { - jwt: Claims::jwt(local_user_view.local_user.id.0, jwt_secret.as_ref())?, + jwt: Claims::jwt(local_user_view.local_user.id.0, &jwt_secret)?, }) } } @@ -271,9 +271,9 @@ impl Perform for SaveUserSettings { }; // Return the jwt - let jwt_secret = blocking(context.pool(), move |conn| Secrets::read_jwt_secret(conn)).await??; + let jwt_secret = Secret::get().jwt_secret; Ok(LoginResponse { - jwt: Claims::jwt(updated_local_user.id.0, jwt_secret.as_ref())?, + jwt: Claims::jwt(updated_local_user.id.0, &jwt_secret)?, }) } } @@ -315,9 +315,9 @@ impl Perform for ChangePassword { .await??; // Return the jwt - let jwt_secret = blocking(context.pool(), move |conn| Secrets::read_jwt_secret(conn)).await??; + let jwt_secret = Secret::get().jwt_secret; Ok(LoginResponse { - jwt: Claims::jwt(updated_local_user.id.0, jwt_secret.as_ref())?, + jwt: Claims::jwt(updated_local_user.id.0, &jwt_secret)?, }) } } @@ -775,9 +775,9 @@ impl Perform for PasswordChange { .map_err(|_| ApiError::err("couldnt_update_user"))?; // Return the jwt - let jwt_secret = blocking(context.pool(), move |conn| Secrets::read_jwt_secret(conn)).await??; + let jwt_secret = Secret::get().jwt_secret; Ok(LoginResponse { - jwt: Claims::jwt(updated_local_user.id.0, jwt_secret.as_ref())?, + jwt: Claims::jwt(updated_local_user.id.0, &jwt_secret)?, }) } } diff --git a/crates/api_common/src/lib.rs b/crates/api_common/src/lib.rs index 309cd0fba..068bd253f 100644 --- a/crates/api_common/src/lib.rs +++ b/crates/api_common/src/lib.rs @@ -11,7 +11,7 @@ use lemmy_db_queries::{ source::{ community::{CommunityModerator_, Community_}, person_block::PersonBlock_, - secrets::Secrets_, + secret::SecretSingleton, site::Site_, }, Crud, @@ -26,7 +26,7 @@ use lemmy_db_schema::{ person_block::PersonBlock, person_mention::{PersonMention, PersonMentionForm}, post::{Post, PostRead, PostReadForm}, - secrets::Secrets, + secret::Secret, site::Site, }, CommunityId, @@ -247,8 +247,8 @@ pub async fn get_local_user_view_from_jwt( jwt: &str, pool: &DbPool, ) -> Result { - let jwt_secret = blocking(pool, move |conn| Secrets::read_jwt_secret(conn)).await??; - let claims = Claims::decode(jwt, jwt_secret.as_ref()) + let jwt_secret = Secret::get().jwt_secret; + let claims = Claims::decode(jwt, &jwt_secret) .map_err(|_| ApiError::err("not_logged_in"))? .claims; let local_user_id = LocalUserId(claims.sub); @@ -296,8 +296,8 @@ pub async fn get_local_user_settings_view_from_jwt( jwt: &str, pool: &DbPool, ) -> Result { - let jwt_secret = blocking(pool, move |conn| Secrets::read_jwt_secret(conn)).await??; - let claims = Claims::decode(jwt, jwt_secret.as_ref()) + let jwt_secret = Secret::get().jwt_secret; + let claims = Claims::decode(jwt, &jwt_secret) .map_err(|_| ApiError::err("not_logged_in"))? .claims; let local_user_id = LocalUserId(claims.sub); diff --git a/crates/api_crud/src/user/create.rs b/crates/api_crud/src/user/create.rs index b5c2866e7..24b734623 100644 --- a/crates/api_crud/src/user/create.rs +++ b/crates/api_crud/src/user/create.rs @@ -9,7 +9,7 @@ use lemmy_apub::{ EndpointType, }; use lemmy_db_queries::{ - source::{local_user::LocalUser_, secrets::Secrets_, site::Site_}, + source::{local_user::LocalUser_, secret::SecretSingleton, site::Site_}, Crud, Followable, Joinable, @@ -21,7 +21,7 @@ use lemmy_db_schema::{ community::*, local_user::{LocalUser, LocalUserForm}, person::*, - secrets::Secrets, + secret::Secret, site::*, }, CommunityId, @@ -219,9 +219,9 @@ impl PerformCrud for Register { } // Return the jwt - let jwt_secret = blocking(context.pool(), move |conn| Secrets::read_jwt_secret(conn)).await??; + let jwt_secret = Secret::get().jwt_secret; Ok(LoginResponse { - jwt: Claims::jwt(inserted_local_user.id.0, jwt_secret.as_ref())?, + jwt: Claims::jwt(inserted_local_user.id.0, &jwt_secret)?, }) } } diff --git a/crates/db_queries/src/source/mod.rs b/crates/db_queries/src/source/mod.rs index d754f3f47..a1e45efa5 100644 --- a/crates/db_queries/src/source/mod.rs +++ b/crates/db_queries/src/source/mod.rs @@ -12,5 +12,5 @@ pub mod person_mention; pub mod post; pub mod post_report; pub mod private_message; -pub mod secrets; +pub mod secret; pub mod site; diff --git a/crates/db_queries/src/source/secret.rs b/crates/db_queries/src/source/secret.rs new file mode 100644 index 000000000..eac0d7eb4 --- /dev/null +++ b/crates/db_queries/src/source/secret.rs @@ -0,0 +1,37 @@ +use diesel::{result::Error, *}; +use lemmy_db_schema::source::secret::Secret; +use lemmy_utils::settings::structs::Settings; +use std::sync::RwLock; + +use crate::get_database_url_from_env; + +lazy_static! { + static ref SECRET: RwLock = RwLock::new(init().expect("Failed to load secrets from DB.")); +} + +pub trait SecretSingleton { + fn get() -> Secret; +} + +impl SecretSingleton for Secret { + /// Returns the Secret as a struct + fn get() -> Self { + SECRET.read().expect("read secrets").to_owned() + } +} + +/// Reads the secrets from the DB +fn init() -> Result { + let db_url = match get_database_url_from_env() { + Ok(url) => url, + Err(_) => Settings::get().get_database_url(), + }; + + let conn = PgConnection::establish(&db_url).expect("Couldn't get DB connection for Secrets."); + read_secrets(&conn) +} + +fn read_secrets(conn: &PgConnection) -> Result { + use lemmy_db_schema::schema::secret::dsl::*; + secret.first::(conn) +} diff --git a/crates/db_queries/src/source/secrets.rs b/crates/db_queries/src/source/secrets.rs deleted file mode 100644 index 471ccd196..000000000 --- a/crates/db_queries/src/source/secrets.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::{diesel::RunQueryDsl, lazy_static::__Deref}; -use diesel::PgConnection; -use lemmy_db_schema::source::secrets::Secrets; -use lemmy_utils::LemmyError; -use std::sync::RwLock; - -pub trait Secrets_ { - fn read_jwt_secret(conn: &PgConnection) -> Result; -} - -// TODO: thread_local! might be better in terms of performance, but i couldnt get it to work -lazy_static! { - static ref JWT_SECRET: RwLock> = RwLock::new(None); -} - -impl Secrets_ for Secrets { - fn read_jwt_secret(conn: &PgConnection) -> Result { - use lemmy_db_schema::schema::secrets::dsl::*; - let jwt_option: Option = JWT_SECRET.read().unwrap().deref().clone(); - match jwt_option { - Some(j) => Ok(j), - None => { - let jwt = secrets.first::(conn).map(|s| s.jwt_secret)?; - let jwt_static = JWT_SECRET.write(); - let mut jwt_static = jwt_static.unwrap(); - *jwt_static = Some(jwt.clone()); - Ok(jwt) - } - } - } -} diff --git a/crates/db_schema/src/schema.rs b/crates/db_schema/src/schema.rs index d6aa27272..b08028900 100644 --- a/crates/db_schema/src/schema.rs +++ b/crates/db_schema/src/schema.rs @@ -552,7 +552,7 @@ table! { } table! { - secrets(id) { + secret(id) { id -> Int4, jwt_secret -> Varchar, } diff --git a/crates/db_schema/src/source/mod.rs b/crates/db_schema/src/source/mod.rs index d754f3f47..a1e45efa5 100644 --- a/crates/db_schema/src/source/mod.rs +++ b/crates/db_schema/src/source/mod.rs @@ -12,5 +12,5 @@ pub mod person_mention; pub mod post; pub mod post_report; pub mod private_message; -pub mod secrets; +pub mod secret; pub mod site; diff --git a/crates/db_schema/src/source/secret.rs b/crates/db_schema/src/source/secret.rs new file mode 100644 index 000000000..1a8b30185 --- /dev/null +++ b/crates/db_schema/src/source/secret.rs @@ -0,0 +1,8 @@ +use crate::schema::secret; + +#[derive(Queryable, Identifiable, Clone)] +#[table_name = "secret"] +pub struct Secret { + pub id: i32, + pub jwt_secret: String, +} diff --git a/crates/db_schema/src/source/secrets.rs b/crates/db_schema/src/source/secrets.rs deleted file mode 100644 index 5f3a860f1..000000000 --- a/crates/db_schema/src/source/secrets.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::schema::secrets; - -#[derive(Queryable, Identifiable)] -#[table_name = "secrets"] -pub struct Secrets { - pub id: i32, - pub jwt_secret: String, -} diff --git a/crates/routes/src/feeds.rs b/crates/routes/src/feeds.rs index 5b90373a0..5cf2f1759 100644 --- a/crates/routes/src/feeds.rs +++ b/crates/routes/src/feeds.rs @@ -4,13 +4,13 @@ use chrono::{DateTime, NaiveDateTime, Utc}; use diesel::PgConnection; use lemmy_api_common::blocking; use lemmy_db_queries::{ - source::{community::Community_, person::Person_, secrets::Secrets_}, + source::{community::Community_, person::Person_, secret::SecretSingleton}, Crud, ListingType, SortType, }; use lemmy_db_schema::{ - source::{community::Community, local_user::LocalUser, person::Person, secrets::Secrets}, + source::{community::Community, local_user::LocalUser, person::Person, secret::Secret}, LocalUserId, }; use lemmy_db_views::{ @@ -229,8 +229,8 @@ fn get_feed_front( jwt: String, ) -> Result { let site_view = SiteView::read(conn)?; - let jwt_secret = Secrets::read_jwt_secret(conn)?; - let local_user_id = LocalUserId(Claims::decode(&jwt, jwt_secret.as_ref())?.claims.sub); + let jwt_secret = Secret::get().jwt_secret; + let local_user_id = LocalUserId(Claims::decode(&jwt, &jwt_secret)?.claims.sub); let local_user = LocalUser::read(conn, local_user_id)?; let posts = PostQueryBuilder::create(conn) @@ -259,8 +259,8 @@ fn get_feed_front( fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result { let site_view = SiteView::read(conn)?; - let jwt_secret = Secrets::read_jwt_secret(conn)?; - let local_user_id = LocalUserId(Claims::decode(&jwt, jwt_secret.as_ref())?.claims.sub); + let jwt_secret = Secret::get().jwt_secret; + let local_user_id = LocalUserId(Claims::decode(&jwt, &jwt_secret)?.claims.sub); let local_user = LocalUser::read(conn, local_user_id)?; let person_id = local_user.person_id; let show_bot_accounts = local_user.show_bot_accounts; diff --git a/crates/routes/src/images.rs b/crates/routes/src/images.rs index a10d79566..230841d44 100644 --- a/crates/routes/src/images.rs +++ b/crates/routes/src/images.rs @@ -2,11 +2,9 @@ use actix_http::http::header::ACCEPT_ENCODING; use actix_web::{body::BodyStream, http::StatusCode, web::Data, *}; use anyhow::anyhow; use awc::Client; -use lemmy_api_common::blocking; -use lemmy_db_queries::source::secrets::Secrets_; -use lemmy_db_schema::source::secrets::Secrets; +use lemmy_db_queries::source::secret::SecretSingleton; +use lemmy_db_schema::source::secret::Secret; use lemmy_utils::{claims::Claims, rate_limit::RateLimit, settings::structs::Settings, LemmyError}; -use lemmy_websocket::LemmyContext; use serde::{Deserialize, Serialize}; use std::time::Duration; @@ -49,7 +47,6 @@ struct PictrsParams { async fn upload( req: HttpRequest, body: web::Payload, - context: web::Data, client: web::Data, ) -> Result { // TODO: check rate limit here @@ -57,8 +54,8 @@ async fn upload( .cookie("jwt") .expect("No auth header for picture upload"); - let jwt_secret = blocking(context.pool(), move |conn| Secrets::read_jwt_secret(conn)).await??; - if Claims::decode(jwt.value(), jwt_secret.as_ref()).is_err() { + let jwt_secret = Secret::get().jwt_secret; + if Claims::decode(jwt.value(), &jwt_secret).is_err() { return Ok(HttpResponse::Unauthorized().finish()); }; diff --git a/crates/utils/src/claims.rs b/crates/utils/src/claims.rs index 7922eb367..2b6ce9854 100644 --- a/crates/utils/src/claims.rs +++ b/crates/utils/src/claims.rs @@ -15,23 +15,23 @@ pub struct Claims { } impl Claims { - pub fn decode(jwt: &str, jwt_secret: &[u8]) -> Result, LemmyError> { + pub fn decode(jwt: &str, jwt_secret: &str) -> Result, LemmyError> { let v = Validation { validate_exp: false, ..Validation::default() }; - let key = DecodingKey::from_secret(jwt_secret); + let key = DecodingKey::from_secret(jwt_secret.as_ref()); Ok(decode::(jwt, &key, &v)?) } - pub fn jwt(local_user_id: i32, jwt_secret: &[u8]) -> Result { + pub fn jwt(local_user_id: i32, jwt_secret: &str) -> Result { let my_claims = Claims { sub: local_user_id, iss: Settings::get().hostname, iat: Utc::now().timestamp(), }; - let key = EncodingKey::from_secret(jwt_secret); + let key = EncodingKey::from_secret(jwt_secret.as_ref()); Ok(encode(&Header::default(), &my_claims, &key)?) } } diff --git a/migrations/2021-09-20-112945_jwt-secret/down.sql b/migrations/2021-09-20-112945_jwt-secret/down.sql index a61285e8a..61b21fbab 100644 --- a/migrations/2021-09-20-112945_jwt-secret/down.sql +++ b/migrations/2021-09-20-112945_jwt-secret/down.sql @@ -1 +1 @@ -drop table secrets; +drop table secret; diff --git a/migrations/2021-09-20-112945_jwt-secret/up.sql b/migrations/2021-09-20-112945_jwt-secret/up.sql index 67d704cb0..e9af9ef55 100644 --- a/migrations/2021-09-20-112945_jwt-secret/up.sql +++ b/migrations/2021-09-20-112945_jwt-secret/up.sql @@ -1,10 +1,9 @@ --- generate a jwt secret with 62 possible characters and length 43. --- this gives an entropy of 256 bits --- log2(62^43) = 256 +-- generate a jwt secret +create extension if not exists pgcrypto; -create table secrets( +create table secret( id serial primary key, - jwt_secret varchar(43) not null + jwt_secret varchar not null default gen_random_uuid() ); --- TODO: generate a random string from A-Za-z0-9, length 43, and insert -insert into secrets(jwt_secret) values('123'); + +insert into secret default values;