]> Untitled Git - lemmy.git/commitdiff
Move jwt secret from config to database (fixes #1728)
authorFelix Ableitner <me@nutomic.com>
Mon, 20 Sep 2021 15:46:34 +0000 (17:46 +0200)
committerFelix Ableitner <me@nutomic.com>
Mon, 27 Sep 2021 09:25:09 +0000 (11:25 +0200)
25 files changed:
ansible/templates/config.hjson
config/config.hjson
crates/api/src/lib.rs
crates/api/src/local_user.rs
crates/api_common/src/lib.rs
crates/api_crud/src/user/create.rs
crates/db_queries/src/source/mod.rs
crates/db_queries/src/source/secret.rs [new file with mode: 0644]
crates/db_schema/src/schema.rs
crates/db_schema/src/source/mod.rs
crates/db_schema/src/source/secret.rs [new file with mode: 0644]
crates/routes/src/feeds.rs
crates/routes/src/images.rs
crates/utils/Cargo.toml
crates/utils/src/claims.rs
crates/utils/src/lib.rs
crates/utils/src/settings/structs.rs
docker/federation/lemmy_alpha.hjson
docker/federation/lemmy_beta.hjson
docker/federation/lemmy_delta.hjson
docker/federation/lemmy_epsilon.hjson
docker/federation/lemmy_gamma.hjson
docker/lemmy.hjson
migrations/2021-09-20-112945_jwt-secret/down.sql [new file with mode: 0644]
migrations/2021-09-20-112945_jwt-secret/up.sql [new file with mode: 0644]

index de98e4a8395d7ac6c66b598aa647e3a8d74693cf..fd7c3176e9a7dc9906e17d99f5dbb6f2988fad50 100644 (file)
@@ -16,8 +16,6 @@
   hostname: "{{ domain }}"
   # the port where lemmy should listen for incoming requests
   port: 8536
-  # json web token for authorization between server and client
-  jwt_secret: "{{ jwt_password }}"
   # whether tls is required for activitypub. only disable this for debugging, never for producion.
   tls_enabled: true
   # address where pictrs is available
index cb08dda454e916dc597bc395319991e50243dbd8..11b64b2048362510d5ac711e207bbd587b97809b 100644 (file)
@@ -33,8 +33,6 @@
   port: 8536
   # whether tls is required for activitypub. only disable this for debugging, never for producion.
   tls_enabled: true
-  # json web token for authorization between server and client
-  jwt_secret: "changeme"
   # address where pictrs is available
   pictrs_url: "http://pictrs:8080"
   # maximum length of local community and user names
index 83d98bde5db9de5955410d6d7025aa24db5f8025..1146975f0d9c3f6f11936a4bd00d3724e4ba936d 100644 (file)
@@ -188,10 +188,15 @@ pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> String {
 #[cfg(test)]
 mod tests {
   use lemmy_api_common::check_validator_time;
-  use lemmy_db_queries::{establish_unpooled_connection, source::local_user::LocalUser_, Crud};
+  use lemmy_db_queries::{
+    establish_unpooled_connection,
+    source::{local_user::LocalUser_, secret::SecretSingleton},
+    Crud,
+  };
   use lemmy_db_schema::source::{
     local_user::{LocalUser, LocalUserForm},
     person::{Person, PersonForm},
+    secret::Secret,
   };
   use lemmy_utils::claims::Claims;
 
@@ -214,8 +219,9 @@ mod tests {
 
     let inserted_local_user = LocalUser::create(&conn, &local_user_form).unwrap();
 
-    let jwt = Claims::jwt(inserted_local_user.id.0).unwrap();
-    let claims = Claims::decode(&jwt).unwrap().claims;
+    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());
 
index 510ef5adb1af5df722797fbf79eb3fde5ba4014d..df03e30205d967283e138e60170a78feaee2e77e 100644 (file)
@@ -25,6 +25,7 @@ use lemmy_db_queries::{
     person_mention::PersonMention_,
     post::Post_,
     private_message::PrivateMessage_,
+    secret::SecretSingleton,
   },
   Blockable,
   Crud,
@@ -43,6 +44,7 @@ use lemmy_db_schema::{
     person_mention::*,
     post::Post,
     private_message::PrivateMessage,
+    secret::Secret,
     site::*,
   },
 };
@@ -103,8 +105,9 @@ impl Perform for Login {
     }
 
     // Return the jwt
+    let jwt_secret = Secret::get().jwt_secret;
     Ok(LoginResponse {
-      jwt: Claims::jwt(local_user_view.local_user.id.0)?,
+      jwt: Claims::jwt(local_user_view.local_user.id.0, &jwt_secret)?,
     })
   }
 }
@@ -268,8 +271,9 @@ impl Perform for SaveUserSettings {
     };
 
     // Return the jwt
+    let jwt_secret = Secret::get().jwt_secret;
     Ok(LoginResponse {
-      jwt: Claims::jwt(updated_local_user.id.0)?,
+      jwt: Claims::jwt(updated_local_user.id.0, &jwt_secret)?,
     })
   }
 }
@@ -311,8 +315,9 @@ impl Perform for ChangePassword {
     .await??;
 
     // Return the jwt
+    let jwt_secret = Secret::get().jwt_secret;
     Ok(LoginResponse {
-      jwt: Claims::jwt(updated_local_user.id.0)?,
+      jwt: Claims::jwt(updated_local_user.id.0, &jwt_secret)?,
     })
   }
 }
@@ -770,8 +775,9 @@ impl Perform for PasswordChange {
     .map_err(|_| ApiError::err("couldnt_update_user"))?;
 
     // Return the jwt
+    let jwt_secret = Secret::get().jwt_secret;
     Ok(LoginResponse {
-      jwt: Claims::jwt(updated_local_user.id.0)?,
+      jwt: Claims::jwt(updated_local_user.id.0, &jwt_secret)?,
     })
   }
 }
index 933a096c50394a32b8e929f948c21e4fade4832a..068bd253f5bc69b9de042f5a9e529017068c220f 100644 (file)
@@ -11,6 +11,7 @@ use lemmy_db_queries::{
   source::{
     community::{CommunityModerator_, Community_},
     person_block::PersonBlock_,
+    secret::SecretSingleton,
     site::Site_,
   },
   Crud,
@@ -25,6 +26,7 @@ use lemmy_db_schema::{
     person_block::PersonBlock,
     person_mention::{PersonMention, PersonMentionForm},
     post::{Post, PostRead, PostReadForm},
+    secret::Secret,
     site::Site,
   },
   CommunityId,
@@ -245,7 +247,8 @@ pub async fn get_local_user_view_from_jwt(
   jwt: &str,
   pool: &DbPool,
 ) -> Result<LocalUserView, LemmyError> {
-  let claims = Claims::decode(jwt)
+  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);
@@ -293,7 +296,8 @@ pub async fn get_local_user_settings_view_from_jwt(
   jwt: &str,
   pool: &DbPool,
 ) -> Result<LocalUserSettingsView, LemmyError> {
-  let claims = Claims::decode(jwt)
+  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);
index 6f9216b361a02a0ce8fac3d1b0a33ba2a24fe25d..24b73462376d6f4069b7a68b92d9fcf2cdd99210 100644 (file)
@@ -9,7 +9,7 @@ use lemmy_apub::{
   EndpointType,
 };
 use lemmy_db_queries::{
-  source::{local_user::LocalUser_, site::Site_},
+  source::{local_user::LocalUser_, secret::SecretSingleton, site::Site_},
   Crud,
   Followable,
   Joinable,
@@ -21,6 +21,7 @@ use lemmy_db_schema::{
     community::*,
     local_user::{LocalUser, LocalUserForm},
     person::*,
+    secret::Secret,
     site::*,
   },
   CommunityId,
@@ -218,8 +219,9 @@ impl PerformCrud for Register {
     }
 
     // Return the jwt
+    let jwt_secret = Secret::get().jwt_secret;
     Ok(LoginResponse {
-      jwt: Claims::jwt(inserted_local_user.id.0)?,
+      jwt: Claims::jwt(inserted_local_user.id.0, &jwt_secret)?,
     })
   }
 }
index 8b82d31a7597a50953bc5f211d4e1c7dfc0f724c..a1e45efa5158a25b308bd0375d93dc30e578d54d 100644 (file)
@@ -12,4 +12,5 @@ pub mod person_mention;
 pub mod post;
 pub mod post_report;
 pub mod private_message;
+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 (file)
index 0000000..eac0d7e
--- /dev/null
@@ -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<Secret> = 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<Secret, Error> {
+  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<Secret, Error> {
+  use lemmy_db_schema::schema::secret::dsl::*;
+  secret.first::<Secret>(conn)
+}
index 4c92fffba8b5fbb725ac105d5f23d59c3e426e0b..b08028900b84ac1b2be4adc10257a225771e01a6 100644 (file)
@@ -551,6 +551,13 @@ table! {
     }
 }
 
+table! {
+  secret(id) {
+    id -> Int4,
+    jwt_secret -> Varchar,
+  }
+}
+
 joinable!(comment_alias_1 -> person_alias_1 (creator_id));
 joinable!(comment -> comment_alias_1 (parent_id));
 joinable!(person_mention -> person_alias_1 (recipient_id));
index 8b82d31a7597a50953bc5f211d4e1c7dfc0f724c..a1e45efa5158a25b308bd0375d93dc30e578d54d 100644 (file)
@@ -12,4 +12,5 @@ pub mod person_mention;
 pub mod post;
 pub mod post_report;
 pub mod private_message;
+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 (file)
index 0000000..1a8b301
--- /dev/null
@@ -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,
+}
index 75542e740da352e0dd7389d3d6bfd98531e51d5b..5cf2f1759d609c3f881dd6254f1a12491de2a1fd 100644 (file)
@@ -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_},
+  source::{community::Community_, person::Person_, secret::SecretSingleton},
   Crud,
   ListingType,
   SortType,
 };
 use lemmy_db_schema::{
-  source::{community::Community, local_user::LocalUser, person::Person},
+  source::{community::Community, local_user::LocalUser, person::Person, secret::Secret},
   LocalUserId,
 };
 use lemmy_db_views::{
@@ -229,17 +229,15 @@ fn get_feed_front(
   jwt: String,
 ) -> Result<ChannelBuilder, LemmyError> {
   let site_view = SiteView::read(conn)?;
-  let local_user_id = LocalUserId(Claims::decode(&jwt)?.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;
-  let show_read_posts = local_user.show_read_posts;
 
   let posts = PostQueryBuilder::create(conn)
     .listing_type(ListingType::Subscribed)
-    .my_person_id(person_id)
-    .show_bot_accounts(show_bot_accounts)
-    .show_read_posts(show_read_posts)
+    .my_person_id(local_user.person_id)
+    .show_bot_accounts(local_user.show_bot_accounts)
+    .show_read_posts(local_user.show_read_posts)
     .sort(*sort_type)
     .list()?;
 
@@ -261,7 +259,8 @@ fn get_feed_front(
 
 fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result<ChannelBuilder, LemmyError> {
   let site_view = SiteView::read(conn)?;
-  let local_user_id = LocalUserId(Claims::decode(&jwt)?.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;
index 7439e4a8e6bd64e8c5f5f807dfdbc480358ed6b6..230841d440bdee44e41e51ec4cde168972c6fe4f 100644 (file)
@@ -2,6 +2,8 @@ use actix_http::http::header::ACCEPT_ENCODING;
 use actix_web::{body::BodyStream, http::StatusCode, web::Data, *};
 use anyhow::anyhow;
 use awc::Client;
+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 serde::{Deserialize, Serialize};
 use std::time::Duration;
@@ -52,7 +54,8 @@ async fn upload(
     .cookie("jwt")
     .expect("No auth header for picture upload");
 
-  if Claims::decode(jwt.value()).is_err() {
+  let jwt_secret = Secret::get().jwt_secret;
+  if Claims::decode(jwt.value(), &jwt_secret).is_err() {
     return Ok(HttpResponse::Unauthorized().finish());
   };
 
index fda58cd1630c1ddc65a0cd7ced285e102849bac4..4778335836528985c06e61ffd694f0ebd2b48877 100644 (file)
@@ -35,7 +35,7 @@ strum_macros = "0.21.1"
 futures = "0.3.16"
 diesel = "1.4.7"
 http = "0.2.4"
-jsonwebtoken = "7.2.0"
 deser-hjson = "1.0.2"
 smart-default = "0.6.0"
 webpage = { version = "1.3.0", default-features = false, features = ["serde"] }
+jsonwebtoken = "7.2.0"
index e1c3ba158223010cf56b29a9c897830c7072e6bd..2b6ce9854d65fa5ced7ee49c89008e2f2200db00 100644 (file)
@@ -1,4 +1,4 @@
-use crate::settings::structs::Settings;
+use crate::{settings::structs::Settings, LemmyError};
 use chrono::Utc;
 use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, TokenData, Validation};
 use serde::{Deserialize, Serialize};
@@ -15,28 +15,23 @@ pub struct Claims {
 }
 
 impl Claims {
-  pub fn decode(jwt: &str) -> Result<TokenData<Claims>, jsonwebtoken::errors::Error> {
+  pub fn decode(jwt: &str, jwt_secret: &str) -> Result<TokenData<Claims>, LemmyError> {
     let v = Validation {
       validate_exp: false,
       ..Validation::default()
     };
-    decode::<Claims>(
-      jwt,
-      &DecodingKey::from_secret(Settings::get().jwt_secret.as_ref()),
-      &v,
-    )
+    let key = DecodingKey::from_secret(jwt_secret.as_ref());
+    Ok(decode::<Claims>(jwt, &key, &v)?)
   }
 
-  pub fn jwt(local_user_id: i32) -> Result<Jwt, jsonwebtoken::errors::Error> {
+  pub fn jwt(local_user_id: i32, jwt_secret: &str) -> Result<Jwt, LemmyError> {
     let my_claims = Claims {
       sub: local_user_id,
       iss: Settings::get().hostname,
       iat: Utc::now().timestamp(),
     };
-    encode(
-      &Header::default(),
-      &my_claims,
-      &EncodingKey::from_secret(Settings::get().jwt_secret.as_ref()),
-    )
+
+    let key = EncodingKey::from_secret(jwt_secret.as_ref());
+    Ok(encode(&Header::default(), &my_claims, &key)?)
   }
 }
index 6bf3237e49daf3bd657dba1d9d093de4cac53d20..f1093fd585f2f032607ee74d01e44c600f105a14 100644 (file)
@@ -6,12 +6,12 @@ extern crate strum_macros;
 extern crate smart_default;
 
 pub mod apub;
-pub mod claims;
 pub mod email;
 pub mod rate_limit;
 pub mod request;
 pub mod settings;
 
+pub mod claims;
 #[cfg(test)]
 mod test;
 pub mod utils;
index 074cf87e8114836ef82196c11957639e79761f92..9ba8a5f9491b0741a219d44138a6d942fff6548b 100644 (file)
@@ -24,8 +24,6 @@ pub struct Settings {
   pub port: u16,
   #[default(true)]
   pub tls_enabled: bool,
-  #[default("changeme")]
-  pub jwt_secret: String,
   #[default(None)]
   pub pictrs_url: Option<String>,
   #[default(None)]
index 3795f0d79f5c2e420ce8c2927ffccd7a2f9856d7..8c69a4c6f32db7996d5d4afb84fe1a28a050b08a 100644 (file)
@@ -2,7 +2,6 @@
   hostname: lemmy-alpha:8541
   port: 8541
   tls_enabled: false
-  jwt_secret: changeme
   setup: {
     admin_username: lemmy_alpha
     admin_password: lemmylemmy
index 1c0b3876b9621735771ffdf6f3939bed54653d34..efd9458cc0ec31b737adc64e6419bf8f75b49b22 100644 (file)
@@ -2,7 +2,6 @@
   hostname: lemmy-beta:8551
   port: 8551
   tls_enabled: false
-  jwt_secret: changeme
   setup: {
     admin_username: lemmy_beta
     admin_password: lemmylemmy
index 2f7dfecb4f02aae4898c0a6e08c4e485159cadde..75f3f916314e04b9f0067cd8aa4c9269b77c15c1 100644 (file)
@@ -2,7 +2,6 @@
   hostname: lemmy-delta:8571
   port: 8571
   tls_enabled: false
-  jwt_secret: changeme
   setup: {
     admin_username: lemmy_delta
     admin_password: lemmylemmy
index 87d142a8f58243a0863f6420203f90cdce2b6344..1b2cbd34cf6ab35a6653f59d7e1280dd9504fb3c 100644 (file)
@@ -2,7 +2,6 @@
   hostname: lemmy-epsilon:8581
   port: 8581
   tls_enabled: false
-  jwt_secret: changeme
   setup: {
     admin_username: lemmy_epsilon
     admin_password: lemmylemmy
index 2b94a968d26afd589908fcc6a718c231e624865c..48b6c1c53437cd59a4f41143711bcc517e19fbba 100644 (file)
@@ -2,7 +2,6 @@
   hostname: lemmy-gamma:8561
   port: 8561
   tls_enabled: false
-  jwt_secret: changeme
   setup: {
     admin_username: lemmy_gamma
     admin_password: lemmylemmy
index de7cf68a0059ac4b7db6c61bd097edae4463b04b..4c42d8aea3008a8ead39980e3a729d852dcc09d2 100644 (file)
@@ -17,8 +17,6 @@
   bind: "0.0.0.0"
   # port where lemmy should listen for incoming requests
   port: 8536
-  # json web token for authorization between server and client
-  jwt_secret: "changeme"
   # settings related to the postgresql database
   # address where pictrs is available
   pictrs_url: "http://pictrs:8080"
diff --git a/migrations/2021-09-20-112945_jwt-secret/down.sql b/migrations/2021-09-20-112945_jwt-secret/down.sql
new file mode 100644 (file)
index 0000000..61b21fb
--- /dev/null
@@ -0,0 +1 @@
+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
new file mode 100644 (file)
index 0000000..e9af9ef
--- /dev/null
@@ -0,0 +1,9 @@
+-- generate a jwt secret
+create extension if not exists pgcrypto;
+
+create table secret(
+  id serial primary key,
+  jwt_secret varchar not null default gen_random_uuid()
+);
+
+insert into secret default values;