Alternative ATProto PDS implementation
at oauth 173 lines 5.4 kB view raw
1//! Based on https://github.com/blacksky-algorithms/rsky/blob/main/rsky-pds/src/account_manager/helpers/email_token.rs 2//! blacksky-algorithms/rsky is licensed under the Apache License 2.0 3//! 4//! Modified for SQLite backend 5use crate::models::pds::EmailToken; 6use crate::models::pds::EmailTokenPurpose; 7use anyhow::{Result, bail}; 8use diesel::*; 9use rsky_common::time::{MINUTE, from_str_to_utc, less_than_ago_s}; 10use rsky_pds::apis::com::atproto::server::get_random_token; 11 12pub async fn create_email_token( 13 did: &str, 14 purpose: EmailTokenPurpose, 15 db: &deadpool_diesel::Pool< 16 deadpool_diesel::Manager<SqliteConnection>, 17 deadpool_diesel::sqlite::Object, 18 >, 19) -> Result<String> { 20 use crate::schema::pds::email_token::dsl as EmailTokenSchema; 21 let token = get_random_token().to_uppercase(); 22 let now = rsky_common::now(); 23 24 let did = did.to_owned(); 25 db.get() 26 .await? 27 .interact(move |conn| { 28 _ = insert_into(EmailTokenSchema::email_token) 29 .values(( 30 EmailTokenSchema::purpose.eq(purpose), 31 EmailTokenSchema::did.eq(did), 32 EmailTokenSchema::token.eq(&token), 33 EmailTokenSchema::requestedAt.eq(&now), 34 )) 35 .on_conflict((EmailTokenSchema::purpose, EmailTokenSchema::did)) 36 .do_update() 37 .set(( 38 EmailTokenSchema::token.eq(&token), 39 EmailTokenSchema::requestedAt.eq(&now), 40 )) 41 .execute(conn)?; 42 Ok(token) 43 }) 44 .await 45 .expect("Failed to create email token") 46} 47 48pub async fn assert_valid_token( 49 did: &str, 50 purpose: EmailTokenPurpose, 51 token: &str, 52 expiration_len: Option<i32>, 53 db: &deadpool_diesel::Pool< 54 deadpool_diesel::Manager<SqliteConnection>, 55 deadpool_diesel::sqlite::Object, 56 >, 57) -> Result<()> { 58 let expiration_len = expiration_len.unwrap_or(MINUTE * 15); 59 use crate::schema::pds::email_token::dsl as EmailTokenSchema; 60 61 let did = did.to_owned(); 62 let token = token.to_owned(); 63 let res = db 64 .get() 65 .await? 66 .interact(move |conn| { 67 EmailTokenSchema::email_token 68 .filter(EmailTokenSchema::purpose.eq(purpose)) 69 .filter(EmailTokenSchema::did.eq(did)) 70 .filter(EmailTokenSchema::token.eq(token.to_uppercase())) 71 .select(EmailToken::as_select()) 72 .first(conn) 73 .optional() 74 }) 75 .await 76 .expect("Failed to assert token")?; 77 if let Some(res) = res { 78 let requested_at = from_str_to_utc(&res.requested_at); 79 let expired = !less_than_ago_s(requested_at, expiration_len); 80 if expired { 81 bail!("Token is expired") 82 } 83 Ok(()) 84 } else { 85 bail!("Token is invalid") 86 } 87} 88 89pub async fn assert_valid_token_and_find_did( 90 purpose: EmailTokenPurpose, 91 token: &str, 92 expiration_len: Option<i32>, 93 db: &deadpool_diesel::Pool< 94 deadpool_diesel::Manager<SqliteConnection>, 95 deadpool_diesel::sqlite::Object, 96 >, 97) -> Result<String> { 98 let expiration_len = expiration_len.unwrap_or(MINUTE * 15); 99 use crate::schema::pds::email_token::dsl as EmailTokenSchema; 100 101 let token = token.to_owned(); 102 let res = db 103 .get() 104 .await? 105 .interact(move |conn| { 106 EmailTokenSchema::email_token 107 .filter(EmailTokenSchema::purpose.eq(purpose)) 108 .filter(EmailTokenSchema::token.eq(token.to_uppercase())) 109 .select(EmailToken::as_select()) 110 .first(conn) 111 .optional() 112 }) 113 .await 114 .expect("Failed to assert token")?; 115 if let Some(res) = res { 116 let requested_at = from_str_to_utc(&res.requested_at); 117 let expired = !less_than_ago_s(requested_at, expiration_len); 118 if expired { 119 bail!("Token is expired") 120 } 121 Ok(res.did) 122 } else { 123 bail!("Token is invalid") 124 } 125} 126 127pub async fn delete_email_token( 128 did: &str, 129 purpose: EmailTokenPurpose, 130 db: &deadpool_diesel::Pool< 131 deadpool_diesel::Manager<SqliteConnection>, 132 deadpool_diesel::sqlite::Object, 133 >, 134) -> Result<()> { 135 use crate::schema::pds::email_token::dsl as EmailTokenSchema; 136 let did = did.to_owned(); 137 _ = db 138 .get() 139 .await? 140 .interact(move |conn| { 141 delete(EmailTokenSchema::email_token) 142 .filter(EmailTokenSchema::did.eq(did)) 143 .filter(EmailTokenSchema::purpose.eq(purpose)) 144 .execute(conn) 145 }) 146 .await 147 .expect("Failed to delete token")?; 148 Ok(()) 149} 150 151pub async fn delete_all_email_tokens( 152 did: &str, 153 db: &deadpool_diesel::Pool< 154 deadpool_diesel::Manager<SqliteConnection>, 155 deadpool_diesel::sqlite::Object, 156 >, 157) -> Result<()> { 158 use crate::schema::pds::email_token::dsl as EmailTokenSchema; 159 160 let did = did.to_owned(); 161 _ = db 162 .get() 163 .await? 164 .interact(move |conn| { 165 delete(EmailTokenSchema::email_token) 166 .filter(EmailTokenSchema::did.eq(did)) 167 .execute(conn) 168 }) 169 .await 170 .expect("Failed to delete all tokens")?; 171 172 Ok(()) 173}