sylvia_iot_auth/models/mongodb/
authorization_code.rs1use std::{error::Error as StdError, sync::Arc};
2
3use async_trait::async_trait;
4use chrono::TimeDelta;
5use futures::TryStreamExt;
6use mongodb::{
7 Database,
8 bson::{DateTime, Document, doc},
9};
10use serde::{Deserialize, Serialize};
11
12use sylvia_iot_corelib::err::E_UNKNOWN;
13
14use super::super::authorization_code::{
15 AuthorizationCode, AuthorizationCodeModel, EXPIRES, QueryCond,
16};
17
18pub struct Model {
20 conn: Arc<Database>,
22}
23
24#[derive(Deserialize, Serialize)]
26struct Schema {
27 code: String,
28 #[serde(rename = "expiresAt")]
29 expires_at: DateTime,
30 #[serde(rename = "redirectUri")]
31 redirect_uri: String,
32 scope: Option<String>,
33 #[serde(rename = "clientId")]
34 client_id: String,
35 #[serde(rename = "userId")]
36 user_id: String,
37 #[serde(rename = "createdAt")]
38 created_at: DateTime,
39}
40
41const COL_NAME: &'static str = "authorizationCode";
42
43impl Model {
44 pub async fn new(conn: Arc<Database>) -> Result<Self, Box<dyn StdError>> {
46 let model = Model { conn };
47 model.init().await?;
48 Ok(model)
49 }
50}
51
52#[async_trait]
53impl AuthorizationCodeModel for Model {
54 async fn init(&self) -> Result<(), Box<dyn StdError>> {
55 let indexes = vec![
56 doc! {"name": "code_1", "key": {"code": 1}, "unique": true},
57 doc! {"name": "clientId_1", "key": {"clientId": 1}},
58 doc! {"name": "userId_1", "key": {"userId": 1}},
59 doc! {"name": "ttl_1", "key": {"createdAt": 1}, "expireAfterSeconds": EXPIRES + 60},
60 ];
61 let command = doc! {
62 "createIndexes": COL_NAME,
63 "indexes": indexes,
64 };
65 self.conn.run_command(command).await?;
66 Ok(())
67 }
68
69 async fn get(&self, code: &str) -> Result<Option<AuthorizationCode>, Box<dyn StdError>> {
70 let mut cursor = self
71 .conn
72 .collection::<Schema>(COL_NAME)
73 .find(doc! {"code": code})
74 .await?;
75 if let Some(item) = cursor.try_next().await? {
76 return Ok(Some(AuthorizationCode {
77 code: item.code,
78 expires_at: item.expires_at.into(),
79 redirect_uri: item.redirect_uri,
80 scope: item.scope,
81 client_id: item.client_id,
82 user_id: item.user_id,
83 }));
84 }
85 Ok(None)
86 }
87
88 async fn add(&self, code: &AuthorizationCode) -> Result<(), Box<dyn StdError>> {
89 let item = Schema {
90 code: code.code.clone(),
91 expires_at: code.expires_at.into(),
92 redirect_uri: code.redirect_uri.clone(),
93 scope: code.scope.clone(),
94 client_id: code.client_id.clone(),
95 user_id: code.user_id.clone(),
96 created_at: match TimeDelta::try_seconds(EXPIRES) {
97 None => panic!("{}", E_UNKNOWN),
98 Some(t) => (code.expires_at - t).into(),
99 },
100 };
101 self.conn
102 .collection::<Schema>(COL_NAME)
103 .insert_one(item)
104 .await?;
105 Ok(())
106 }
107
108 async fn del(&self, cond: &QueryCond) -> Result<(), Box<dyn StdError>> {
109 let filter = get_query_filter(cond);
110 self.conn
111 .collection::<Schema>(COL_NAME)
112 .delete_many(filter)
113 .await?;
114 Ok(())
115 }
116}
117
118fn get_query_filter(cond: &QueryCond) -> Document {
120 let mut filter = Document::new();
121 if let Some(value) = cond.code {
122 filter.insert("code", value);
123 }
124 if let Some(value) = cond.client_id {
125 filter.insert("clientId", value);
126 }
127 if let Some(value) = cond.user_id {
128 filter.insert("userId", value);
129 }
130 filter
131}