1
use std::error::Error as StdError;
2

            
3
use chrono::{TimeZone, Utc};
4
use redis::{aio::MultiplexedConnection, AsyncCommands};
5
use serde::{Deserialize, Serialize};
6
use serde_json;
7

            
8
use super::{
9
    super::authorization_code::{AuthorizationCode, QueryCond, EXPIRES},
10
    conn::{self, Options},
11
};
12

            
13
/// Model instance.
14
pub struct Model {
15
    /// The associated database connection.
16
    _conn: MultiplexedConnection,
17
}
18

            
19
/// Redis schema. Use JSON string as the value.
20
28
#[derive(Deserialize, Serialize)]
21
struct Schema {
22
    #[serde(rename = "code")]
23
    code: String,
24
    /// i64 as time tick from Epoch in milliseconds.
25
    #[serde(rename = "expiresAt")]
26
    expires_at: i64,
27
    #[serde(rename = "redirectUri")]
28
    redirect_uri: String,
29
    #[serde(rename = "scope")]
30
    scope: Option<String>,
31
    #[serde(rename = "clientId")]
32
    client_id: String,
33
    #[serde(rename = "userId")]
34
    user_id: String,
35
}
36

            
37
const PREFIX: &'static str = "auth:authorizationCode:";
38

            
39
impl Model {
40
    /// To create the model instance with a database connection.
41
    pub async fn new(opts: &Options) -> Result<Self, Box<dyn StdError>> {
42
        Ok(Model {
43
            _conn: conn::connect(opts).await?,
44
        })
45
    }
46
}
47

            
48
pub async fn init(_conn: &MultiplexedConnection) -> Result<(), Box<dyn StdError>> {
49
    Ok(())
50
}
51

            
52
5
pub async fn get(
53
5
    conn: &mut MultiplexedConnection,
54
5
    code: &str,
55
5
) -> Result<Option<AuthorizationCode>, Box<dyn StdError>> {
56
5
    let result: Option<String> = conn.get(PREFIX.to_string() + code).await?;
57
5
    let code_str = match result {
58
1
        None => return Ok(None),
59
4
        Some(code) => code,
60
    };
61
4
    let code: Schema = serde_json::from_str(code_str.as_str())?;
62
4
    Ok(Some(AuthorizationCode {
63
4
        code: code.code,
64
4
        expires_at: Utc.timestamp_nanos(code.expires_at * 1000000),
65
4
        redirect_uri: code.redirect_uri.clone(),
66
4
        scope: code.scope,
67
4
        client_id: code.client_id,
68
4
        user_id: code.user_id,
69
4
    }))
70
5
}
71

            
72
3
pub async fn add(
73
3
    conn: &mut MultiplexedConnection,
74
3
    code: &AuthorizationCode,
75
3
) -> Result<(), Box<dyn StdError>> {
76
3
    let code = Schema {
77
3
        code: code.code.to_string(),
78
3
        expires_at: code.expires_at.timestamp_millis(),
79
3
        redirect_uri: code.redirect_uri.clone(),
80
3
        scope: code.scope.clone(),
81
3
        client_id: code.client_id.to_string(),
82
3
        user_id: code.user_id.to_string(),
83
3
    };
84
3
    let item_str = serde_json::to_string(&code)?;
85
3
    let _: () = conn
86
3
        .set_ex(
87
3
            PREFIX.to_string() + code.code.as_str(),
88
3
            item_str,
89
3
            (EXPIRES + 60) as u64,
90
3
        )
91
3
        .await?;
92
3
    Ok(())
93
3
}
94

            
95
1
pub async fn del<'a>(
96
1
    conn: &mut MultiplexedConnection,
97
1
    cond: &QueryCond<'a>,
98
1
) -> Result<(), Box<dyn StdError>> {
99
1
    if cond.code.is_none() {
100
        return Ok(());
101
1
    }
102
1
    let _: () = conn.del(PREFIX.to_string() + cond.code.unwrap()).await?;
103
1
    Ok(())
104
1
}