webserver_rs/web_core/
authorization.rs

1use jsonwebtoken::{self, EncodingKey};
2use salvo::jwt_auth::{ConstDecoder, JwtTokenFinder};
3use salvo::prelude::*;
4use serde::de::DeserializeOwned;
5use serde::Serialize;
6
7use crate::web_core::http_error::AnyHttpError;
8use crate::HttpErrorKind;
9
10#[allow(dead_code)]
11/// Generate a jwt middleware with the provided parameters
12pub fn gen_jwt_auth<T: Send + Sync + DeserializeOwned + 'static>(
13    secret_key: String,
14    finders: Vec<Box<dyn JwtTokenFinder>>,
15) -> JwtAuth<T, ConstDecoder> {
16    JwtAuth::new(ConstDecoder::from_secret(secret_key.as_bytes()))
17        .finders(finders)
18        .force_passed(true)
19}
20
21#[allow(dead_code)]
22/// Generate a jwt with the provided parameters
23pub fn gen_token<T: Serialize + Send + Sync + 'static>(
24    secret_key: String,
25    claim: T,
26) -> Result<String, jsonwebtoken::errors::Error> {
27    jsonwebtoken::encode(
28        &jsonwebtoken::Header::default(),
29        &claim,
30        &EncodingKey::from_secret(secret_key.as_bytes()),
31    )
32}
33
34/// JWT Guard middleware that checks the validation of the request before entry of the handler
35pub struct AuthGuard<F: Fn(JwtAuthState) -> AnyHttpError + Send + Sync + 'static> {
36    f: F,
37}
38#[allow(dead_code)]
39impl<F> AuthGuard<F>
40where
41    F: Fn(JwtAuthState) -> AnyHttpError + Send + Sync + 'static,
42{
43    /// Construct the guard with a handler on how to respond to the clients when validation failed
44    pub fn new(f: F) -> Self {
45        Self { f }
46    }
47}
48#[async_trait]
49impl<F> Handler for AuthGuard<F>
50where
51    F: Fn(JwtAuthState) -> AnyHttpError + Send + Sync + 'static,
52{
53    async fn handle(
54        &self,
55        req: &mut Request,
56        depot: &mut Depot,
57        res: &mut Response,
58        ctrl: &mut FlowCtrl,
59    ) {
60        match depot.jwt_auth_state() {
61            JwtAuthState::Authorized => {
62                ctrl.call_next(req, depot, res).await;
63            }
64            JwtAuthState::Unauthorized => {
65                res.status_code(StatusCode::UNAUTHORIZED);
66                match (self.f)(JwtAuthState::Unauthorized).1 {
67                    HttpErrorKind::Html(v) => {
68                        res.render(Text::Html(v));
69                    }
70                    HttpErrorKind::Json(v) => {
71                        res.render(Text::Json(v.to_string()));
72                    }
73                };
74                ctrl.skip_rest();
75            }
76            JwtAuthState::Forbidden => {
77                res.status_code(StatusCode::FORBIDDEN);
78                match (self.f)(JwtAuthState::Forbidden).1 {
79                    HttpErrorKind::Html(v) => {
80                        res.render(Text::Html(v));
81                    }
82                    HttpErrorKind::Json(v) => {
83                        res.render(Text::Json(v.to_string()));
84                    }
85                };
86                ctrl.skip_rest();
87            }
88        };
89    }
90}
91
92/// Conveniently generate an expiration time for a JWT
93#[macro_export]
94macro_rules! expire_time {
95    (Days($day:expr)) => {{
96        use $crate::time::{Duration, OffsetDateTime};
97        let tmp = OffsetDateTime::now_utc() + Duration::days($day);
98        tmp.unix_timestamp()
99    }};
100    (Weeks($w:expr)) => {{
101        use $crate::time::{Duration, OffsetDateTime};
102        let tmp = OffsetDateTime::now_utc() + Duration::weeks($w);
103        tmp.unix_timestamp()
104    }};
105    (Hours($h:expr)) => {{
106        use $crate::time::{Duration, OffsetDateTime};
107        let tmp = OffsetDateTime::now_utc() + Duration::hours($h);
108        tmp.unix_timestamp()
109    }};
110    (Minutes($m:expr)) => {{
111        use $crate::time::{Duration, OffsetDateTime};
112        let tmp = OffsetDateTime::now_utc() + Duration::minutes($m);
113        tmp.unix_timestamp()
114    }};
115    (Seconds($s:expr)) => {{
116        use $crate::time::{Duration, OffsetDateTime};
117        let tmp = OffsetDateTime::now_utc() + Duration::seconds($s);
118        tmp.unix_timestamp()
119    }};
120}