this repo has no description
1mod common; 2use common::*; 3use reqwest::StatusCode; 4use serde_json::json; 5use sqlx::PgPool; 6 7#[tokio::test] 8async fn test_plc_operation_auth() { 9 let client = client(); 10 let res = client.post(format!("{}/xrpc/com.atproto.identity.requestPlcOperationSignature", base_url().await)) 11 .send().await.unwrap(); 12 assert_eq!(res.status(), StatusCode::UNAUTHORIZED); 13 let res = client.post(format!("{}/xrpc/com.atproto.identity.signPlcOperation", base_url().await)) 14 .json(&json!({})).send().await.unwrap(); 15 assert_eq!(res.status(), StatusCode::UNAUTHORIZED); 16 let res = client.post(format!("{}/xrpc/com.atproto.identity.submitPlcOperation", base_url().await)) 17 .json(&json!({ "operation": {} })).send().await.unwrap(); 18 assert_eq!(res.status(), StatusCode::UNAUTHORIZED); 19 let (token, _) = create_account_and_login(&client).await; 20 let res = client.post(format!("{}/xrpc/com.atproto.identity.requestPlcOperationSignature", base_url().await)) 21 .bearer_auth(&token).send().await.unwrap(); 22 assert_eq!(res.status(), StatusCode::OK); 23} 24 25#[tokio::test] 26async fn test_sign_plc_operation_validation() { 27 let client = client(); 28 let (token, _) = create_account_and_login(&client).await; 29 let res = client.post(format!("{}/xrpc/com.atproto.identity.signPlcOperation", base_url().await)) 30 .bearer_auth(&token).json(&json!({})).send().await.unwrap(); 31 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 32 let body: serde_json::Value = res.json().await.unwrap(); 33 assert_eq!(body["error"], "InvalidRequest"); 34 let res = client.post(format!("{}/xrpc/com.atproto.identity.signPlcOperation", base_url().await)) 35 .bearer_auth(&token).json(&json!({ "token": "invalid-token-12345" })).send().await.unwrap(); 36 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 37 let body: serde_json::Value = res.json().await.unwrap(); 38 assert!(body["error"] == "InvalidToken" || body["error"] == "ExpiredToken"); 39} 40 41#[tokio::test] 42async fn test_submit_plc_operation_validation() { 43 let client = client(); 44 let (token, did) = create_account_and_login(&client).await; 45 let hostname = std::env::var("PDS_HOSTNAME").unwrap_or_else(|_| format!("127.0.0.1:{}", app_port())); 46 let res = client.post(format!("{}/xrpc/com.atproto.identity.submitPlcOperation", base_url().await)) 47 .bearer_auth(&token).json(&json!({ "operation": { "type": "invalid_type" } })).send().await.unwrap(); 48 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 49 let body: serde_json::Value = res.json().await.unwrap(); 50 assert_eq!(body["error"], "InvalidRequest"); 51 let res = client.post(format!("{}/xrpc/com.atproto.identity.submitPlcOperation", base_url().await)) 52 .bearer_auth(&token).json(&json!({ 53 "operation": { "type": "plc_operation", "rotationKeys": [], "verificationMethods": {}, 54 "alsoKnownAs": [], "services": {}, "prev": null } 55 })).send().await.unwrap(); 56 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 57 let handle = did.split(':').last().unwrap_or("user"); 58 let res = client.post(format!("{}/xrpc/com.atproto.identity.submitPlcOperation", base_url().await)) 59 .bearer_auth(&token).json(&json!({ 60 "operation": { "type": "plc_operation", "rotationKeys": ["did:key:z123"], 61 "verificationMethods": { "atproto": "did:key:z456" }, 62 "alsoKnownAs": [format!("at://{}", handle)], 63 "services": { "atproto_pds": { "type": "AtprotoPersonalDataServer", "endpoint": "https://wrong.example.com" } }, 64 "prev": null, "sig": "fake_signature" } 65 })).send().await.unwrap(); 66 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 67 let res = client.post(format!("{}/xrpc/com.atproto.identity.submitPlcOperation", base_url().await)) 68 .bearer_auth(&token).json(&json!({ 69 "operation": { "type": "plc_operation", "rotationKeys": ["did:key:zWrongRotationKey123"], 70 "verificationMethods": { "atproto": "did:key:zWrongVerificationKey456" }, 71 "alsoKnownAs": [format!("at://{}", handle)], 72 "services": { "atproto_pds": { "type": "AtprotoPersonalDataServer", "endpoint": format!("https://{}", hostname) } }, 73 "prev": null, "sig": "fake_signature" } 74 })).send().await.unwrap(); 75 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 76 let body: serde_json::Value = res.json().await.unwrap(); 77 assert_eq!(body["error"], "InvalidRequest"); 78 assert!(body["message"].as_str().unwrap_or("").contains("signing key") || body["message"].as_str().unwrap_or("").contains("rotation")); 79 let res = client.post(format!("{}/xrpc/com.atproto.identity.submitPlcOperation", base_url().await)) 80 .bearer_auth(&token).json(&json!({ 81 "operation": { "type": "plc_operation", "rotationKeys": ["did:key:z123"], 82 "verificationMethods": { "atproto": "did:key:z456" }, 83 "alsoKnownAs": ["at://totally.wrong.handle"], 84 "services": { "atproto_pds": { "type": "AtprotoPersonalDataServer", "endpoint": format!("https://{}", hostname) } }, 85 "prev": null, "sig": "fake_signature" } 86 })).send().await.unwrap(); 87 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 88 let res = client.post(format!("{}/xrpc/com.atproto.identity.submitPlcOperation", base_url().await)) 89 .bearer_auth(&token).json(&json!({ 90 "operation": { "type": "plc_operation", "rotationKeys": ["did:key:z123"], 91 "verificationMethods": { "atproto": "did:key:z456" }, 92 "alsoKnownAs": ["at://user"], 93 "services": { "atproto_pds": { "type": "WrongServiceType", "endpoint": format!("https://{}", hostname) } }, 94 "prev": null, "sig": "fake_signature" } 95 })).send().await.unwrap(); 96 assert_eq!(res.status(), StatusCode::BAD_REQUEST); 97} 98 99#[tokio::test] 100async fn test_plc_token_lifecycle() { 101 let client = client(); 102 let (token, did) = create_account_and_login(&client).await; 103 let res = client.post(format!("{}/xrpc/com.atproto.identity.requestPlcOperationSignature", base_url().await)) 104 .bearer_auth(&token).send().await.unwrap(); 105 assert_eq!(res.status(), StatusCode::OK); 106 let db_url = get_db_connection_string().await; 107 let pool = PgPool::connect(&db_url).await.unwrap(); 108 let row = sqlx::query!( 109 "SELECT t.token, t.expires_at FROM plc_operation_tokens t JOIN users u ON t.user_id = u.id WHERE u.did = $1", 110 did 111 ).fetch_optional(&pool).await.unwrap(); 112 assert!(row.is_some(), "PLC token should be created in database"); 113 let row = row.unwrap(); 114 assert_eq!(row.token.len(), 11, "Token should be in format xxxxx-xxxxx"); 115 assert!(row.token.contains('-'), "Token should contain hyphen"); 116 assert!(row.expires_at > chrono::Utc::now(), "Token should not be expired"); 117 let diff = row.expires_at - chrono::Utc::now(); 118 assert!(diff.num_minutes() >= 9 && diff.num_minutes() <= 11, "Token should expire in ~10 minutes"); 119 let token1 = row.token.clone(); 120 let res = client.post(format!("{}/xrpc/com.atproto.identity.requestPlcOperationSignature", base_url().await)) 121 .bearer_auth(&token).send().await.unwrap(); 122 assert_eq!(res.status(), StatusCode::OK); 123 let token2 = sqlx::query_scalar!( 124 "SELECT t.token FROM plc_operation_tokens t JOIN users u ON t.user_id = u.id WHERE u.did = $1", did 125 ).fetch_one(&pool).await.unwrap(); 126 assert_ne!(token1, token2, "Second request should generate a new token"); 127 let count: i64 = sqlx::query_scalar!( 128 "SELECT COUNT(*) as \"count!\" FROM plc_operation_tokens t JOIN users u ON t.user_id = u.id WHERE u.did = $1", did 129 ).fetch_one(&pool).await.unwrap(); 130 assert_eq!(count, 1, "Should only have one token per user"); 131}