From 5fad9d93c22f597b56762d11e898fed520e35aff Mon Sep 17 00:00:00 2001 From: petru Date: Fri, 8 Mar 2024 08:57:39 +0200 Subject: [PATCH 01/13] 30-access-polices working on the general JsonConfig --- src/startup.rs | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/startup.rs b/src/startup.rs index b8052662..1047bea2 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -4,8 +4,13 @@ use crate::routes; use actix_cors::Cors; use actix_web::dev::Server; use actix_web::{ - web::{self}, - App, HttpServer, + error, + web, + App, + HttpServer, + HttpResponse, + FromRequest, + rt, }; use crate::middleware; use sqlx::{Pool, Postgres}; @@ -24,6 +29,26 @@ pub async fn run( let mq_manager = web::Data::new(mq_manager); let authorization = middleware::authorization::try_new(settings.database.connection_string()).await?; + let json_cfg = web::JsonConfig::default() + .error_handler(|err, req| { + let bytes = web::Bytes::from_request(req, &mut actix_web::dev::Payload::None); + let rt = rt::Runtime::new().unwrap(); + let handle = rt.spawn(async move { + let bytes: web::Bytes = bytes.await.unwrap(); + }); + + rt.block_on(handle).unwrap(); //todo 1. get the result of bytes. + //todo 2. transform line, column into index + //todo 3. transform index in the start of json till the + //error occured + + match err { + error::JsonPayloadError::Deserialize(ref err) => println!("deserialize {err:?} {err}"), + _ => println!("sth else {err:?}") + } + + error::InternalError::from_response(err, HttpResponse::Conflict().into()).into() + }); let server = HttpServer::new(move || { App::new() @@ -74,6 +99,7 @@ pub async fn run( .service(routes::stack::get::admin_item) .service(routes::stack::get::admin_list) ) + .app_data(json_cfg.clone()) .app_data(pg_pool.clone()) .app_data(mq_manager.clone()) .app_data(settings.clone()) From 810bb6a0b4492122624e6969632393c9d995d4aa Mon Sep 17 00:00:00 2001 From: petru Date: Sun, 10 Mar 2024 02:10:32 +0200 Subject: [PATCH 02/13] 30-access-polices json errors --- src/routes/test/casbin.rs | 8 ----- src/routes/test/json_explain.rs | 37 +++++++++++++++++++++++ src/routes/test/mod.rs | 2 +- src/startup.rs | 53 +++++++++++++++++---------------- 4 files changed, 65 insertions(+), 35 deletions(-) delete mode 100644 src/routes/test/casbin.rs create mode 100644 src/routes/test/json_explain.rs diff --git a/src/routes/test/casbin.rs b/src/routes/test/casbin.rs deleted file mode 100644 index 77e1ec59..00000000 --- a/src/routes/test/casbin.rs +++ /dev/null @@ -1,8 +0,0 @@ -use actix_web::{get, Responder, Result}; -use crate::helpers::JsonResponse; - -#[tracing::instrument(name = "Test casbin.")] -#[get("")] -pub async fn handler() -> Result { - Ok(JsonResponse::::build().ok("success")) -} diff --git a/src/routes/test/json_explain.rs b/src/routes/test/json_explain.rs new file mode 100644 index 00000000..734a59a8 --- /dev/null +++ b/src/routes/test/json_explain.rs @@ -0,0 +1,37 @@ +use actix_web::{post, web, HttpResponse, Result, http::header::ContentType}; + +#[tracing::instrument(name = "Json explain")] +#[post("/json/explain/{line}/{column}")] +pub async fn handler(path: web::Path<(usize, usize,)>, body: web::Bytes) -> HttpResponse { + let line = path.0; + let column = path.1; + + let index = line_column_to_index(body.as_ref(), line, column); + let body = String::from_utf8(body.as_ref()[..index].to_vec()).unwrap(); //todo unwrap + + HttpResponse::Ok() + .content_type(ContentType::plaintext()) + .body(body) +} + +fn line_column_to_index(u8slice: &[u8], line: usize, column: usize) -> usize { + let mut l = 1; + let mut c = 0; + let mut i = 0; + for ch in u8slice { + i += 1; + match ch { + b'\n' => { + l += 1; + c = 0; + } + _ => { + c += 1; + } + } + if line == l && c == column { + break; + } + } + return i; +} diff --git a/src/routes/test/mod.rs b/src/routes/test/mod.rs index 2b55b427..9c490669 100644 --- a/src/routes/test/mod.rs +++ b/src/routes/test/mod.rs @@ -1,2 +1,2 @@ pub mod deploy; -pub mod casbin; +pub mod json_explain; diff --git a/src/startup.rs b/src/startup.rs index 1047bea2..37892a3e 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -29,27 +29,6 @@ pub async fn run( let mq_manager = web::Data::new(mq_manager); let authorization = middleware::authorization::try_new(settings.database.connection_string()).await?; - let json_cfg = web::JsonConfig::default() - .error_handler(|err, req| { - let bytes = web::Bytes::from_request(req, &mut actix_web::dev::Payload::None); - let rt = rt::Runtime::new().unwrap(); - let handle = rt.spawn(async move { - let bytes: web::Bytes = bytes.await.unwrap(); - }); - - rt.block_on(handle).unwrap(); //todo 1. get the result of bytes. - //todo 2. transform line, column into index - //todo 3. transform index in the start of json till the - //error occured - - match err { - error::JsonPayloadError::Deserialize(ref err) => println!("deserialize {err:?} {err}"), - _ => println!("sth else {err:?}") - } - - error::InternalError::from_response(err, HttpResponse::Conflict().into()).into() - }); - let server = HttpServer::new(move || { App::new() .wrap(TracingLogger::default()) @@ -73,10 +52,9 @@ pub async fn run( .service(routes::client::admin_disable_handler), ) .service( - web::scope("/test").service(routes::test::deploy::handler), - ) - .service( - web::scope("/pen/1").service(routes::test::casbin::handler), + web::scope("/test") + .service(routes::test::deploy::handler) + .service(routes::test::json_explain::handler), ) .service( web::scope("/rating") @@ -99,7 +77,6 @@ pub async fn run( .service(routes::stack::get::admin_item) .service(routes::stack::get::admin_list) ) - .app_data(json_cfg.clone()) .app_data(pg_pool.clone()) .app_data(mq_manager.clone()) .app_data(settings.clone()) @@ -109,3 +86,27 @@ pub async fn run( Ok(server) } + +fn line_column_to_index(u8slice: &[u8], line: usize, column: usize) -> usize { + let mut l = 1; + let mut c = 0; + let mut i = 0; + for ch in u8slice { + i += 1; + match ch { + b'\n' => { + l += 1; + c = 0; + } + _ => { + c += 1; + } + } + + if line == l && c == column { + break; + } + } + + return i; +} From a83071a7292816a9843cbf772a44cb13ba937017 Mon Sep 17 00:00:00 2001 From: petru Date: Mon, 11 Mar 2024 16:56:22 +0200 Subject: [PATCH 03/13] 30-access-policies json message for errors --- src/startup.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/startup.rs b/src/startup.rs index 37892a3e..2240565b 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -2,8 +2,9 @@ use crate::configuration::Settings; use crate::helpers; use crate::routes; use actix_cors::Cors; -use actix_web::dev::Server; use actix_web::{ + dev::Server, + http, error, web, App, @@ -29,6 +30,14 @@ pub async fn run( let mq_manager = web::Data::new(mq_manager); let authorization = middleware::authorization::try_new(settings.database.connection_string()).await?; + let json_config = web::JsonConfig::default() + .error_handler(|err, req| { + let msg: String = match err { + error::JsonPayloadError::Deserialize(err) => format!("{{\"kind\":\"deserialize\",\"line\":{}, \"column\":{}, \"msg\":\"{}\"}}", err.line(), err.column(), err), + _ => format!("{{\"kind\":\"other\",\"msg\":\"{}\"}}", err) + }; + error::InternalError::new(msg, http::StatusCode::BAD_REQUEST).into() + }); let server = HttpServer::new(move || { App::new() .wrap(TracingLogger::default()) @@ -77,6 +86,7 @@ pub async fn run( .service(routes::stack::get::admin_item) .service(routes::stack::get::admin_list) ) + .app_data(json_config.clone()) .app_data(pg_pool.clone()) .app_data(mq_manager.clone()) .app_data(settings.clone()) From b402c83e9bb7c7faaea976ed9aaebf19774662af Mon Sep 17 00:00:00 2001 From: petru Date: Mon, 11 Mar 2024 17:22:02 +0200 Subject: [PATCH 04/13] 30-access-policies json debug command. sketch --- src/console/commands/debug/json.rs | 23 +++++++++++++++++++++++ src/console/commands/debug/mod.rs | 3 +++ src/console/commands/mod.rs | 1 + src/console/main.rs | 22 ++++++++++++++++++++-- 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 src/console/commands/debug/json.rs create mode 100644 src/console/commands/debug/mod.rs diff --git a/src/console/commands/debug/json.rs b/src/console/commands/debug/json.rs new file mode 100644 index 00000000..63d90f53 --- /dev/null +++ b/src/console/commands/debug/json.rs @@ -0,0 +1,23 @@ +use crate::configuration::get_configuration; +use actix_web::rt; +use actix_web::web; +use sqlx::PgPool; + +pub struct JsonCommand { + line: usize, + column: usize, + payload: String +} + +impl JsonCommand { + pub fn new(line: usize, column: usize, payload: String) -> Self { + Self { line, column, payload } + } +} + +impl crate::console::commands::CallableTrait for JsonCommand { + fn call(&self) -> Result<(), Box> { + println!("line={} column={} payload={}", self.line, self.column, self.payload); + Ok(()) + } +} diff --git a/src/console/commands/debug/mod.rs b/src/console/commands/debug/mod.rs new file mode 100644 index 00000000..5f50076c --- /dev/null +++ b/src/console/commands/debug/mod.rs @@ -0,0 +1,3 @@ +mod json; + +pub use json::*; diff --git a/src/console/commands/mod.rs b/src/console/commands/mod.rs index 2cf75be7..0b8d17a5 100644 --- a/src/console/commands/mod.rs +++ b/src/console/commands/mod.rs @@ -1,4 +1,5 @@ pub mod appclient; +pub mod debug; mod callable; pub use callable::*; diff --git a/src/console/main.rs b/src/console/main.rs index 09211286..5d5b168d 100644 --- a/src/console/main.rs +++ b/src/console/main.rs @@ -12,6 +12,10 @@ enum Commands { #[command(subcommand)] command: AppClientCommands, }, + Debug { + #[command(subcommand)] + command: DebugCommands, + }, } #[derive(Debug, Subcommand)] @@ -22,8 +26,17 @@ enum AppClientCommands { }, } -//todo add documentation about how to add a new command -//todo the helper from console should have a nicer display +#[derive(Debug, Subcommand)] +enum DebugCommands { + Json { + #[arg(long)] + line: usize, + #[arg(long)] + column: usize, + #[arg(long)] + payload: String, + }, +} fn main() -> Result<(), Box> { let cli = Cli::parse(); @@ -38,6 +51,11 @@ fn get_command(cli: Cli) -> Result match command { + DebugCommands::Json { line, column, payload } => Ok(Box::new( + stacker::console::commands::debug::JsonCommand::new(line, column, payload), + )), + }, _ => Err("command does not match".to_string()), } } From d708f82787398988755d13c49a3b13786ee6ffcc Mon Sep 17 00:00:00 2001 From: petru Date: Wed, 13 Mar 2024 16:50:37 +0200 Subject: [PATCH 05/13] 30-access-policies read payload from file --- src/console/commands/debug/json.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/console/commands/debug/json.rs b/src/console/commands/debug/json.rs index 63d90f53..544b1b5f 100644 --- a/src/console/commands/debug/json.rs +++ b/src/console/commands/debug/json.rs @@ -17,7 +17,8 @@ impl JsonCommand { impl crate::console::commands::CallableTrait for JsonCommand { fn call(&self) -> Result<(), Box> { - println!("line={} column={} payload={}", self.line, self.column, self.payload); + let payload = std::fs::read_to_string(&self.payload)?; + println!("line={} column={} payload={}", self.line, self.column, payload); Ok(()) } } From fd47bfc7a292e908046386038b43a5653bd9ce3e Mon Sep 17 00:00:00 2001 From: petru Date: Fri, 15 Mar 2024 16:56:31 +0200 Subject: [PATCH 06/13] 30-access-policies - json debug command --- src/console/commands/debug/json.rs | 33 ++++++++++++++++++++++---- src/routes/test/json_explain.rs | 37 ------------------------------ src/routes/test/mod.rs | 1 - src/startup.rs | 1 - 4 files changed, 28 insertions(+), 44 deletions(-) delete mode 100644 src/routes/test/json_explain.rs diff --git a/src/console/commands/debug/json.rs b/src/console/commands/debug/json.rs index 544b1b5f..047faf0f 100644 --- a/src/console/commands/debug/json.rs +++ b/src/console/commands/debug/json.rs @@ -1,7 +1,5 @@ use crate::configuration::get_configuration; -use actix_web::rt; -use actix_web::web; -use sqlx::PgPool; +use actix_web::{rt, post, web, HttpResponse, Result, http::header::ContentType}; pub struct JsonCommand { line: usize, @@ -17,8 +15,33 @@ impl JsonCommand { impl crate::console::commands::CallableTrait for JsonCommand { fn call(&self) -> Result<(), Box> { - let payload = std::fs::read_to_string(&self.payload)?; - println!("line={} column={} payload={}", self.line, self.column, payload); + let payload: String = std::fs::read_to_string(&self.payload)?; + let index = line_column_to_index(payload.as_ref(), self.line, self.column); + let prefix = String::from_utf8(>::as_ref(&payload)[..index].to_vec()).unwrap(); + + println!("{}", prefix); Ok(()) } } + +fn line_column_to_index(u8slice: &[u8], line: usize, column: usize) -> usize { + let mut l = 1; + let mut c = 0; + let mut i = 0; + for ch in u8slice { + i += 1; + match ch { + b'\n' => { + l += 1; + c = 0; + } + _ => { + c += 1; + } + } + if line == l && c == column { + break; + } + } + return i; +} diff --git a/src/routes/test/json_explain.rs b/src/routes/test/json_explain.rs deleted file mode 100644 index 734a59a8..00000000 --- a/src/routes/test/json_explain.rs +++ /dev/null @@ -1,37 +0,0 @@ -use actix_web::{post, web, HttpResponse, Result, http::header::ContentType}; - -#[tracing::instrument(name = "Json explain")] -#[post("/json/explain/{line}/{column}")] -pub async fn handler(path: web::Path<(usize, usize,)>, body: web::Bytes) -> HttpResponse { - let line = path.0; - let column = path.1; - - let index = line_column_to_index(body.as_ref(), line, column); - let body = String::from_utf8(body.as_ref()[..index].to_vec()).unwrap(); //todo unwrap - - HttpResponse::Ok() - .content_type(ContentType::plaintext()) - .body(body) -} - -fn line_column_to_index(u8slice: &[u8], line: usize, column: usize) -> usize { - let mut l = 1; - let mut c = 0; - let mut i = 0; - for ch in u8slice { - i += 1; - match ch { - b'\n' => { - l += 1; - c = 0; - } - _ => { - c += 1; - } - } - if line == l && c == column { - break; - } - } - return i; -} diff --git a/src/routes/test/mod.rs b/src/routes/test/mod.rs index 9c490669..40149b14 100644 --- a/src/routes/test/mod.rs +++ b/src/routes/test/mod.rs @@ -1,2 +1 @@ pub mod deploy; -pub mod json_explain; diff --git a/src/startup.rs b/src/startup.rs index 2240565b..46d0b5b6 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -63,7 +63,6 @@ pub async fn run( .service( web::scope("/test") .service(routes::test::deploy::handler) - .service(routes::test::json_explain::handler), ) .service( web::scope("/rating") From d68bc5efa552cfc76d1d36ffceccb269c5adfdc4 Mon Sep 17 00:00:00 2001 From: petru Date: Thu, 21 Mar 2024 17:23:19 +0200 Subject: [PATCH 07/13] 30-access-policies --- src/routes/rating/get.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/routes/rating/get.rs b/src/routes/rating/get.rs index 34ef6afd..7071dcc8 100644 --- a/src/routes/rating/get.rs +++ b/src/routes/rating/get.rs @@ -5,11 +5,6 @@ use actix_web::{get, web, Responder, Result}; use sqlx::PgPool; use tracing::Instrument; -// workflow -// add, update, list, get(user_id), ACL, -// ACL - access to func for a user -// ACL - access to objects for a user - #[tracing::instrument(name = "Get rating.")] #[get("/{id}")] pub async fn get_handler( From 70cbfafe4a03cde5f53937b1306af52bf9b5e7ee Mon Sep 17 00:00:00 2001 From: petru Date: Fri, 22 Mar 2024 22:34:40 +0200 Subject: [PATCH 08/13] 30-access-policies --- src/forms/project/form.rs | 23 ----------------------- src/routes/project/add.rs | 28 ++++++++++++++++++++-------- src/routes/project/update.rs | 2 ++ src/startup.rs | 4 ++-- 4 files changed, 24 insertions(+), 33 deletions(-) diff --git a/src/forms/project/form.rs b/src/forms/project/form.rs index d2498cf4..7d3a3a65 100644 --- a/src/forms/project/form.rs +++ b/src/forms/project/form.rs @@ -52,26 +52,3 @@ impl ProjectForm { Ok(is_active) } } - -pub(crate) async fn body_into_form(body: Bytes) -> actix_web::Result { - let body_bytes = actix_web::body::to_bytes(body).await.unwrap(); - let body_str = str::from_utf8(&body_bytes) - .map_err(|err| JsonResponse::::build().internal_server_error(err.to_string()))?; - let deserializer = &mut serde_json::Deserializer::from_str(body_str); - serde_path_to_error::deserialize(deserializer) - .map_err(|err| { - let msg = format!("{}:{:?}", err.path().to_string(), err); - JsonResponse::::build().bad_request(msg) - }) - .and_then(|mut form: forms::project::ProjectForm| { - if !form.validate().is_ok() { - let errors = form.validate().unwrap_err().to_string(); - let err_msg = format!("Invalid data received {:?}", &errors); - tracing::debug!(err_msg); - - return Err(JsonResponse::::build().form_error(errors)); - } - - Ok(form) - }) -} diff --git a/src/routes/project/add.rs b/src/routes/project/add.rs index e1b84bc9..ee8c67f7 100644 --- a/src/routes/project/add.rs +++ b/src/routes/project/add.rs @@ -12,24 +12,37 @@ use sqlx::PgPool; use std::sync::Arc; use std::str::FromStr; use std::str; +use serde_valid::Validate; #[tracing::instrument(name = "Add project.")] #[post("")] pub async fn item( - body: Bytes, + request_json: web::Json, user: web::ReqData>, pg_pool: Data, ) -> Result { // @todo ACL - let form = forms::project::form::body_into_form(body.clone()).await?; + let request_json = request_json.into_inner(); //todo + let form = serde_json::from_value(request_json.clone()) + .map_err(|err| { + let msg = format!("{}", err); //todo JsonReponse::BadRequest::from(err) + JsonResponse::::build().bad_request(msg) + }) + .and_then(|mut form: forms::project::ProjectForm| { + if !form.validate().is_ok() { + let errors = form.validate().unwrap_err().to_string(); + let err_msg = format!("Invalid data received {:?}", &errors); + tracing::debug!(err_msg); + + return Err(JsonResponse::::build().form_error(errors)); + } + + Ok(form) + })?; + let project_name = form.custom.custom_stack_code.clone(); - let body_bytes = actix_web::body::to_bytes(body).await.unwrap(); - let body_str = str::from_utf8(&body_bytes) - .map_err(|err| JsonResponse::::build().internal_server_error(err.to_string()))?; - let request_json = Value::from_str(body_str).unwrap(); tracing::debug!("Request json: {:?}", request_json); - let body: Value = serde_json::to_value::(form) .or(serde_json::to_value::(forms::project::ProjectForm::default())) .unwrap(); @@ -48,4 +61,3 @@ pub async fn item( JsonResponse::::build().internal_server_error("Internal Server Error") }) } - diff --git a/src/routes/project/update.rs b/src/routes/project/update.rs index ecd72b10..bfd82b66 100644 --- a/src/routes/project/update.rs +++ b/src/routes/project/update.rs @@ -12,6 +12,7 @@ use actix_web::web::Bytes; use tracing::Instrument; use std::str; +/* #[tracing::instrument(name = "Update project.")] #[put("/{id}")] pub async fn item( @@ -74,3 +75,4 @@ pub async fn item( JsonResponse::::build().internal_server_error("") }) } +*/ diff --git a/src/startup.rs b/src/startup.rs index 72d66c6f..9c31afd4 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -31,7 +31,7 @@ pub async fn run( let authorization = middleware::authorization::try_new(settings.database.connection_string()).await?; let json_config = web::JsonConfig::default() - .error_handler(|err, req| { + .error_handler(|err, req| { //todo let msg: String = match err { error::JsonPayloadError::Deserialize(err) => format!("{{\"kind\":\"deserialize\",\"line\":{}, \"column\":{}, \"msg\":\"{}\"}}", err.line(), err.column(), err), _ => format!("{{\"kind\":\"other\",\"msg\":\"{}\"}}", err) @@ -78,7 +78,7 @@ pub async fn run( .service(crate::routes::project::get::item) .service(crate::routes::project::get::list) .service(crate::routes::project::add::item) - .service(crate::routes::project::update::item) + //.service(crate::routes::project::update::item) todo .service(crate::routes::project::delete::item), ) .service( From db44167062993dad3840d0f8eb504385b22afe34 Mon Sep 17 00:00:00 2001 From: petru Date: Sat, 23 Mar 2024 16:24:43 +0200 Subject: [PATCH 09/13] 30-access-policies project add --- src/helpers/json.rs | 22 +++++++++++++++------- src/routes/project/add.rs | 29 +++++++---------------------- 2 files changed, 22 insertions(+), 29 deletions(-) diff --git a/src/helpers/json.rs b/src/helpers/json.rs index ee209c10..ac77cbe9 100644 --- a/src/helpers/json.rs +++ b/src/helpers/json.rs @@ -1,7 +1,9 @@ use actix_web::error::{ErrorBadRequest, ErrorConflict, ErrorInternalServerError, ErrorNotFound, ErrorUnauthorized}; use actix_web::web::Json; -use actix_web::Error; +use actix_web::Error as ActixError; use serde_derive::Serialize; +use std::error::Error as StdError; +use std::convert::From; #[derive(Serialize)] pub(crate) struct JsonResponse { @@ -70,33 +72,33 @@ where pub(crate) fn bad_request>( self, msg: I, - ) -> Error { + ) -> ActixError { ErrorBadRequest(self.set_msg(msg).to_string()) } - pub(crate) fn form_error(self, msg: String) -> Error { + pub(crate) fn form_error(self, msg: String) -> ActixError { ErrorBadRequest(msg) } - pub(crate) fn not_found>(self, msg: I) -> Error { + pub(crate) fn not_found>(self, msg: I) -> ActixError { ErrorNotFound(self.set_msg(msg).to_string()) } pub(crate) fn internal_server_error>( self, msg: I, - ) -> Error { + ) -> ActixError { ErrorInternalServerError(self.set_msg(msg).to_string()) } pub(crate) fn unauthorized>( self, msg: I, - ) -> Error { + ) -> ActixError { ErrorUnauthorized(self.set_msg(msg).to_string()) } - pub(crate) fn conflict>(self, msg: I) -> Error { + pub(crate) fn conflict>(self, msg: I) -> ActixError { ErrorConflict(self.set_msg(msg).to_string()) } } @@ -109,3 +111,9 @@ where JsonResponseBuilder::default() } } + +impl JsonResponse { + pub fn err_bad_request(e: E) -> ActixError { + JsonResponse::::build().bad_request( e.to_string()) + } +} diff --git a/src/routes/project/add.rs b/src/routes/project/add.rs index ee8c67f7..892d31aa 100644 --- a/src/routes/project/add.rs +++ b/src/routes/project/add.rs @@ -10,39 +10,24 @@ use actix_web::{ use serde_json::Value; use sqlx::PgPool; use std::sync::Arc; -use std::str::FromStr; -use std::str; use serde_valid::Validate; #[tracing::instrument(name = "Add project.")] #[post("")] pub async fn item( - request_json: web::Json, + web::Json(request_json): web::Json, user: web::ReqData>, pg_pool: Data, ) -> Result { // @todo ACL - let request_json = request_json.into_inner(); //todo - let form = serde_json::from_value(request_json.clone()) - .map_err(|err| { - let msg = format!("{}", err); //todo JsonReponse::BadRequest::from(err) - JsonResponse::::build().bad_request(msg) - }) - .and_then(|mut form: forms::project::ProjectForm| { - if !form.validate().is_ok() { - let errors = form.validate().unwrap_err().to_string(); - let err_msg = format!("Invalid data received {:?}", &errors); - tracing::debug!(err_msg); - - return Err(JsonResponse::::build().form_error(errors)); - } - - Ok(form) - })?; + let form: forms::project::ProjectForm= serde_json::from_value(request_json.clone()) + .map_err(JsonResponse::err_bad_request)?; + if !form.validate().is_ok() { + let errors = form.validate().unwrap_err(); + return Err(JsonResponse::err_bad_request(errors)); + } let project_name = form.custom.custom_stack_code.clone(); - - tracing::debug!("Request json: {:?}", request_json); let body: Value = serde_json::to_value::(form) .or(serde_json::to_value::(forms::project::ProjectForm::default())) .unwrap(); From 63888bd8e4060b8bdb6843ecc003d2045ee2606b Mon Sep 17 00:00:00 2001 From: petru Date: Sat, 23 Mar 2024 16:34:50 +0200 Subject: [PATCH 10/13] 30-access-policies POST /project --- src/routes/project/add.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/routes/project/add.rs b/src/routes/project/add.rs index 892d31aa..0c170665 100644 --- a/src/routes/project/add.rs +++ b/src/routes/project/add.rs @@ -1,10 +1,10 @@ use crate::db; -use crate::forms; +use crate::forms::project::ProjectForm; use crate::helpers::JsonResponse; use crate::models; use actix_web::{ post, web, - web::{Bytes, Data}, + web::{Data}, Responder, Result, }; use serde_json::Value; @@ -20,7 +20,7 @@ pub async fn item( pg_pool: Data, ) -> Result { // @todo ACL - let form: forms::project::ProjectForm= serde_json::from_value(request_json.clone()) + let form: ProjectForm= serde_json::from_value(request_json.clone()) .map_err(JsonResponse::err_bad_request)?; if !form.validate().is_ok() { let errors = form.validate().unwrap_err(); @@ -28,8 +28,8 @@ pub async fn item( } let project_name = form.custom.custom_stack_code.clone(); - let body: Value = serde_json::to_value::(form) - .or(serde_json::to_value::(forms::project::ProjectForm::default())) + let body: Value = serde_json::to_value::(form) + .or(serde_json::to_value::(ProjectForm::default())) .unwrap(); let project = models::Project::new( From 1637f6f0b5e623a024bc75d01c62645238425285 Mon Sep 17 00:00:00 2001 From: petru Date: Sat, 23 Mar 2024 21:11:07 +0200 Subject: [PATCH 11/13] 30-access-policies POST/PUT /project --- src/helpers/json.rs | 27 +++++++++++++--------- src/routes/project/add.rs | 6 ++--- src/routes/project/update.rs | 43 ++++++++++++++---------------------- src/startup.rs | 2 +- 4 files changed, 37 insertions(+), 41 deletions(-) diff --git a/src/helpers/json.rs b/src/helpers/json.rs index ac77cbe9..36f8d39f 100644 --- a/src/helpers/json.rs +++ b/src/helpers/json.rs @@ -1,8 +1,7 @@ use actix_web::error::{ErrorBadRequest, ErrorConflict, ErrorInternalServerError, ErrorNotFound, ErrorUnauthorized}; use actix_web::web::Json; -use actix_web::Error as ActixError; +use actix_web::Error; use serde_derive::Serialize; -use std::error::Error as StdError; use std::convert::From; #[derive(Serialize)] @@ -72,33 +71,33 @@ where pub(crate) fn bad_request>( self, msg: I, - ) -> ActixError { + ) -> Error { ErrorBadRequest(self.set_msg(msg).to_string()) } - pub(crate) fn form_error(self, msg: String) -> ActixError { + pub(crate) fn form_error(self, msg: String) -> Error { ErrorBadRequest(msg) } - pub(crate) fn not_found>(self, msg: I) -> ActixError { + pub(crate) fn not_found>(self, msg: I) -> Error { ErrorNotFound(self.set_msg(msg).to_string()) } pub(crate) fn internal_server_error>( self, msg: I, - ) -> ActixError { + ) -> Error { ErrorInternalServerError(self.set_msg(msg).to_string()) } pub(crate) fn unauthorized>( self, msg: I, - ) -> ActixError { + ) -> Error { ErrorUnauthorized(self.set_msg(msg).to_string()) } - pub(crate) fn conflict>(self, msg: I) -> ActixError { + pub(crate) fn conflict>(self, msg: I) -> Error { ErrorConflict(self.set_msg(msg).to_string()) } } @@ -113,7 +112,15 @@ where } impl JsonResponse { - pub fn err_bad_request(e: E) -> ActixError { - JsonResponse::::build().bad_request( e.to_string()) + pub fn bad_request>(msg: I) -> Error { + JsonResponse::::build().bad_request( msg.into()) + } + + pub fn internal_server_error>(msg: I) -> Error { + JsonResponse::::build().internal_server_error( msg.into()) + } + + pub fn not_found>(msg: I) -> Error { + JsonResponse::::build().not_found(msg.into()) } } diff --git a/src/routes/project/add.rs b/src/routes/project/add.rs index 0c170665..683e1d33 100644 --- a/src/routes/project/add.rs +++ b/src/routes/project/add.rs @@ -21,10 +21,10 @@ pub async fn item( ) -> Result { // @todo ACL let form: ProjectForm= serde_json::from_value(request_json.clone()) - .map_err(JsonResponse::err_bad_request)?; + .map_err(|err| JsonResponse::bad_request(err.to_string()))?; if !form.validate().is_ok() { let errors = form.validate().unwrap_err(); - return Err(JsonResponse::err_bad_request(errors)); + return Err(JsonResponse::bad_request(errors.to_string())); } let project_name = form.custom.custom_stack_code.clone(); @@ -43,6 +43,6 @@ pub async fn item( .await .map(|project| JsonResponse::build().set_item(project).ok("Ok")) .map_err(|_| { - JsonResponse::::build().internal_server_error("Internal Server Error") + JsonResponse::internal_server_error("Internal Server Error") }) } diff --git a/src/routes/project/update.rs b/src/routes/project/update.rs index bfd82b66..312c4226 100644 --- a/src/routes/project/update.rs +++ b/src/routes/project/update.rs @@ -1,60 +1,51 @@ use std::str::FromStr; -use crate::forms; +use crate::forms::project::ProjectForm; use crate::helpers::JsonResponse; use crate::models; use crate::db; -use actix_web::{web, web::Data, Responder, Result, put}; +use actix_web::{web, Responder, Result, put}; use serde_json::Value; use serde_valid::Validate; use sqlx::PgPool; use std::sync::Arc; -use actix_web::web::Bytes; use tracing::Instrument; use std::str; -/* #[tracing::instrument(name = "Update project.")] #[put("/{id}")] pub async fn item( path: web::Path<(i32,)>, - body: Bytes, + web::Json(request_json): web::Json, user: web::ReqData>, - pg_pool: Data, + pg_pool: web::Data, ) -> Result { let id = path.0; let mut project = db::project::fetch(pg_pool.get_ref(), id) .await - .map_err(|err| JsonResponse::::build().internal_server_error(err)) + .map_err(JsonResponse::internal_server_error) .and_then(|project| match project { Some(project) if project.user_id != user.id => { - Err(JsonResponse::::build().bad_request("Project not found")) + Err(JsonResponse::bad_request("Project not found")) } Some(project) => Ok(project), - None => Err(JsonResponse::::build().not_found("Project not found")), + None => Err(JsonResponse::not_found("Project not found")), })?; - let body_bytes = actix_web::body::to_bytes(body.clone()).await.unwrap(); - let body_str = str::from_utf8(&body_bytes) - .map_err(|err| JsonResponse::::build().internal_server_error(err.to_string()))?; - let request_json = Value::from_str(body_str)?; - tracing::debug!("Request json: {:?}", request_json); - // @todo ACL - let form = forms::project::form::body_into_form(body.clone()).await?; - tracing::debug!("form data: {:?}", form); - - if let Err(errors) = form.validate() { - return Err(JsonResponse::::build().form_error(errors.to_string())); + let form: ProjectForm= serde_json::from_value(request_json.clone()) + .map_err(|err| JsonResponse::bad_request(err.to_string()))?; + if !form.validate().is_ok() { + let errors = form.validate().unwrap_err(); + return Err(JsonResponse::bad_request(errors.to_string())); } let project_name = form.custom.custom_stack_code.clone(); - if !form.is_readable_docker_image().await.is_ok() { - return Err(JsonResponse::::build().bad_request("Can not access docker image")); + return Err(JsonResponse::bad_request("Can not access docker image")); } - let body: Value = serde_json::to_value::(form) - .or(serde_json::to_value::(forms::project::ProjectForm::default())) + let body: Value = serde_json::to_value::(form) + .or(serde_json::to_value::(ProjectForm::default())) .unwrap(); @@ -62,7 +53,6 @@ pub async fn item( project.body = body; project.request_json = request_json; - db::project::update(pg_pool.get_ref(), project) .await .map(|project| { @@ -72,7 +62,6 @@ pub async fn item( }) .map_err(|err| { tracing::error!("Failed to execute query: {:?}", err); - JsonResponse::::build().internal_server_error("") + JsonResponse::internal_server_error("") }) } -*/ diff --git a/src/startup.rs b/src/startup.rs index 9c31afd4..35ee007e 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -78,7 +78,7 @@ pub async fn run( .service(crate::routes::project::get::item) .service(crate::routes::project::get::list) .service(crate::routes::project::add::item) - //.service(crate::routes::project::update::item) todo + .service(crate::routes::project::update::item) .service(crate::routes::project::delete::item), ) .service( From 304f54be296624b7f2d1c0ea3f08515885bfd42b Mon Sep 17 00:00:00 2001 From: petru Date: Sat, 23 Mar 2024 21:34:44 +0200 Subject: [PATCH 12/13] 30-access-policies admin get project --- src/routes/project/get.rs | 11 +++++------ src/startup.rs | 8 +++++++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/routes/project/get.rs b/src/routes/project/get.rs index 3cd7fc3e..7544a797 100644 --- a/src/routes/project/get.rs +++ b/src/routes/project/get.rs @@ -13,18 +13,17 @@ pub async fn item( path: web::Path<(i32,)>, pg_pool: web::Data, ) -> Result { - /// Get project apps of logged user only - let (id,) = path.into_inner(); + let id = path.0; db::project::fetch(pg_pool.get_ref(), id) .await - .map_err(|err| JsonResponse::::build().internal_server_error(err)) + .map_err(|err| JsonResponse::internal_server_error(err.to_string())) .and_then(|project| match project { Some(project) if project.user_id != user.id => { - Err(JsonResponse::::build().not_found("not found")) + Err(JsonResponse::not_found("not found")) } Some(project) => Ok(JsonResponse::build().set_item(Some(project)).ok("OK")), - None => Err(JsonResponse::::build().not_found("not found")), + None => Err(JsonResponse::not_found("not found")), }) } @@ -42,6 +41,6 @@ pub async fn list( db::project::fetch_by_user(pg_pool.get_ref(), &user_id) .await - .map_err(|err| JsonResponse::::build().internal_server_error(err)) + .map_err(|err| JsonResponse::internal_server_error(err)) .map(|projects| JsonResponse::build().set_list(projects).ok("OK")) } diff --git a/src/startup.rs b/src/startup.rs index 35ee007e..b0d788b4 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -76,11 +76,17 @@ pub async fn run( .service(crate::routes::project::compose::add) .service(crate::routes::project::compose::admin) .service(crate::routes::project::get::item) - .service(crate::routes::project::get::list) .service(crate::routes::project::add::item) .service(crate::routes::project::update::item) .service(crate::routes::project::delete::item), ) + .service( + web::scope("/admin") + .service( + web::scope("/project") + .service(crate::routes::project::get::list) + ) + ) .service( web::scope("/cloud") .service(crate::routes::cloud::get::item) From c00e09356b57aceef8c3a5f44f8e11fd19b23502 Mon Sep 17 00:00:00 2001 From: petru Date: Sat, 23 Mar 2024 21:38:48 +0200 Subject: [PATCH 13/13] 30-access-policies admin routes grouped --- src/routes/project/get.rs | 2 +- src/startup.rs | 20 +++++++------------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/routes/project/get.rs b/src/routes/project/get.rs index 7544a797..7c0ca553 100644 --- a/src/routes/project/get.rs +++ b/src/routes/project/get.rs @@ -29,7 +29,7 @@ pub async fn item( #[tracing::instrument(name = "Get user's project list.")] #[get("/user/{id}")] -pub async fn list( +pub async fn admin_list( user: web::ReqData>, path: web::Path<(String,)>, pg_pool: web::Data, diff --git a/src/startup.rs b/src/startup.rs index b0d788b4..56a317f8 100644 --- a/src/startup.rs +++ b/src/startup.rs @@ -54,12 +54,6 @@ pub async fn run( .service(routes::client::enable_handler) .service(routes::client::disable_handler), ) - .service( - web::scope("/admin/client") - .service(routes::client::admin_enable_handler) - .service(routes::client::admin_update_handler) - .service(routes::client::admin_disable_handler), - ) .service( web::scope("/test") .service(routes::test::deploy::handler) @@ -84,7 +78,13 @@ pub async fn run( web::scope("/admin") .service( web::scope("/project") - .service(crate::routes::project::get::list) + .service(crate::routes::project::get::admin_list) + ) + .service( + web::scope("/client") + .service(routes::client::admin_enable_handler) + .service(routes::client::admin_update_handler) + .service(routes::client::admin_disable_handler), ) ) .service( @@ -103,12 +103,6 @@ pub async fn run( .service(crate::routes::server::update::item) .service(crate::routes::server::delete::item), ) - // @todo stack renamed to project - // .service( - // web::scope("/admin/project") - // .service(routes::project::get::admin_item) - // .service(routes::project::get::admin_list) - // ) .app_data(json_config.clone()) .app_data(pg_pool.clone()) .app_data(mq_manager.clone())