Add API tokens support #9
| @@ -19,7 +19,7 @@ impl TokenID { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] | ||||
| #[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq)] | ||||
| pub struct TokenRight { | ||||
|     verb: TokenVerb, | ||||
|     path: String, | ||||
| @@ -28,6 +28,24 @@ pub struct TokenRight { | ||||
| #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] | ||||
| pub struct TokenRights(Vec<TokenRight>); | ||||
|  | ||||
| impl TokenRights { | ||||
|     pub fn check_error(&self) -> Option<&'static str> { | ||||
|         for r in &self.0 { | ||||
|             if !r.path.starts_with("/api/") { | ||||
|                 return Some("All API rights shall start with /api/"); | ||||
|             } | ||||
|         } | ||||
|         None | ||||
|     } | ||||
|  | ||||
|     pub fn contains(&self, verb: TokenVerb, path: &str) -> bool { | ||||
|         self.0.contains(&TokenRight { | ||||
|             verb, | ||||
|             path: path.to_string(), | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] | ||||
| pub struct Token { | ||||
|     pub id: TokenID, | ||||
| @@ -121,17 +139,6 @@ pub struct NewToken { | ||||
|     pub delete_after_inactivity: Option<u64>, | ||||
| } | ||||
|  | ||||
| impl TokenRights { | ||||
|     pub fn check_error(&self) -> Option<&'static str> { | ||||
|         for r in &self.0 { | ||||
|             if !r.path.starts_with("/api/") { | ||||
|                 return Some("All API rights shall start with /api/"); | ||||
|             } | ||||
|         } | ||||
|         None | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl NewToken { | ||||
|     /// Check for error in token | ||||
|     pub fn check_error(&self) -> Option<&'static str> { | ||||
|   | ||||
| @@ -4,7 +4,7 @@ use crate::api_tokens; | ||||
| use crate::utils::jwt_utils; | ||||
| use crate::utils::time_utils::time; | ||||
| use actix_web::dev::Payload; | ||||
| use actix_web::error::ErrorBadRequest; | ||||
| use actix_web::error::{ErrorBadRequest, ErrorUnauthorized}; | ||||
| use actix_web::{Error, FromRequest, HttpRequest}; | ||||
| use std::future::Future; | ||||
| use std::pin::Pin; | ||||
| @@ -108,7 +108,16 @@ impl FromRequest for ApiAuthExtractor { | ||||
|                 return Err(ErrorBadRequest("JWT method mismatch!")); | ||||
|             } | ||||
|  | ||||
|             // TODO : check if route is authorized with token | ||||
|             if !token.rights.contains(claims.verb, req.path()) { | ||||
|                 log::error!( | ||||
|                     "Attempt to use a token for an unauthorized route! (token_id={})", | ||||
|                     token.id.0 | ||||
|                 ); | ||||
|                 return Err(ErrorUnauthorized( | ||||
|                     "Token cannot be used to query this route!", | ||||
|                 )); | ||||
|             } | ||||
|  | ||||
|             // TODO : check for ip restriction | ||||
|  | ||||
|             // TODO : manually validate all checks | ||||
| @@ -116,7 +125,7 @@ impl FromRequest for ApiAuthExtractor { | ||||
|             if token.should_update_last_activity() { | ||||
|                 if let Err(e) = api_tokens::refresh_last_used(token.id).await { | ||||
|                     log::error!("Could not update token last activity! {e}"); | ||||
|                     return Err(ErrorBadRequest("!")); | ||||
|                     return Err(ErrorBadRequest("Couldn't refresh token last activity!")); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user