From c5eed34237ba52ca807a40cb60b887b9c78eca3c Mon Sep 17 00:00:00 2001 From: nm17 Date: Mon, 24 Feb 2025 20:06:54 +0400 Subject: [PATCH] feat(old_app_api): proper api key handling --- src/web_server/old_app_api/mod.rs | 31 ++++++++++++++++++++----------- src/web_server/utils/redis.rs | 22 +++++++++++++--------- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/web_server/old_app_api/mod.rs b/src/web_server/old_app_api/mod.rs index 113d7dd..55136a8 100644 --- a/src/web_server/old_app_api/mod.rs +++ b/src/web_server/old_app_api/mod.rs @@ -6,21 +6,15 @@ mod types; use ntex::web::types::State; use ntex::util::Bytes; use ntex::web; -use nom::AsBytes; -use snafu::ResultExt; use crate::web_server::app_error::AppError; use crate::web_server::NMAppState; use crate::web_server::old_app_api::handlers::{app_init, version}; use crate::web_server::old_app_api::types::{AppInitRequest, MandatoryParams}; use crate::web_server::utils::redis::is_api_key_valid; -use crate::web_server::NMAppState; -use ntex::http::HeaderMap; -use ntex::util::Bytes; -use ntex::web::types::State; -use ntex::web::{self, HttpRequest}; +use ntex::web::HttpRequest; use snafu::{whatever, ResultExt}; -use super::app_error::{self, Utf8Snafu}; +use super::app_error; /// Обработчик запросов от приложений. /// @@ -28,9 +22,12 @@ use super::app_error::{self, Utf8Snafu}; /// /// Вызывается напрямую из ntex приложения. pub async fn old_api_handler( + request: HttpRequest, app_state: State, body_bytes: Bytes, ) -> Result { + let headers = request.headers(); + if body_bytes.len() > 10 * 1024 { // 10 KiB return Err(AppError::RequestTooLarge); @@ -39,10 +36,22 @@ pub async fn old_api_handler( let mandatory_params: MandatoryParams = serde_json::from_slice(&body_bytes).context(app_error::JsonSnafu {})?; // TODO: Simd-JSON - // Ignore clippy singlematch - if mandatory_params.cmd.as_ref() == "version" { return version((), &app_state).await } + // Тут все cmd которые могут быть вызваны без api ключа + if mandatory_params.cmd == "version" { + return version((), &app_state).await; + } - is_api_key_valid(&app_state.redis_client, mandatory_params.api_key.as_ref()).await?; + let api_key: String; + + if let Some(key) = mandatory_params.api_key { + api_key = key; + } else if let Some(key) = headers.get("Narodmon-Api-Key") { + api_key = key.to_str().with_whatever_context(|_| "asd")?.to_string(); + } else { + whatever!("No API key found") + } + + is_api_key_valid(&app_state.redis_client, api_key).await?; match mandatory_params.cmd.as_str() { "appInit" => { diff --git a/src/web_server/utils/redis.rs b/src/web_server/utils/redis.rs index 124e3cd..bf3f19d 100644 --- a/src/web_server/utils/redis.rs +++ b/src/web_server/utils/redis.rs @@ -1,18 +1,22 @@ //! Сборник утилит для работы с Redis. -use crate::web_server::app_error::{AppError, ServerRedisSnafu}; -use fred::prelude::*; +use crate::{ + uformat, + web_server::app_error::{AppError, ServerRedisSnafu}, +}; use fred::clients::Client as RedisClient; +use fred::prelude::*; use heapless::String as HeaplessString; use lazy_static::lazy_static; use regex::Regex; use serde::{Deserialize, Serialize}; use snafu::ResultExt; use ufmt::uwrite; +use uuid::Uuid; lazy_static! { /// Разрешённые знаки для API ключа. - static ref ALLOWED_API_KEY_CHARACTERS: Regex = Regex::new("[a-zA-Z0-9]{13}").unwrap(); + static ref ALLOWED_API_KEY_CHARACTERS: Regex = Regex::new("[a-zA-Z0-9\\-]{13,36}").unwrap(); } /// Описание полей в KV DB у `apikey_{}`. @@ -25,18 +29,18 @@ pub struct ApiKeyDescription { /// Проверка API ключа на валидность. pub async fn is_api_key_valid( client: &RedisClient, - api_key: &str, + api_key: String, ) -> Result { - if !ALLOWED_API_KEY_CHARACTERS.is_match(api_key) { + if !ALLOWED_API_KEY_CHARACTERS.is_match(&api_key) { return Err(AppError::ApiKeyInvalid { reason: "Invalid characters present in the API key.", }); } - let mut key_buffer = HeaplessString::<{ 7 + 13 }>::new(); - uwrite!(key_buffer, "apikey_{}", api_key).expect("TODO"); // TODO: Error handling - - let valid: Option = client.hget(key_buffer.as_str(), "owner").await.context(ServerRedisSnafu)?; + let valid: Option = client + .hget(uformat!("apikey_{}", api_key), "owner") + .await + .context(ServerRedisSnafu)?; valid .map(|uid| ApiKeyDescription { apikey_owner: uid })