this repo has no description
at main 151 lines 5.0 kB view raw
1use crate::advent::day::Day; 2use crate::advent::{AdventChallenge, AdventError, AdventPart, ChallengeCheckResponse}; 3use crate::lexicons::codes::advent; 4use crate::lexicons::record::KnownRecord; 5use crate::{OAuthAgentType, PasswordAgent}; 6use async_trait::async_trait; 7use atrium_api::types::Collection; 8use atrium_api::types::string::Tid; 9use serde_json::json; 10use sqlx::PgPool; 11 12pub struct DayFive { 13 pub pool: PgPool, 14 pub oauth_client: Option<OAuthAgentType>, 15 pub secret_agent: Option<PasswordAgent>, 16} 17 18impl DayFive { 19 /// Creates the challenge record on the secret agent's repo with the user's verification code. 20 /// This is called from the `/day/5/{did}` endpoint. 21 pub async fn create_secret_record(&self, did: &str) -> Result<(), AdventError> { 22 let Some(agent) = &self.secret_agent else { 23 log::warn!("No secret agent configured, skipping record creation for day five"); 24 return Err(AdventError::ShouldNotHappen( 25 "No secret agent configured".to_string(), 26 )); 27 }; 28 29 // Get the user's challenge to find their verification code 30 let challenge = self.get_days_challenge(did).await?.ok_or_else(|| { 31 AdventError::ShouldNotHappen("Could not find challenge record for day 5".to_string()) 32 })?; 33 34 let code = challenge.verification_code_one.ok_or_else(|| { 35 AdventError::ShouldNotHappen( 36 "No verification code found for day 5 challenge".to_string(), 37 ) 38 })?; 39 40 let agent_did = agent 41 .did() 42 .await 43 .ok_or_else(|| AdventError::ShouldNotHappen("Secret agent has no DID".to_string()))?; 44 45 let record_data = advent::challenge::shhh::RecordData { 46 secret_part_one: code, 47 created_at: atrium_api::types::string::Datetime::now(), 48 subject: did.parse().unwrap(), 49 }; 50 let known_record: KnownRecord = record_data.into(); 51 let record_value: atrium_api::types::Unknown = known_record.into(); 52 53 let tid = Tid::from_datetime(23.try_into().unwrap(), challenge.time_started); 54 let result = agent 55 .api 56 .com 57 .atproto 58 .repo 59 .put_record( 60 atrium_api::com::atproto::repo::put_record::InputData { 61 collection: advent::challenge::Shhh::NSID.parse().unwrap(), 62 repo: agent_did.as_ref().parse().unwrap(), 63 rkey: tid.as_ref().parse().unwrap(), 64 swap_record: None, 65 record: record_value, 66 swap_commit: None, 67 validate: Some(false), 68 } 69 .into(), 70 ) 71 .await; 72 73 match result { 74 Ok(_) => { 75 log::info!("Created secret record for day 5 for user: {did}"); 76 Ok(()) 77 } 78 Err(e) => { 79 log::error!("Failed to create secret record for day 5: {e}"); 80 Err(AdventError::ShouldNotHappen(format!( 81 "Failed to create secret record: {e}" 82 ))) 83 } 84 } 85 } 86} 87 88#[async_trait] 89impl AdventChallenge for DayFive { 90 fn pool(&self) -> &PgPool { 91 &self.pool 92 } 93 94 fn day(&self) -> Day { 95 Day::Five 96 } 97 98 fn has_part_two(&self) -> bool { 99 false 100 } 101 102 fn requires_manual_verification_part_one(&self) -> bool { 103 true 104 } 105 106 async fn build_additional_context( 107 &self, 108 did: &str, 109 part: &AdventPart, 110 _code: &str, 111 ) -> Result<Option<serde_json::Value>, AdventError> { 112 match part { 113 AdventPart::One => Ok(Some(json!({ "did": did }))), 114 AdventPart::Two => Ok(None), 115 } 116 } 117 118 async fn check_part_one( 119 &self, 120 did: String, 121 verification_code: Option<String>, 122 ) -> Result<ChallengeCheckResponse, AdventError> { 123 let submitted_code = match verification_code { 124 Some(code) if !code.is_empty() => code, 125 _ => { 126 return Ok(ChallengeCheckResponse::Incorrect( 127 "Please enter a verification code".to_string(), 128 )); 129 } 130 }; 131 132 let Some(challenge) = self.get_days_challenge(&did).await? else { 133 log::error!("Could not find a challenge record for day: 5 for the user: {did:?}"); 134 return Err(AdventError::ShouldNotHappen( 135 "Could not find challenge record".to_string(), 136 )); 137 }; 138 139 let expected_code = challenge 140 .verification_code_one 141 .ok_or(AdventError::ShouldNotHappen( 142 "no verification code for day 5 challenge".to_string(), 143 ))?; 144 145 Ok(if submitted_code == expected_code { 146 ChallengeCheckResponse::Correct 147 } else { 148 ChallengeCheckResponse::Incorrect(format!("The code {} is incorrect", submitted_code)) 149 }) 150 } 151}