Microservice to bring 2FA to self hosted PDSes

clippy warnings and unwrap cleanups

+7 -7
src/main.rs
··· 21 21 use tower_governor::governor::GovernorConfigBuilder; 22 22 use tower_http::compression::CompressionLayer; 23 23 use tower_http::cors::{Any, CorsLayer}; 24 - use tracing::{error, log}; 24 + use tracing::{error}; 25 25 use tracing_subscriber::{EnvFilter, fmt, prelude::*}; 26 26 27 27 mod middleware; ··· 74 74 75 75 let intro = "\n\nThis is a PDS gatekeeper\n\nCode: https://tangled.sh/@baileytownsend.dev/pds-gatekeeper\n"; 76 76 77 - let banner = format!(" {}\n{}", body, intro); 77 + let banner = format!(" {body}\n{intro}"); 78 78 79 79 ( 80 80 [(header::CONTENT_TYPE, "text/plain; charset=utf-8")], ··· 85 85 #[tokio::main] 86 86 async fn main() -> Result<(), Box<dyn std::error::Error>> { 87 87 setup_tracing(); 88 - //TODO prod 88 + //TODO may need to change where this reads from? Like an env variable for it's location? 89 89 dotenvy::from_path(Path::new("./pds.env"))?; 90 90 let pds_root = env::var("PDS_DATA_DIRECTORY")?; 91 - let account_db_url = format!("{}/account.sqlite", pds_root); 92 - log::info!("accounts_db_url: {}", account_db_url); 91 + let account_db_url = format!("{pds_root}/account.sqlite"); 93 92 94 93 let account_options = SqliteConnectOptions::new() 95 94 .journal_mode(SqliteJournalMode::Wal) ··· 100 99 .connect_with(account_options) 101 100 .await?; 102 101 103 - let bells_db_url = format!("{}/pds_gatekeeper.sqlite", pds_root); 102 + let bells_db_url = format!("{pds_root}/pds_gatekeeper.sqlite"); 104 103 let options = SqliteConnectOptions::new() 105 104 .journal_mode(SqliteJournalMode::Wal) 106 105 .filename(bells_db_url) ··· 149 148 .per_second(60) 150 149 .burst_size(5) 151 150 .finish() 152 - .unwrap(); 151 + .expect("failed to create governor config. this hsould not happen and is a bug"); 152 + 153 153 let governor_limiter = governor_conf.limiter().clone(); 154 154 let interval = Duration::from_secs(60); 155 155 // a separate background task to clean up
+9 -11
src/middleware.rs
··· 23 23 match token { 24 24 Ok(token) => { 25 25 match token { 26 - None => json_error_response(StatusCode::BAD_REQUEST, "TokenRequired", "").unwrap(), 26 + None => json_error_response(StatusCode::BAD_REQUEST, "TokenRequired", "").expect("Error creating an error response"), 27 27 Some(token) => { 28 28 let token = UntrustedToken::new(&token); 29 - //Doing weird unwraps cause I can't do Result for middleware? 30 29 if token.is_err() { 31 30 return json_error_response(StatusCode::BAD_REQUEST, "TokenRequired", "") 32 - .unwrap(); 31 + .expect("Error creating an error response"); 33 32 } 34 - let parsed_token = token.unwrap(); 33 + let parsed_token = token.expect("Already checked for error"); 35 34 let claims: Result<Claims<TokenClaims>, ValidationError> = 36 35 parsed_token.deserialize_claims_unchecked(); 37 36 if claims.is_err() { 38 37 return json_error_response(StatusCode::BAD_REQUEST, "TokenRequired", "") 39 - .unwrap(); 38 + .expect("Error creating an error response"); 40 39 } 41 40 42 - let key = Hs256Key::new(env::var("PDS_JWT_SECRET").unwrap()); 41 + let key = Hs256Key::new(env::var("PDS_JWT_SECRET").expect("PDS_JWT_SECRET not set in the pds.env")); 43 42 let token: Result<Token<TokenClaims>, ValidationError> = 44 43 Hs256.validator(&key).validate(&parsed_token); 45 44 if token.is_err() { 46 45 return json_error_response(StatusCode::BAD_REQUEST, "InvalidToken", "") 47 - .unwrap(); 46 + .expect("Error creating an error response"); 48 47 } 49 - let token = token.unwrap(); 48 + let token = token.expect("Already checked for error,"); 50 49 //Not going to worry about expiration since it still goes to the PDS 51 - 52 50 req.extensions_mut() 53 51 .insert(Did(Some(token.claims().custom.sub.clone()))); 54 52 next.run(req).await ··· 56 54 } 57 55 } 58 56 Err(err) => { 59 - log::error!("Error extracting token: {}", err); 60 - json_error_response(StatusCode::BAD_REQUEST, "InvalidToken", "").unwrap() 57 + log::error!("Error extracting token: {err}"); 58 + json_error_response(StatusCode::BAD_REQUEST, "InvalidToken", "").expect("Error creating an error response") 61 59 } 62 60 } 63 61 }
+2 -2
src/xrpc/com_atproto_server.rs
··· 122 122 }, 123 123 Err(err) => { 124 124 log::error!( 125 - "Error during pre-auth check. This happens on the create_session endpoint when trying to decide if the user has access\n {}", 126 - err 125 + "Error during pre-auth check. This happens on the create_session endpoint when trying to decide if the user has access\n {err}" 126 + 127 127 ); 128 128 json_error_response( 129 129 StatusCode::INTERNAL_SERVER_ERROR,
+27 -32
src/xrpc/helpers.rs
··· 1 + use anyhow::anyhow; 1 2 use crate::AppState; 2 3 use crate::xrpc::helpers::TokenCheckError::InvalidToken; 3 4 use axum::body::{Body, to_bytes}; ··· 103 104 //Just going a head and doing uppercase here. 104 105 let slice_one = &full_code[0..5].to_ascii_uppercase(); 105 106 let slice_two = &full_code[5..10].to_ascii_uppercase(); 106 - format!("{}-{}", slice_one, slice_two) 107 + format!("{slice_one}-{slice_two}") 107 108 } 108 109 109 110 pub enum TokenCheckError { ··· 151 152 let sha = hasher.finalize(); 152 153 let salt = hex::encode(&sha[..16]); 153 154 let hash_hex = scrypt_hex(password, &salt)?; 154 - Ok(format!("{}:{}", salt, hash_hex)) 155 + Ok(format!("{salt}:{hash_hex}")) 155 156 } 156 157 157 - async fn verify_password(password: &str, password_scrypt: &str) -> Result<bool, StatusCode> { 158 + async fn verify_password(password: &str, password_scrypt: &str) -> anyhow::Result<bool> { 158 159 // Expected format: "salt:hash" where hash is hex of scrypt(password, salt, 64 bytes) 159 160 let mut parts = password_scrypt.splitn(2, ':'); 160 161 let salt = match parts.next() { ··· 195 196 ) 196 197 .bind(identifier) 197 198 .fetch_optional(&state.account_pool) 198 - .await 199 - .map_err(|_| StatusCode::BAD_REQUEST)?, 199 + .await?, 200 200 IdentifierType::Handle => sqlx::query_as::<_, (String, String, String, String)>( 201 201 "SELECT account.did, account.passwordScrypt, account.email, actor.handle 202 202 FROM actor ··· 205 205 ) 206 206 .bind(identifier) 207 207 .fetch_optional(&state.account_pool) 208 - .await 209 - .map_err(|_| StatusCode::BAD_REQUEST)?, 208 + .await?, 210 209 IdentifierType::Did => sqlx::query_as::<_, (String, String, String, String)>( 211 210 "SELECT account.did, account.passwordScrypt, account.email, actor.handle 212 211 FROM actor ··· 215 214 ) 216 215 .bind(identifier) 217 216 .fetch_optional(&state.account_pool) 218 - .await 219 - .map_err(|_| StatusCode::BAD_REQUEST)?, 217 + .await?, 220 218 }; 221 219 222 220 if let Some((did, password_scrypt, email, handle)) = account_row { ··· 226 224 ) 227 225 .bind(did.clone()) 228 226 .fetch_optional(&state.pds_gatekeeper_pool) 229 - .await 230 - .map_err(|_| StatusCode::BAD_REQUEST)?; 227 + .await?; 231 228 232 229 let two_factor_required = match required_opt { 233 230 Some(row) => row.0 != 0, ··· 249 246 } 250 247 } 251 248 Err(err) => { 252 - log::error!("Error checking the app password: {}", err); 253 - Err(StatusCode::BAD_REQUEST) 249 + log::error!("Error checking the app password: {err}"); 250 + Err(err) 254 251 } 255 252 }; 256 253 } ··· 266 263 .await 267 264 { 268 265 Ok(_) => { 269 - let _ = delete_all_email_tokens(&state.account_pool, did.clone()).await; 266 + let result_of_cleanup = delete_all_email_tokens(&state.account_pool, did.clone()).await; 267 + if result_of_cleanup.is_err(){ 268 + log::error!("There was an error deleting the email tokens after login: {:?}", result_of_cleanup.err()) 269 + } 270 270 Ok(AuthResult::ProxyThrough) 271 271 } 272 272 Err(err) => Ok(AuthResult::TokenCheckFailed(err)), ··· 275 275 } 276 276 277 277 return match create_two_factor_token(&state.account_pool, did).await { 278 - //TODO replace unwraps with the mythical ? 279 278 Ok(code) => { 280 279 let mut email_data = Map::new(); 281 280 email_data.insert("token".to_string(), Value::from(code.clone())); 282 281 email_data.insert("handle".to_string(), Value::from(handle.clone())); 283 - //TODO bad unwrap 284 282 let email_body = state 285 283 .template_engine 286 - .render("two_factor_code.hbs", email_data) 287 - .unwrap(); 284 + .render("two_factor_code.hbs", email_data)?; 288 285 289 286 let email = Message::builder() 290 287 //TODO prob get the proper type in the state 291 - .from(state.mailer_from.parse().unwrap()) 292 - .to(email.parse().unwrap()) 288 + .from(state.mailer_from.parse()?) 289 + .to(email.parse()?) 293 290 .subject("Sign in to Bluesky") 294 291 .multipart( 295 292 MultiPart::alternative() // This is composed of two parts. 296 293 .singlepart( 297 294 SinglePart::builder() 298 295 .header(header::ContentType::TEXT_PLAIN) 299 - .body(format!("We received a sign-in request for the account @{}. Use the code: {} to sign in. If this wasn't you, we recommend taking steps to protect your account by changing your password at https://bsky.app/settings.", handle, code)), // Every message should have a plain text fallback. 296 + .body(format!("We received a sign-in request for the account @{handle}. Use the code: {code} to sign in. If this wasn't you, we recommend taking steps to protect your account by changing your password at https://bsky.app/settings.")), // Every message should have a plain text fallback. 300 297 ) 301 298 .singlepart( 302 299 SinglePart::builder() 303 300 .header(header::ContentType::TEXT_HTML) 304 301 .body(email_body), 305 302 ), 306 - ) 307 - //TODO bad 308 - .unwrap(); 303 + )?; 309 304 match state.mailer.send(email).await { 310 305 Ok(_) => Ok(AuthResult::TwoFactorRequired), 311 306 Err(err) => { 312 - log::error!("Error sending the 2FA email: {}", err); 313 - Err(StatusCode::BAD_REQUEST) 307 + log::error!("Error sending the 2FA email: {err}"); 308 + Err(anyhow!(err)) 314 309 } 315 310 } 316 311 } 317 312 Err(err) => { 318 - log::error!("error on creating a 2fa token: {}", err); 319 - Err(StatusCode::BAD_REQUEST) 313 + log::error!("error on creating a 2fa token: {err}"); 314 + Err(anyhow!(err)) 320 315 } 321 316 }; 322 317 } ··· 351 346 352 347 match res { 353 348 Ok(_) => Ok(token), 354 - Err(e) => { 355 - log::error!("Error creating a two factor token: {}", e); 356 - Err(anyhow::anyhow!(e)) 349 + Err(err) => { 350 + log::error!("Error creating a two factor token: {err}"); 351 + Err(anyhow::anyhow!(err)) 357 352 } 358 353 } 359 354 } ··· 383 378 .fetch_optional(account_db) 384 379 .await 385 380 .map_err(|err| { 386 - log::error!("Error getting the 2fa token: {}", err); 381 + log::error!("Error getting the 2fa token: {err}"); 387 382 InvalidToken 388 383 })?; 389 384