1#[doc(hidden)]
2pub use base64;
3#[doc(hidden)]
4pub use chrono::{DateTime, Utc};
5#[doc(hidden)]
6pub use constant_time_eq;
7#[doc(hidden)]
8pub use rocket::{
9 async_trait,
10 http::Status,
11 data::{FromData, Outcome},
12 request::{self},
13};
14#[doc(hidden)]
15pub use rocket::figment::Figment;
16
17pub use serde::Deserialize;
18
19#[derive(Debug)]
21pub enum TokenError {
22 InvalidToken,
23 SignatureInvalid,
24 InvalidRequest,
25 ServerError,
26}
27
28#[async_trait::async_trait]
32pub trait AuthorizationTrait: Sync + Send + 'static {
33 async fn get_token_from_access_token(
35 access_token: String,
36 figment: Figment,
37 ) -> Result<crate::Token, TokenError>;
38
39 async fn get_user_from_token(
43 token: crate::Token,
44 figment: Figment,
45 ) -> Result<Box<Self>, TokenError>;
46}
47
48#[derive(Debug, Clone)]
49pub struct RequestData<T: AuthorizationTrait> {
50 pub identity: T,
51 pub data: String
52}
53
54impl<T: AuthorizationTrait + Clone> RequestData<T> {
55 pub fn get_identity(&self) -> T {
56 return self.identity.clone();
57 }
58
59 pub fn get_data<D: rocket::serde::DeserializeOwned>(&self) -> Result<crate::rocket::Json<D>, anyhow::Error> {
60 match crate::rocket::Json::<D>::from_str(self.data.clone().as_str()) {
61 Ok(s) => Ok(s),
62 Err(e) => Err(anyhow::anyhow!(e.to_string()))
63 }
64 }
65}
66
67use crate::Authorization;
68
69#[crate::rocket::async_trait]
79impl<'r, T: AuthorizationTrait> FromData<'r> for RequestData<T> {
80 type Error = TokenError;
81
82 async fn from_data(
83 req: &'r rocket::request::Request<'_>,
84 data: rocket::Data<'r>
85 ) -> rocket::data::Outcome<'r, Self> {
86 let _ = crate::rocket::parse_body::<serde_json::Value>(req, data).await;
87 let dbs = req.rocket().figment().focus("databases");
88
89 let body = req.local_cache(|| return "".to_string());
90
91 let header: String = match req.headers().get_one("Authorization") {
93 Some(h) => h.to_string(),
94 None => return Outcome::Error((Status::Unauthorized, TokenError::InvalidToken))
95 };
96
97 let params = match crate::Authorization::extract_params_from_header_string(header) {
98 Ok(params) => params,
99 Err(_) => return Outcome::Error((Status::Unauthorized, TokenError::InvalidToken))
100 };
101
102 match <T>::get_token_from_access_token(params.access_token, dbs.clone()).await {
103 Ok(token) => {
104 let date: crate::rocket::DateTime<Utc> = match params.date {
106 Some(date) => date,
107 None => {
108 let date: crate::rocket::DateTime<Utc> = match req.headers().get_one("X-Date") {
109 Some(h) => {
110 let date = crate::rocket::DateTime::parse_from_rfc2822(&h.to_string());
111 date.unwrap().with_timezone(&Utc)
112 },
113 None => {
114 return Outcome::Error((Status::Unauthorized, TokenError::InvalidToken));
115 }
116 };
117 date
118 }
119 };
120
121 let method = req.method().to_string();
122 let uri = req.uri().to_string();
123 let data = body.clone();
124 match Authorization::from(
125 method,
126 uri,
127 token.clone(),
128 date,
129 data.clone().to_owned(),
130 Some(params.salt),
131 params.version
132 ) {
133 Ok(auth) => {
134 if auth.verify(params.hmac, crate::rocket::NCRYPTF_DRIFT_ALLOWANCE) {
135 match <T>::get_user_from_token(token, dbs).await {
136 Ok(user) => return Outcome::Success(RequestData { identity: *user, data: data.clone() }),
137 Err(_) => return Outcome::Error((Status::Unauthorized, TokenError::InvalidToken))
138 };
139 }
140 },
141 Err(_) => return Outcome::Error((Status::Unauthorized, TokenError::InvalidToken))
142 };
143 },
144 Err(_) => return Outcome::Error((Status::Unauthorized, TokenError::InvalidToken))
145 };
146
147 return Outcome::Error((Status::Unauthorized, TokenError::InvalidToken))
148 }
149}
150
151#[macro_export]
178macro_rules! auth {
179 ($T: ty) => {
180 use $crate::rocket::TokenError;
181 use $crate::rocket::AuthorizationTrait;
182 use $crate::Authorization;
183 use rocket::request::{self, FromRequest, Outcome};
184
185 #[$crate::rocket::async_trait]
186 impl<'r> $crate::rocket::request::FromRequest<'r> for $T {
187 type Error = TokenError;
188
189 async fn from_request(req: &'r $crate::rocket::request::Request<'_>) -> rocket::request::Outcome<Self, TokenError> {
190 let dbs = req.rocket().figment().focus("databases");
191
192 let body = req.local_cache(|| return "".to_string());
193
194 let header: String = match req.headers().get_one("Authorization") {
196 Some(h) => h.to_string(),
197 None => return rocket::request::Outcome::Error(($crate::rocket::Status::Unauthorized, TokenError::InvalidToken))
198 };
199
200 let params = match $crate::Authorization::extract_params_from_header_string(header) {
201 Ok(params) => params,
202 Err(_) => return rocket::request::Outcome::Error(($crate::rocket::Status::Unauthorized, TokenError::InvalidToken))
203 };
204
205 match <$T>::get_token_from_access_token(params.access_token, dbs.clone()).await {
206 Ok(token) => {
207 let date: $crate::rocket::DateTime<$crate::rocket::Utc> = match params.date {
209 Some(date) => date,
210 None => {
211 let date: $crate::rocket::DateTime<$crate::rocket::Utc> = match req.headers().get_one("X-Date") {
212 Some(h) => {
213 let date = $crate::rocket::DateTime::parse_from_rfc2822(&h.to_string());
214 date.unwrap().with_timezone(&$crate::rocket::Utc)
215 },
216 None => {
217 return rocket::request::Outcome::Error(($crate::rocket::Status::Unauthorized, TokenError::InvalidToken));
218 }
219 };
220 date
221 }
222 };
223
224 let method = req.method().to_string();
225 let uri = req.uri().to_string();
226 let data = body.to_owned();
227 match $crate::Authorization::from(
228 method,
229 uri,
230 token.clone(),
231 date,
232 data,
233 Some(params.salt),
234 params.version
235 ) {
236 Ok(auth) => {
237 if auth.verify(params.hmac, $crate::rocket::NCRYPTF_DRIFT_ALLOWANCE) {
238 match <$T>::get_user_from_token(token, dbs).await {
239 Ok(user) => return rocket::request::Outcome::Success(*user),
240 Err(_) => return rocket::request::Outcome::Error(($crate::rocket::Status::Unauthorized, TokenError::InvalidToken))
241 };
242 }
243 },
244 Err(_) => return rocket::request::Outcome::Error(($crate::rocket::Status::Unauthorized, TokenError::InvalidToken))
245 };
246 },
247 Err(_) => return rocket::request::Outcome::Error(($crate::rocket::Status::Unauthorized, TokenError::InvalidToken))
248 };
249
250 return rocket::request::Outcome::Error(($crate::rocket::Status::Unauthorized, TokenError::InvalidToken))
251 }
252 }
253 }
254}