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

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

            
8
use super::{
9
    super::authorization_code::{AuthorizationCode, EXPIRES, QueryCond},
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
#[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
10
pub async fn get(
53
10
    conn: &mut MultiplexedConnection,
54
10
    code: &str,
55
10
) -> Result<Option<AuthorizationCode>, Box<dyn StdError>> {
56
10
    let result: Option<String> = conn.get(PREFIX.to_string() + code).await?;
57
10
    let code_str = match result {
58
2
        None => return Ok(None),
59
8
        Some(code) => code,
60
    };
61
8
    let code: Schema = serde_json::from_str(code_str.as_str())?;
62
8
    Ok(Some(AuthorizationCode {
63
8
        code: code.code,
64
8
        expires_at: Utc.timestamp_nanos(code.expires_at * 1000000),
65
8
        redirect_uri: code.redirect_uri.clone(),
66
8
        scope: code.scope,
67
8
        client_id: code.client_id,
68
8
        user_id: code.user_id,
69
8
    }))
70
10
}
71

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

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