Skip to main content

revolt_database/models/mfa_tickets/ops/
mongodb.rs

1use std::time::{Duration, SystemTime};
2
3use crate::{AbstractMFATickets, MFATicket, MongoDb};
4use bson::{to_document, Document};
5use iso8601_timestamp::Timestamp;
6use mongodb::options::UpdateOptions;
7use revolt_result::Result;
8use ulid::Ulid;
9
10const COL: &str = "mfa_tickets";
11
12#[async_trait]
13impl AbstractMFATickets for MongoDb {
14    /// Find ticket by token
15    ///
16    /// Ticket is only valid for 5 minute
17    async fn fetch_ticket_by_token(&self, token: &str) -> Result<MFATicket> {
18        let ticket: MFATicket = query!(self, find_one, COL, doc! { "token": token })?
19            .ok_or_else(|| create_error!(InvalidToken))?;
20
21        if let Ok(ulid) = Ulid::from_string(&ticket.id) {
22            if Timestamp::from(ulid.datetime() + Duration::from_mins(5)) > Timestamp::now_utc() {
23                Ok(ticket)
24            } else {
25                Err(create_error!(InvalidToken))
26            }
27        } else {
28            Err(create_error!(InvalidToken))
29        }
30    }
31
32    /// Save ticket
33    async fn save_ticket(&self, ticket: &MFATicket) -> Result<()> {
34        self.col::<MFATicket>(COL)
35            .update_one(
36                doc! {
37                    "_id": &ticket.id
38                },
39                doc! {
40                    "$set": to_document(ticket).map_err(|_| create_database_error!("to_document", COL))?,
41                },
42            )
43            .with_options(UpdateOptions::builder().upsert(true).build())
44            .await
45            .map_err(|_| create_database_error!("upsert_one", COL))
46            .map(|_| ())
47    }
48
49    /// Delete ticket
50    async fn delete_ticket(&self, id: &str) -> Result<()> {
51        query!(self, delete_one_by_id, COL, id).map(|_| ())
52    }
53
54    /// Delete all expired tickets
55    async fn delete_expired_tickets(&self) -> Result<usize> {
56        let threshhold =
57            Ulid::from_datetime(SystemTime::now() - Duration::from_mins(5)).to_string();
58
59        self.col::<Document>(COL)
60            .delete_many(doc! {
61                "_id": { "$lt": threshhold }
62            })
63            .await
64            .map_err(|_| create_database_error!("delete_many", COL))
65            .map(|result| result.deleted_count as usize)
66    }
67}