rsiot/components/cmp_auth/
fn_process.rs

1use std::time::Duration;
2
3use tokio::time::sleep;
4use tracing::{info, warn};
5
6use hmac::{Hmac, Mac};
7use jwt::{SignWithKey, VerifyWithKey};
8use sha2::Sha256;
9
10use crate::{
11    executor::CmpInOut,
12    message::{system_messages::*, *},
13};
14
15use super::{token_payload::TokenPayload, Config, ConfigStore, ConfigStoreLocalItem, Error};
16
17pub async fn fn_process<TMsg>(config: Config, in_out: CmpInOut<TMsg>) -> super::Result<()>
18where
19    TMsg: MsgDataBound + 'static,
20{
21    loop {
22        let result = task_main(config.clone(), in_out.clone()).await;
23        warn!("Component error: {:?}", result);
24        info!("Restart");
25        sleep(Duration::from_secs(2)).await;
26    }
27}
28
29async fn task_main<TMsg>(config: Config, mut in_out: CmpInOut<TMsg>) -> super::Result<()>
30where
31    TMsg: MsgDataBound + 'static,
32{
33    while let Ok(msg) = in_out.recv_input().await {
34        let msg_response = match msg.data {
35            MsgData::System(data) => match data {
36                System::AuthRequestByLogin(value) => {
37                    process_request_by_login(value, &config, msg.trace.clone()).await
38                }
39                System::AuthRequestByToken(value) => {
40                    process_request_by_token(value, &config, msg.trace.clone()).await
41                }
42                _ => continue,
43            },
44            _ => continue,
45        };
46        let msg = match msg_response {
47            Ok(msg) => {
48                info!("Success login: {:?}", msg);
49                msg
50            }
51            Err(err) => {
52                warn!("Wrong login attempt: {}", err);
53                let trace_ids = msg.trace.get_ids();
54                let value = AuthResponseErr {
55                    error: err.to_string(),
56                    trace_ids,
57                };
58                Message::new(MsgData::System(System::AuthResponseErr(value)))
59            }
60        };
61        in_out.send_output(msg).await.map_err(Error::CmpOutput)?;
62    }
63    Ok(())
64}
65
66/// Обработка запроса по токену
67async fn process_request_by_token<TMsg>(
68    request_by_login: AuthRequestByToken,
69    config: &Config,
70    msg_trace: MsgTrace,
71) -> super::Result<Message<TMsg>>
72where
73    TMsg: MsgDataBound,
74{
75    let key: Hmac<Sha256> = Hmac::new_from_slice(config.secret_key.as_bytes())?;
76    let claims: TokenPayload = request_by_login.token.verify_with_key(&key)?;
77
78    let trace_ids = msg_trace.get_ids();
79    let value = AuthResponseOk {
80        token: request_by_login.token,
81        perm: claims.role,
82        trace_ids,
83        login: claims.login,
84    };
85    let msg = Message::new(MsgData::System(System::AuthResponseOk(value)));
86    Ok(msg)
87}
88
89/// Обработка запроса по логину-паролю
90async fn process_request_by_login<TMsg>(
91    request_by_login: AuthRequestByLogin,
92    config: &Config,
93    msg_trace: MsgTrace,
94) -> super::Result<Message<TMsg>>
95where
96    TMsg: MsgDataBound,
97{
98    info!("Request: {request_by_login:?}");
99    let valid_password = get_credentials(&request_by_login.login, config).await?;
100
101    // Пользователь не найден
102    let valid_password = valid_password.ok_or(Error::ProcessRequest("Unknown user".into()))?;
103
104    // Пароль не подходит
105    if valid_password.password != request_by_login.password {
106        return Err(Error::ProcessRequest("Wrong password".into()));
107    }
108
109    // Генерируем jwt
110    let key: Hmac<Sha256> = Hmac::new_from_slice(config.secret_key.as_bytes())?;
111    let claims = TokenPayload {
112        login: request_by_login.login.clone(),
113        role: valid_password.role,
114    };
115    let token = claims.sign_with_key(&key)?;
116
117    let trace_ids = msg_trace.get_ids();
118
119    let value = AuthResponseOk {
120        token,
121        perm: valid_password.role,
122        trace_ids,
123        login: request_by_login.login,
124    };
125    let msg = Message::new(MsgData::System(System::AuthResponseOk(value)));
126
127    Ok(msg)
128}
129
130async fn get_credentials(
131    login: &str,
132    config: &Config,
133) -> super::Result<Option<ConfigStoreLocalItem>> {
134    match &config.store {
135        ConfigStore::Local(local) => {
136            let item = local.iter().find(|e| e.login == login).cloned();
137            Ok(item)
138        }
139
140        ConfigStore::Surrealdb => todo!(),
141    }
142}