Alternative ATProto PDS implementation

prototype auth

Changed files
+191 -67
src
+52 -49
src/actor_endpoints.rs
··· 3 3 use constcat::concat; 4 4 use diesel::prelude::*; 5 5 6 + use crate::actor_store::ActorStore; 7 + 6 8 use super::*; 7 9 8 10 async fn put_preferences( ··· 14 16 let json_string = 15 17 serde_json::to_string(&input.preferences).context("failed to serialize preferences")?; 16 18 17 - let conn = &mut actor_pools 18 - .get(&did) 19 - .context("failed to get actor pool")? 20 - .repo 21 - .get() 22 - .await 23 - .expect("failed to get database connection"); 24 - conn.interact(move |conn| { 25 - diesel::update(accounts::table) 26 - .filter(accounts::did.eq(did)) 27 - .set(accounts::private_prefs.eq(json_string)) 28 - .execute(conn) 29 - .context("failed to update user preferences") 30 - }); 31 - 19 + // let conn = &mut actor_pools 20 + // .get(&did) 21 + // .context("failed to get actor pool")? 22 + // .repo 23 + // .get() 24 + // .await 25 + // .expect("failed to get database connection"); 26 + // conn.interact(move |conn| { 27 + // diesel::update(accounts::table) 28 + // .filter(accounts::did.eq(did)) 29 + // .set(accounts::private_prefs.eq(json_string)) 30 + // .execute(conn) 31 + // .context("failed to update user preferences") 32 + // }); 33 + todo!("Use actor_store's preferences writer instead"); 32 34 Ok(()) 33 35 } 34 36 ··· 37 39 State(actor_pools): State<std::collections::HashMap<String, ActorPools>>, 38 40 ) -> Result<Json<actor::get_preferences::Output>> { 39 41 let did = user.did(); 40 - let conn = &mut actor_pools 41 - .get(&did) 42 - .context("failed to get actor pool")? 43 - .repo 44 - .get() 45 - .await 46 - .expect("failed to get database connection"); 42 + // let conn = &mut actor_pools 43 + // .get(&did) 44 + // .context("failed to get actor pool")? 45 + // .repo 46 + // .get() 47 + // .await 48 + // .expect("failed to get database connection"); 47 49 48 - #[derive(QueryableByName)] 49 - struct Prefs { 50 - #[diesel(sql_type = diesel::sql_types::Text)] 51 - private_prefs: Option<String>, 52 - } 50 + // #[derive(QueryableByName)] 51 + // struct Prefs { 52 + // #[diesel(sql_type = diesel::sql_types::Text)] 53 + // private_prefs: Option<String>, 54 + // } 53 55 54 - let result = conn 55 - .interact(move |conn| { 56 - diesel::sql_query("SELECT private_prefs FROM accounts WHERE did = ?") 57 - .bind::<diesel::sql_types::Text, _>(did) 58 - .get_result::<Prefs>(conn) 59 - }) 60 - .await 61 - .expect("failed to fetch preferences"); 56 + // let result = conn 57 + // .interact(move |conn| { 58 + // diesel::sql_query("SELECT private_prefs FROM accounts WHERE did = ?") 59 + // .bind::<diesel::sql_types::Text, _>(did) 60 + // .get_result::<Prefs>(conn) 61 + // }) 62 + // .await 63 + // .expect("failed to fetch preferences"); 62 64 63 - if let Some(prefs_json) = result.private_prefs { 64 - let prefs: actor::defs::Preferences = 65 - serde_json::from_str(&prefs_json).context("failed to deserialize preferences")?; 65 + // if let Some(prefs_json) = result.private_prefs { 66 + // let prefs: actor::defs::Preferences = 67 + // serde_json::from_str(&prefs_json).context("failed to deserialize preferences")?; 66 68 67 - Ok(Json( 68 - actor::get_preferences::OutputData { preferences: prefs }.into(), 69 - )) 70 - } else { 71 - Ok(Json( 72 - actor::get_preferences::OutputData { 73 - preferences: Vec::new(), 74 - } 75 - .into(), 76 - )) 77 - } 69 + // Ok(Json( 70 + // actor::get_preferences::OutputData { preferences: prefs }.into(), 71 + // )) 72 + // } else { 73 + // Ok(Json( 74 + // actor::get_preferences::OutputData { 75 + // preferences: Vec::new(), 76 + // } 77 + // .into(), 78 + // )) 79 + // } 80 + todo!("Use actor_store's preferences writer instead"); 78 81 } 79 82 80 83 /// Register all actor endpoints.
+22 -18
src/auth.rs
··· 130 130 131 131 // Extract subject (DID) 132 132 if let Some(did) = claims.get("sub").and_then(serde_json::Value::as_str) { 133 - use crate::schema::accounts::dsl as AccountSchema; 133 + use rsky_pds::schema::pds::account::dsl as AccountSchema; 134 + let did_clone = did.to_owned(); 134 135 135 - let _status = state 136 + let _did = state 136 137 .db 137 138 .get() 138 139 .await 139 140 .expect("failed to get db connection") 140 141 .interact(move |conn| { 141 - AccountSchema::accounts 142 - .filter(AccountSchema::did.eq(did.to_string())) 143 - .select(AccountSchema::status) 142 + AccountSchema::account 143 + .filter(AccountSchema::did.eq(did_clone)) 144 + .select(AccountSchema::did) 144 145 .first::<String>(conn) 145 146 }) 146 147 .await 147 - .with_context(|| format!("failed to query account {did}")) 148 - .context("should fetch account status")?; 148 + .expect("failed to query account"); 149 149 150 150 Ok(AuthenticatedUser { 151 151 did: did.to_owned(), ··· 338 338 339 339 let timestamp = chrono::Utc::now().timestamp(); 340 340 341 - use crate::schema::oauth_used_jtis::dsl as JtiSchema; 341 + use crate::schema::pds::oauth_used_jtis::dsl as JtiSchema; 342 342 343 343 // Check if JTI has been used before 344 344 let jti_string = jti.to_string(); ··· 354 354 .get_result::<i64>(conn) 355 355 }) 356 356 .await 357 - .context("failed to check JTI")?; 357 + .expect("failed to query JTI") 358 + .expect("failed to get JTI count"); 358 359 359 360 if jti_used > 0 { 360 361 return Err(Error::with_status( ··· 389 390 .execute(conn) 390 391 }) 391 392 .await 392 - .context("failed to store JTI")?; 393 + .expect("failed to insert JTI") 394 + .expect("failed to insert JTI"); 393 395 394 396 // Extract subject (DID) from access token 395 - if let Some(did) = claims.get("sub").and_then(|v| v.as_str) { 396 - use crate::schema::accounts::dsl as AccountSchema; 397 + if let Some(did) = claims.get("sub").and_then(|v| v.as_str()) { 398 + use rsky_pds::schema::pds::account::dsl as AccountSchema; 397 399 398 - let _status = state 400 + let did_clone = did.to_owned(); 401 + 402 + let _did = state 399 403 .db 400 404 .get() 401 405 .await 402 406 .expect("failed to get db connection") 403 407 .interact(move |conn| { 404 - AccountSchema::accounts 405 - .filter(AccountSchema::did.eq(did.to_string())) 406 - .select(AccountSchema::status) 408 + AccountSchema::account 409 + .filter(AccountSchema::did.eq(did_clone)) 410 + .select(AccountSchema::did) 407 411 .first::<String>(conn) 408 412 }) 409 413 .await 410 - .with_context(|| format!("failed to query account {did}")) 411 - .context("should fetch account status")?; 414 + .expect("failed to query account") 415 + .expect("failed to get account"); 412 416 413 417 Ok(AuthenticatedUser { 414 418 did: did.to_owned(),
+1
src/main.rs
··· 12 12 mod mmap; 13 13 mod oauth; 14 14 mod plc; 15 + mod schema; 15 16 #[cfg(test)] 16 17 mod tests; 17 18
+116
src/schema.rs
··· 1 + pub mod pds { 2 + 3 + // Legacy tables 4 + 5 + diesel::table! { 6 + pds.oauth_par_requests (request_uri) { 7 + request_uri -> Varchar, 8 + client_id -> Varchar, 9 + response_type -> Varchar, 10 + code_challenge -> Varchar, 11 + code_challenge_method -> Varchar, 12 + state -> Nullable<Varchar>, 13 + login_hint -> Nullable<Varchar>, 14 + scope -> Nullable<Varchar>, 15 + redirect_uri -> Nullable<Varchar>, 16 + response_mode -> Nullable<Varchar>, 17 + display -> Nullable<Varchar>, 18 + created_at -> Int8, 19 + expires_at -> Int8, 20 + } 21 + } 22 + diesel::table! { 23 + pds.oauth_authorization_codes (code) { 24 + code -> Varchar, 25 + client_id -> Varchar, 26 + subject -> Varchar, 27 + code_challenge -> Varchar, 28 + code_challenge_method -> Varchar, 29 + redirect_uri -> Varchar, 30 + scope -> Nullable<Varchar>, 31 + created_at -> Int8, 32 + expires_at -> Int8, 33 + used -> Bool, 34 + } 35 + } 36 + diesel::table! { 37 + pds.oauth_refresh_tokens (token) { 38 + token -> Varchar, 39 + client_id -> Varchar, 40 + subject -> Varchar, 41 + dpop_thumbprint -> Varchar, 42 + scope -> Nullable<Varchar>, 43 + created_at -> Int8, 44 + expires_at -> Int8, 45 + revoked -> Bool, 46 + } 47 + } 48 + diesel::table! { 49 + pds.oauth_used_jtis (jti) { 50 + jti -> Varchar, 51 + issuer -> Varchar, 52 + created_at -> Int8, 53 + expires_at -> Int8, 54 + } 55 + } 56 + 57 + // Upcoming tables 58 + 59 + diesel::table! { 60 + pds.authorization_request (id) { 61 + id -> Varchar, 62 + did -> Nullable<Varchar>, 63 + deviceId -> Nullable<Varchar>, 64 + clientId -> Varchar, 65 + clientAuth -> Varchar, 66 + parameters -> Varchar, 67 + expiresAt -> Timestamptz, 68 + code -> Nullable<Varchar>, 69 + } 70 + } 71 + 72 + diesel::table! { 73 + pds.device (id) { 74 + id -> Varchar, 75 + sessionId -> Nullable<Varchar>, 76 + userAgent -> Nullable<Varchar>, 77 + ipAddress -> Varchar, 78 + lastSeenAt -> Timestamptz, 79 + } 80 + } 81 + 82 + diesel::table! { 83 + pds.device_account (deviceId, did) { 84 + did -> Varchar, 85 + deviceId -> Varchar, 86 + authenticatedAt -> Timestamptz, 87 + remember -> Bool, 88 + authorizedClients -> Varchar, 89 + } 90 + } 91 + 92 + diesel::table! { 93 + pds.token (id) { 94 + id -> Varchar, 95 + did -> Varchar, 96 + tokenId -> Varchar, 97 + createdAt -> Timestamptz, 98 + updatedAt -> Timestamptz, 99 + expiresAt -> Timestamptz, 100 + clientId -> Varchar, 101 + clientAuth -> Varchar, 102 + deviceId -> Nullable<Varchar>, 103 + parameters -> Varchar, 104 + details -> Nullable<Varchar>, 105 + code -> Nullable<Varchar>, 106 + currentRefreshToken -> Nullable<Varchar>, 107 + } 108 + } 109 + 110 + diesel::table! { 111 + pds.used_refresh_token (refreshToken) { 112 + refreshToken -> Varchar, 113 + tokenId -> Varchar, 114 + } 115 + } 116 + }