1use redis::{Commands, Client, Connection};
3use serde::de::DeserializeOwned;
4use serde::{Deserialize, Serialize};
5use thiserror::Error;
6
7#[derive(Serialize, Deserialize, PartialEq, Clone, Debug)]
9pub struct TokenUserInfo {
10 pub created: u64,
11 pub devId: String,
12 pub distId: i32,
13 pub distributorType: i32,
14 pub email: String,
15 pub frozenAll: i32,
16 pub frozenAsset: i32,
17 pub frozenCfd: i32,
18 pub frozenOtc: i32,
19 pub frozenSpot: i32,
20 pub invite: String,
21 pub ip: u64,
22 pub isMarketMaker: Option<i32>,
23 pub mobile: String,
24 pub parentId: i64,
25 pub parentInvite: String,
26 pub partnerId: i64,
27 pub salesId: i32,
28 pub status: i32,
29 pub userId: i64,
30 pub username: Option<String>,
31 pub lastIp: Option<String>,
32}
33
34#[derive(Debug, Clone)]
35pub struct AuthConfig {
36 pub redis_url: String,
37 pub header_key: String,
38}
39
40impl Default for AuthConfig {
41 fn default() -> Self {
42 Self {
43 redis_url: "redis://127.0.0.1/".into(),
44 header_key: "X-Authorization".into(),
45 }
46 }
47}
48
49#[derive(Debug, Error)]
50pub enum AuthError {
51 #[error("Missing auth header")]
52 MissingHeader,
53 #[error("Redis connection error: {0}")]
54 ConnectionError(String),
55 #[error("Redis command error: {0}")]
56 CommandError(String),
57 #[error("Deserialization error: {0}")]
58 DeserializationError(String),
59}
60
61#[derive(Clone)]
62pub struct AuthClient {
63 pub client: Client,
65 pub config: AuthConfig,
66}
67
68impl AuthClient {
69 pub fn new(config: AuthConfig) -> Result<Self, AuthError> {
70 let client = Client::open(config.redis_url.clone())
71 .map_err(|e| AuthError::ConnectionError(e.to_string()))?;
72 Ok(Self { client, config })
73 }
74
75 fn get_conn(&self) -> Result<Connection, AuthError> {
76 self.client.get_connection()
77 .map_err(|e| AuthError::ConnectionError(e.to_string()))
78 }
79
80 pub fn get_user_data<T: DeserializeOwned>(
81 &self,
82 token: &str,
83 ) -> Result<T, AuthError> {
84 let mut conn = self.get_conn()?;
85
86 let redis_key1 = format!("kubiex:session:id:{}", token);
88 let user_id: Option<String> = conn.get(&redis_key1)
89 .map_err(|e| AuthError::CommandError(e.to_string()))?;
90
91 let data = if let Some(user_id) = user_id {
92 let redis_key2 = format!("kubiex:session:user_id:{}", user_id);
94 conn.get(&redis_key2)
95 .map_err(|e| AuthError::CommandError(e.to_string()))?
96 } else {
97 String::new()
99 };
100
101 serde_json::from_str(&data)
102 .map_err(|e| AuthError::DeserializationError(e.to_string()))
103 }
104 }
149
150
151
152
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158
159 #[tokio::test]
160 async fn test_full_flow() {
161 let config = AuthConfig {
162 redis_url: "redis://locklevel-redis-dev1-india.8wuaih.ng.0001.apse1.cache.amazonaws.com:6379/0".into(),
163 header_key: "X-Authorization".into(),
164 };
165
166 let client = AuthClient::new(config).unwrap();
167 let result: Result<TokenUserInfo, _> = client.get_user_data("f4a7afb9e538aa9e7f61646864e20466d5671680266603db6b70141821cf9067");
168 if let Ok(user_info) = result {
169 println!("Got user info: {:?}", user_info);
170 }
171 }
177}