move claims.rs back to utils

move-jwt-secret-to-db
Felix Ableitner 2021-09-21 15:18:14 +02:00
parent 48918f362d
commit 43b0371775
13 changed files with 170 additions and 184 deletions

3
Cargo.lock generated
View File

@ -1628,8 +1628,6 @@ dependencies = [
"actix-web", "actix-web",
"chrono", "chrono",
"diesel", "diesel",
"jsonwebtoken",
"lazy_static",
"lemmy_db_queries", "lemmy_db_queries",
"lemmy_db_schema", "lemmy_db_schema",
"lemmy_db_views", "lemmy_db_views",
@ -1909,6 +1907,7 @@ dependencies = [
"futures", "futures",
"http", "http",
"itertools", "itertools",
"jsonwebtoken",
"lazy_static", "lazy_static",
"lettre", "lettre",
"log", "log",

View File

@ -187,30 +187,22 @@ pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> String {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use diesel::{ use lemmy_api_common::check_validator_time;
r2d2::{ConnectionManager, Pool},
PgConnection,
};
use lemmy_api_common::{check_validator_time, claims::Claims};
use lemmy_db_queries::{ use lemmy_db_queries::{
establish_unpooled_connection, establish_unpooled_connection,
get_database_url_from_env, source::{local_user::LocalUser_, secrets::Secrets_},
source::local_user::LocalUser_,
Crud, Crud,
}; };
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
local_user::{LocalUser, LocalUserForm}, local_user::{LocalUser, LocalUserForm},
person::{Person, PersonForm}, person::{Person, PersonForm},
secrets::Secrets,
}; };
use lemmy_utils::settings::structs::Settings; use lemmy_utils::claims::Claims;
#[actix_rt::test] #[test]
async fn test_should_not_validate_user_token_after_password_change() { fn test_should_not_validate_user_token_after_password_change() {
let conn = establish_unpooled_connection(); let conn = establish_unpooled_connection();
let db_url = get_database_url_from_env().unwrap_or_else(|_| Settings::get().get_database_url());
let pool = Pool::builder()
.build(ConnectionManager::<PgConnection>::new(&db_url))
.unwrap();
let new_person = PersonForm { let new_person = PersonForm {
name: "Gerry9812".into(), name: "Gerry9812".into(),
@ -227,8 +219,9 @@ mod tests {
let inserted_local_user = LocalUser::create(&conn, &local_user_form).unwrap(); let inserted_local_user = LocalUser::create(&conn, &local_user_form).unwrap();
let jwt = Claims::jwt(inserted_local_user.id.0, &pool).await.unwrap(); let jwt_secret = Secrets::read_jwt_secret(&conn).unwrap();
let claims = Claims::decode(&jwt, &pool).await.unwrap().claims; let jwt = Claims::jwt(inserted_local_user.id.0, jwt_secret.as_ref()).unwrap();
let claims = Claims::decode(&jwt, jwt_secret.as_ref()).unwrap().claims;
let check = check_validator_time(&inserted_local_user.validator_time, &claims); let check = check_validator_time(&inserted_local_user.validator_time, &claims);
assert!(check.is_ok()); assert!(check.is_ok());

View File

@ -6,7 +6,6 @@ use captcha::{gen, Difficulty};
use chrono::Duration; use chrono::Duration;
use lemmy_api_common::{ use lemmy_api_common::{
blocking, blocking,
claims::Claims,
collect_moderated_communities, collect_moderated_communities,
get_local_user_view_from_jwt, get_local_user_view_from_jwt,
is_admin, is_admin,
@ -26,6 +25,7 @@ use lemmy_db_queries::{
person_mention::PersonMention_, person_mention::PersonMention_,
post::Post_, post::Post_,
private_message::PrivateMessage_, private_message::PrivateMessage_,
secrets::Secrets_,
}, },
Blockable, Blockable,
Crud, Crud,
@ -44,6 +44,7 @@ use lemmy_db_schema::{
person_mention::*, person_mention::*,
post::Post, post::Post,
private_message::PrivateMessage, private_message::PrivateMessage,
secrets::Secrets,
site::*, site::*,
}, },
}; };
@ -59,6 +60,7 @@ use lemmy_db_views_actor::{
person_view::PersonViewSafe, person_view::PersonViewSafe,
}; };
use lemmy_utils::{ use lemmy_utils::{
claims::Claims,
email::send_email, email::send_email,
location_info, location_info,
settings::structs::Settings, settings::structs::Settings,
@ -103,8 +105,9 @@ impl Perform for Login {
} }
// Return the jwt // Return the jwt
let jwt_secret = blocking(context.pool(), move |conn| Secrets::read_jwt_secret(conn)).await??;
Ok(LoginResponse { Ok(LoginResponse {
jwt: Claims::jwt(local_user_view.local_user.id.0, context.pool()).await?, jwt: Claims::jwt(local_user_view.local_user.id.0, jwt_secret.as_ref())?,
}) })
} }
} }
@ -268,8 +271,9 @@ impl Perform for SaveUserSettings {
}; };
// Return the jwt // Return the jwt
let jwt_secret = blocking(context.pool(), move |conn| Secrets::read_jwt_secret(conn)).await??;
Ok(LoginResponse { Ok(LoginResponse {
jwt: Claims::jwt(updated_local_user.id.0, context.pool()).await?, jwt: Claims::jwt(updated_local_user.id.0, jwt_secret.as_ref())?,
}) })
} }
} }
@ -311,8 +315,9 @@ impl Perform for ChangePassword {
.await??; .await??;
// Return the jwt // Return the jwt
let jwt_secret = blocking(context.pool(), move |conn| Secrets::read_jwt_secret(conn)).await??;
Ok(LoginResponse { Ok(LoginResponse {
jwt: Claims::jwt(updated_local_user.id.0, context.pool()).await?, jwt: Claims::jwt(updated_local_user.id.0, jwt_secret.as_ref())?,
}) })
} }
} }
@ -770,8 +775,9 @@ impl Perform for PasswordChange {
.map_err(|_| ApiError::err("couldnt_update_user"))?; .map_err(|_| ApiError::err("couldnt_update_user"))?;
// Return the jwt // Return the jwt
let jwt_secret = blocking(context.pool(), move |conn| Secrets::read_jwt_secret(conn)).await??;
Ok(LoginResponse { Ok(LoginResponse {
jwt: Claims::jwt(updated_local_user.id.0, context.pool()).await?, jwt: Claims::jwt(updated_local_user.id.0, jwt_secret.as_ref())?,
}) })
} }
} }

View File

@ -24,5 +24,3 @@ actix-web = { version = "4.0.0-beta.8", default-features = false, features = ["c
chrono = { version = "0.4.19", features = ["serde"] } chrono = { version = "0.4.19", features = ["serde"] }
serde_json = { version = "1.0.66", features = ["preserve_order"] } serde_json = { version = "1.0.66", features = ["preserve_order"] }
url = "2.2.2" url = "2.2.2"
jsonwebtoken = "7.2.0"
lazy_static = "1.4.0"

View File

@ -1,63 +0,0 @@
use crate::blocking;
use chrono::Utc;
use diesel::PgConnection;
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, TokenData, Validation};
use lazy_static::lazy_static;
use lemmy_db_queries::{source::secrets::Secrets_, DbPool};
use lemmy_db_schema::source::secrets::Secrets;
use lemmy_utils::{settings::structs::Settings, LemmyError};
use serde::{Deserialize, Serialize};
use std::{ops::Deref, sync::RwLock};
type Jwt = String;
#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
/// local_user_id, standard claim by RFC 7519.
pub sub: i32,
pub iss: String,
/// Time when this token was issued as UNIX-timestamp in seconds
pub iat: i64,
}
impl Claims {
pub async fn decode(jwt: &str, pool: &DbPool) -> Result<TokenData<Claims>, LemmyError> {
let v = Validation {
validate_exp: false,
..Validation::default()
};
let secret = blocking(pool, move |conn| get_jwt_secret(conn)).await??;
let key = DecodingKey::from_secret(secret.as_ref());
Ok(decode::<Claims>(jwt, &key, &v)?)
}
pub async fn jwt(local_user_id: i32, pool: &DbPool) -> Result<Jwt, LemmyError> {
let my_claims = Claims {
sub: local_user_id,
iss: Settings::get().hostname,
iat: Utc::now().timestamp(),
};
let secret = blocking(pool, move |conn| get_jwt_secret(conn)).await??;
let key = EncodingKey::from_secret(secret.as_ref());
Ok(encode(&Header::default(), &my_claims, &key)?)
}
}
lazy_static! {
static ref JWT_SECRET: RwLock<Option<String>> = RwLock::new(None);
}
fn get_jwt_secret(conn: &PgConnection) -> Result<String, LemmyError> {
let jwt_option: Option<String> = JWT_SECRET.read().unwrap().deref().clone();
match jwt_option {
Some(j) => Ok(j),
None => {
let jwt = Secrets::read(conn)?;
let jwt_static = JWT_SECRET.write();
let mut jwt_static = jwt_static.unwrap();
*jwt_static = Some(jwt.clone());
Ok(jwt)
}
}
}

View File

@ -1,4 +1,3 @@
pub mod claims;
pub mod comment; pub mod comment;
pub mod community; pub mod community;
pub mod person; pub mod person;
@ -6,12 +5,13 @@ pub mod post;
pub mod site; pub mod site;
pub mod websocket; pub mod websocket;
use crate::{claims::Claims, site::FederatedInstances}; use crate::site::FederatedInstances;
use diesel::PgConnection; use diesel::PgConnection;
use lemmy_db_queries::{ use lemmy_db_queries::{
source::{ source::{
community::{CommunityModerator_, Community_}, community::{CommunityModerator_, Community_},
person_block::PersonBlock_, person_block::PersonBlock_,
secrets::Secrets_,
site::Site_, site::Site_,
}, },
Crud, Crud,
@ -26,6 +26,7 @@ use lemmy_db_schema::{
person_block::PersonBlock, person_block::PersonBlock,
person_mention::{PersonMention, PersonMentionForm}, person_mention::{PersonMention, PersonMentionForm},
post::{Post, PostRead, PostReadForm}, post::{Post, PostRead, PostReadForm},
secrets::Secrets,
site::Site, site::Site,
}, },
CommunityId, CommunityId,
@ -39,6 +40,7 @@ use lemmy_db_views_actor::{
community_view::CommunityView, community_view::CommunityView,
}; };
use lemmy_utils::{ use lemmy_utils::{
claims::Claims,
email::send_email, email::send_email,
settings::structs::Settings, settings::structs::Settings,
utils::MentionData, utils::MentionData,
@ -245,8 +247,8 @@ pub async fn get_local_user_view_from_jwt(
jwt: &str, jwt: &str,
pool: &DbPool, pool: &DbPool,
) -> Result<LocalUserView, LemmyError> { ) -> Result<LocalUserView, LemmyError> {
let claims = Claims::decode(jwt, pool) let jwt_secret = blocking(pool, move |conn| Secrets::read_jwt_secret(conn)).await??;
.await let claims = Claims::decode(jwt, jwt_secret.as_ref())
.map_err(|_| ApiError::err("not_logged_in"))? .map_err(|_| ApiError::err("not_logged_in"))?
.claims; .claims;
let local_user_id = LocalUserId(claims.sub); let local_user_id = LocalUserId(claims.sub);
@ -294,8 +296,8 @@ pub async fn get_local_user_settings_view_from_jwt(
jwt: &str, jwt: &str,
pool: &DbPool, pool: &DbPool,
) -> Result<LocalUserSettingsView, LemmyError> { ) -> Result<LocalUserSettingsView, LemmyError> {
let claims = Claims::decode(jwt, pool) let jwt_secret = blocking(pool, move |conn| Secrets::read_jwt_secret(conn)).await??;
.await let claims = Claims::decode(jwt, jwt_secret.as_ref())
.map_err(|_| ApiError::err("not_logged_in"))? .map_err(|_| ApiError::err("not_logged_in"))?
.claims; .claims;
let local_user_id = LocalUserId(claims.sub); let local_user_id = LocalUserId(claims.sub);

View File

@ -1,6 +1,6 @@
use crate::PerformCrud; use crate::PerformCrud;
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_api_common::{blocking, claims::Claims, password_length_check, person::*}; use lemmy_api_common::{blocking, password_length_check, person::*};
use lemmy_apub::{ use lemmy_apub::{
generate_apub_endpoint, generate_apub_endpoint,
generate_followers_url, generate_followers_url,
@ -9,7 +9,7 @@ use lemmy_apub::{
EndpointType, EndpointType,
}; };
use lemmy_db_queries::{ use lemmy_db_queries::{
source::{local_user::LocalUser_, site::Site_}, source::{local_user::LocalUser_, secrets::Secrets_, site::Site_},
Crud, Crud,
Followable, Followable,
Joinable, Joinable,
@ -21,6 +21,7 @@ use lemmy_db_schema::{
community::*, community::*,
local_user::{LocalUser, LocalUserForm}, local_user::{LocalUser, LocalUserForm},
person::*, person::*,
secrets::Secrets,
site::*, site::*,
}, },
CommunityId, CommunityId,
@ -28,6 +29,7 @@ use lemmy_db_schema::{
use lemmy_db_views_actor::person_view::PersonViewSafe; use lemmy_db_views_actor::person_view::PersonViewSafe;
use lemmy_utils::{ use lemmy_utils::{
apub::generate_actor_keypair, apub::generate_actor_keypair,
claims::Claims,
settings::structs::Settings, settings::structs::Settings,
utils::{check_slurs, is_valid_actor_name}, utils::{check_slurs, is_valid_actor_name},
ApiError, ApiError,
@ -217,8 +219,9 @@ impl PerformCrud for Register {
} }
// Return the jwt // Return the jwt
let jwt_secret = blocking(context.pool(), move |conn| Secrets::read_jwt_secret(conn)).await??;
Ok(LoginResponse { Ok(LoginResponse {
jwt: Claims::jwt(inserted_local_user.id.0, context.pool()).await?, jwt: Claims::jwt(inserted_local_user.id.0, jwt_secret.as_ref())?,
}) })
} }
} }

View File

@ -1,13 +1,31 @@
use diesel::{result::Error, *}; use crate::{diesel::RunQueryDsl, lazy_static::__Deref};
use diesel::PgConnection;
use lemmy_db_schema::source::secrets::Secrets; use lemmy_db_schema::source::secrets::Secrets;
use lemmy_utils::LemmyError;
use std::sync::RwLock;
pub trait Secrets_ { pub trait Secrets_ {
fn read(conn: &PgConnection) -> Result<String, Error>; fn read_jwt_secret(conn: &PgConnection) -> Result<String, LemmyError>;
}
// TODO: thread_local! might be better in terms of performance, but i couldnt get it to work
lazy_static! {
static ref JWT_SECRET: RwLock<Option<String>> = RwLock::new(None);
} }
impl Secrets_ for Secrets { impl Secrets_ for Secrets {
fn read(conn: &PgConnection) -> Result<String, Error> { fn read_jwt_secret(conn: &PgConnection) -> Result<String, LemmyError> {
use lemmy_db_schema::schema::secrets::dsl::*; use lemmy_db_schema::schema::secrets::dsl::*;
secrets.first::<Self>(conn).map(|s| s.jwt_secret) let jwt_option: Option<String> = JWT_SECRET.read().unwrap().deref().clone();
match jwt_option {
Some(j) => Ok(j),
None => {
let jwt = secrets.first::<Self>(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)
}
}
} }
} }

View File

@ -1,16 +1,16 @@
use actix_web::{error::ErrorBadRequest, *}; use actix_web::{error::ErrorBadRequest, *};
use anyhow::anyhow; use anyhow::anyhow;
use chrono::{DateTime, NaiveDateTime, Utc}; use chrono::{DateTime, NaiveDateTime, Utc};
use lemmy_api_common::{blocking, claims::Claims}; use diesel::PgConnection;
use lemmy_api_common::blocking;
use lemmy_db_queries::{ use lemmy_db_queries::{
source::{community::Community_, person::Person_}, source::{community::Community_, person::Person_, secrets::Secrets_},
Crud, Crud,
DbPool,
ListingType, ListingType,
SortType, SortType,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{community::Community, local_user::LocalUser, person::Person}, source::{community::Community, local_user::LocalUser, person::Person, secrets::Secrets},
LocalUserId, LocalUserId,
}; };
use lemmy_db_views::{ use lemmy_db_views::{
@ -19,7 +19,12 @@ use lemmy_db_views::{
site_view::SiteView, site_view::SiteView,
}; };
use lemmy_db_views_actor::person_mention_view::{PersonMentionQueryBuilder, PersonMentionView}; use lemmy_db_views_actor::person_mention_view::{PersonMentionQueryBuilder, PersonMentionView};
use lemmy_utils::{settings::structs::Settings, utils::markdown_to_html, LemmyError}; use lemmy_utils::{
claims::Claims,
settings::structs::Settings,
utils::markdown_to_html,
LemmyError,
};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use rss::{ use rss::{
extension::dublincore::DublinCoreExtensionBuilder, extension::dublincore::DublinCoreExtensionBuilder,
@ -136,12 +141,13 @@ async fn get_feed(
_ => return Err(ErrorBadRequest(LemmyError::from(anyhow!("wrong_type")))), _ => return Err(ErrorBadRequest(LemmyError::from(anyhow!("wrong_type")))),
}; };
let builder = match request_type { let builder = blocking(context.pool(), move |conn| match request_type {
RequestType::User => get_feed_user(context.pool(), sort_type, param).await, RequestType::User => get_feed_user(conn, &sort_type, param),
RequestType::Community => get_feed_community(context.pool(), sort_type, param).await, RequestType::Community => get_feed_community(conn, &sort_type, param),
RequestType::Front => get_feed_front(context.pool(), sort_type, param).await, RequestType::Front => get_feed_front(conn, &sort_type, param),
RequestType::Inbox => get_feed_inbox(context.pool(), param).await, RequestType::Inbox => get_feed_inbox(conn, param),
} })
.await?
.map_err(ErrorBadRequest)?; .map_err(ErrorBadRequest)?;
let rss = builder.build().map_err(ErrorBadRequest)?.to_string(); let rss = builder.build().map_err(ErrorBadRequest)?.to_string();
@ -161,23 +167,19 @@ fn get_sort_type(info: web::Query<Params>) -> Result<SortType, ParseError> {
SortType::from_str(&sort_query) SortType::from_str(&sort_query)
} }
async fn get_feed_user( fn get_feed_user(
pool: &DbPool, conn: &PgConnection,
sort_type: SortType, sort_type: &SortType,
user_name: String, user_name: String,
) -> Result<ChannelBuilder, LemmyError> { ) -> Result<ChannelBuilder, LemmyError> {
let site_view = blocking(pool, move |conn| SiteView::read(conn)).await??; let site_view = SiteView::read(conn)?;
let person = blocking(pool, move |conn| Person::find_by_name(conn, &user_name)).await??; let person = Person::find_by_name(conn, &user_name)?;
let person_id = person.id; let posts = PostQueryBuilder::create(conn)
let posts = blocking(pool, move |conn| { .listing_type(ListingType::All)
PostQueryBuilder::create(conn) .sort(*sort_type)
.listing_type(ListingType::All) .creator_id(person.id)
.sort(sort_type) .list()?;
.creator_id(person_id)
.list()
})
.await??;
let items = create_post_items(posts)?; let items = create_post_items(posts)?;
@ -191,26 +193,19 @@ async fn get_feed_user(
Ok(channel_builder) Ok(channel_builder)
} }
async fn get_feed_community( fn get_feed_community(
pool: &DbPool, conn: &PgConnection,
sort_type: SortType, sort_type: &SortType,
community_name: String, community_name: String,
) -> Result<ChannelBuilder, LemmyError> { ) -> Result<ChannelBuilder, LemmyError> {
let site_view = blocking(pool, move |conn| SiteView::read(conn)).await??; let site_view = SiteView::read(conn)?;
let community = blocking(pool, move |conn| { let community = Community::read_from_name(conn, &community_name)?;
Community::read_from_name(conn, &community_name)
})
.await??;
let community_id = community.id; let posts = PostQueryBuilder::create(conn)
let posts = blocking(pool, move |conn| { .listing_type(ListingType::All)
PostQueryBuilder::create(conn) .sort(*sort_type)
.listing_type(ListingType::All) .community_id(community.id)
.sort(sort_type) .list()?;
.community_id(community_id)
.list()
})
.await??;
let items = create_post_items(posts)?; let items = create_post_items(posts)?;
@ -228,25 +223,23 @@ async fn get_feed_community(
Ok(channel_builder) Ok(channel_builder)
} }
async fn get_feed_front( fn get_feed_front(
pool: &DbPool, conn: &PgConnection,
sort_type: SortType, sort_type: &SortType,
jwt: String, jwt: String,
) -> Result<ChannelBuilder, LemmyError> { ) -> Result<ChannelBuilder, LemmyError> {
let site_view = blocking(pool, move |conn| SiteView::read(conn)).await??; let site_view = SiteView::read(conn)?;
let local_user_id = LocalUserId(Claims::decode(&jwt, pool).await?.claims.sub); let jwt_secret = Secrets::read_jwt_secret(conn)?;
let local_user_id = LocalUserId(Claims::decode(&jwt, jwt_secret.as_ref())?.claims.sub);
let local_user = LocalUser::read(conn, local_user_id)?;
let posts = blocking(pool, move |conn| { let posts = PostQueryBuilder::create(conn)
let local_user = LocalUser::read(conn, local_user_id)?; .listing_type(ListingType::Subscribed)
PostQueryBuilder::create(conn) .my_person_id(local_user.person_id)
.listing_type(ListingType::Subscribed) .show_bot_accounts(local_user.show_bot_accounts)
.my_person_id(local_user.person_id) .show_read_posts(local_user.show_read_posts)
.show_bot_accounts(local_user.show_bot_accounts) .sort(*sort_type)
.show_read_posts(local_user.show_read_posts) .list()?;
.sort(sort_type)
.list()
})
.await??;
let items = create_post_items(posts)?; let items = create_post_items(posts)?;
@ -264,33 +257,28 @@ async fn get_feed_front(
Ok(channel_builder) Ok(channel_builder)
} }
async fn get_feed_inbox(pool: &DbPool, jwt: String) -> Result<ChannelBuilder, LemmyError> { fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result<ChannelBuilder, LemmyError> {
let site_view = blocking(pool, move |conn| SiteView::read(conn)).await??; let site_view = SiteView::read(conn)?;
let local_user_id = LocalUserId(Claims::decode(&jwt, pool).await?.claims.sub); let jwt_secret = Secrets::read_jwt_secret(conn)?;
let local_user = blocking(pool, move |conn| LocalUser::read(conn, local_user_id)).await??; let local_user_id = LocalUserId(Claims::decode(&jwt, jwt_secret.as_ref())?.claims.sub);
let local_user = LocalUser::read(conn, local_user_id)?;
let person_id = local_user.person_id; let person_id = local_user.person_id;
let show_bot_accounts = local_user.show_bot_accounts; let show_bot_accounts = local_user.show_bot_accounts;
let sort = SortType::New; let sort = SortType::New;
let replies = blocking(pool, move |conn| { let replies = CommentQueryBuilder::create(conn)
CommentQueryBuilder::create(conn) .recipient_id(person_id)
.recipient_id(person_id) .my_person_id(person_id)
.my_person_id(person_id) .show_bot_accounts(show_bot_accounts)
.show_bot_accounts(show_bot_accounts) .sort(sort)
.sort(sort) .list()?;
.list()
})
.await??;
let mentions = blocking(pool, move |conn| { let mentions = PersonMentionQueryBuilder::create(conn)
PersonMentionQueryBuilder::create(conn) .recipient_id(person_id)
.recipient_id(person_id) .my_person_id(person_id)
.my_person_id(person_id) .sort(sort)
.sort(sort) .list()?;
.list()
})
.await??;
let items = create_reply_and_mention_items(replies, mentions)?; let items = create_reply_and_mention_items(replies, mentions)?;

View File

@ -2,8 +2,10 @@ use actix_http::http::header::ACCEPT_ENCODING;
use actix_web::{body::BodyStream, http::StatusCode, web::Data, *}; use actix_web::{body::BodyStream, http::StatusCode, web::Data, *};
use anyhow::anyhow; use anyhow::anyhow;
use awc::Client; use awc::Client;
use lemmy_api_common::claims::Claims; use lemmy_api_common::blocking;
use lemmy_utils::{rate_limit::RateLimit, settings::structs::Settings, LemmyError}; use lemmy_db_queries::source::secrets::Secrets_;
use lemmy_db_schema::source::secrets::Secrets;
use lemmy_utils::{claims::Claims, rate_limit::RateLimit, settings::structs::Settings, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::time::Duration; use std::time::Duration;
@ -47,15 +49,16 @@ struct PictrsParams {
async fn upload( async fn upload(
req: HttpRequest, req: HttpRequest,
body: web::Payload, body: web::Payload,
client: web::Data<Client>,
context: web::Data<LemmyContext>, context: web::Data<LemmyContext>,
client: web::Data<Client>,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
// TODO: check rate limit here // TODO: check rate limit here
let jwt = req let jwt = req
.cookie("jwt") .cookie("jwt")
.expect("No auth header for picture upload"); .expect("No auth header for picture upload");
if Claims::decode(jwt.value(), context.pool()).await.is_err() { 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() {
return Ok(HttpResponse::Unauthorized().finish()); return Ok(HttpResponse::Unauthorized().finish());
}; };

View File

@ -38,3 +38,4 @@ http = "0.2.4"
deser-hjson = "1.0.2" deser-hjson = "1.0.2"
smart-default = "0.6.0" smart-default = "0.6.0"
webpage = { version = "1.3.0", default-features = false, features = ["serde"] } webpage = { version = "1.3.0", default-features = false, features = ["serde"] }
jsonwebtoken = "7.2.0"

View File

@ -0,0 +1,37 @@
use crate::{settings::structs::Settings, LemmyError};
use chrono::Utc;
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, TokenData, Validation};
use serde::{Deserialize, Serialize};
type Jwt = String;
#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
/// local_user_id, standard claim by RFC 7519.
pub sub: i32,
pub iss: String,
/// Time when this token was issued as UNIX-timestamp in seconds
pub iat: i64,
}
impl Claims {
pub fn decode(jwt: &str, jwt_secret: &[u8]) -> Result<TokenData<Claims>, LemmyError> {
let v = Validation {
validate_exp: false,
..Validation::default()
};
let key = DecodingKey::from_secret(jwt_secret);
Ok(decode::<Claims>(jwt, &key, &v)?)
}
pub fn jwt(local_user_id: i32, jwt_secret: &[u8]) -> Result<Jwt, LemmyError> {
let my_claims = Claims {
sub: local_user_id,
iss: Settings::get().hostname,
iat: Utc::now().timestamp(),
};
let key = EncodingKey::from_secret(jwt_secret);
Ok(encode(&Header::default(), &my_claims, &key)?)
}
}

View File

@ -11,6 +11,7 @@ pub mod rate_limit;
pub mod request; pub mod request;
pub mod settings; pub mod settings;
pub mod claims;
#[cfg(test)] #[cfg(test)]
mod test; mod test;
pub mod utils; pub mod utils;