proper error catchers

This commit is contained in:
c0repwn3r 2023-02-03 23:08:19 -05:00
parent 94d90c21a2
commit a6ea23c32d
Signed by: core
GPG Key ID: FDBF740DADDCEECF
8 changed files with 116 additions and 96 deletions

1
Cargo.lock generated
View File

@ -1941,6 +1941,7 @@ dependencies = [
"base64 0.21.0", "base64 0.21.0",
"dotenvy", "dotenvy",
"log", "log",
"paste",
"rocket", "rocket",
"serde", "serde",
"sqlx", "sqlx",

View File

@ -14,3 +14,4 @@ tokio = { version = "1", features = ["full"] }
toml = "0.7.1" toml = "0.7.1"
serde = "1.0.152" serde = "1.0.152"
dotenvy = "0.15.6" dotenvy = "0.15.6"
paste = "1.0.11"

View File

@ -1,6 +1,4 @@
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use base64::{decode, Engine};
use rocket::info;
use crate::format::PEMValidationError::{IncorrectSegmentLength, InvalidBase64Data, MissingStartSentinel}; use crate::format::PEMValidationError::{IncorrectSegmentLength, InvalidBase64Data, MissingStartSentinel};
use crate::util::base64decode; use crate::util::base64decode;

View File

@ -3,107 +3,17 @@ use std::fs;
use std::path::Path; use std::path::Path;
use dotenvy::dotenv; use dotenvy::dotenv;
use log::{error, info}; use log::{error, info};
use rocket::{routes, post, State, Rocket, Ignite}; use rocket::{catchers, routes};
use rocket::{serde::Serialize};
use rocket::http::{ContentType, Status};
use rocket::serde::Deserialize;
use rocket::serde::json::Json;
use sqlx::migrate::Migrator; use sqlx::migrate::Migrator;
use sqlx::PgPool;
use sqlx::postgres::PgPoolOptions; use sqlx::postgres::PgPoolOptions;
use crate::config::TFConfig; use crate::config::TFConfig;
use crate::format::{validate_dh_pubkey_base64, validate_ed_pubkey_base64};
pub mod format; pub mod format;
pub mod util; pub mod util;
pub mod db; pub mod db;
pub mod config; pub mod config;
#[derive(Deserialize)] pub mod routes;
#[serde(crate = "rocket::serde")]
pub struct EnrollRequest {
pub code: String,
#[serde(rename = "dhPubkey")]
pub dh_pubkey: String,
#[serde(rename = "edPubkey")]
pub ed_pubkey: String,
pub timestamp: String,
}
#[derive(Serialize)]
#[serde(crate = "rocket::serde")]
pub struct EnrollResponseMetadata {}
#[derive(Serialize)]
#[serde(crate = "rocket::serde")]
pub struct EnrollResponseOrganization {
pub id: String,
pub name: String,
}
#[derive(Serialize)]
#[serde(crate = "rocket::serde")]
pub struct EnrollResponseData {
pub config: String,
pub host_id: String,
pub counter: i64,
pub trusted_keys: String,
pub organization: EnrollResponseOrganization,
}
#[derive(Serialize)]
#[serde(crate = "rocket::serde")]
pub struct EnrollResponse {
pub data: EnrollResponseData,
pub metadata: EnrollResponseMetadata,
}
#[derive(Serialize)]
#[serde(crate = "rocket::serde")]
pub struct APIError {
errors: Vec<APIErrorSingular>
}
#[derive(Serialize)]
#[serde(crate = "rocket::serde")]
pub struct APIErrorSingular {
code: String,
message: String
}
pub const ERR_MSG_MALFORMED_REQUEST: &str = "unable to parse the request body - is it valid JSON, using correct types?";
pub const ERR_MSG_MALFORMED_REQUEST_CODE: &str = "ERR_MALFORMED_REQUEST";
#[post("/v2/enroll", data = "<request>")]
fn enroll_endpoint(request: String, pool: &State<PgPool>) -> Result<(ContentType, Json<EnrollResponse>), (Status, Json<APIError>)> {
let request: EnrollRequest = match rocket::serde::json::from_str(request.as_str()) {
Ok(r) => r,
Err(e) => {
return Err((Status::BadRequest, Json(APIError { errors: vec![APIErrorSingular { code: ERR_MSG_MALFORMED_REQUEST_CODE.to_string(), message: format!("{} - {}", ERR_MSG_MALFORMED_REQUEST, e) }]})))
}
};
// validate request
if let Err(e) = validate_dh_pubkey_base64(request.dh_pubkey.as_str()) {
return Err((Status::BadRequest, Json(APIError { errors: vec![APIErrorSingular { code: ERR_MSG_MALFORMED_REQUEST_CODE.to_string(), message: format!("{} - invalid dhPubkey - {}", ERR_MSG_MALFORMED_REQUEST, e) }]})))
}
if let Err(e) = validate_ed_pubkey_base64(request.ed_pubkey.as_str()) {
return Err((Status::BadRequest, Json(APIError { errors: vec![APIErrorSingular { code: ERR_MSG_MALFORMED_REQUEST_CODE.to_string(), message: format!("{} - invalid edPubkey - {}", ERR_MSG_MALFORMED_REQUEST, e) }]})))
}
Ok((ContentType::JSON, Json(EnrollResponse {
data: EnrollResponseData {
config: "sdf".to_string(),
host_id: "sdf".to_string(),
counter: 0,
trusted_keys: "sdf".to_string(),
organization: EnrollResponseOrganization { id: "99s98d9878fds".to_string(), name: "e3team CA".to_string() },
},
metadata: EnrollResponseMetadata {},
})))
}
#[post("/v1/dnclient")]
fn dnclient_endpoint() -> &'static str {
"DNClient functionality is not yet implemented"
}
static MIGRATOR: Migrator = sqlx::migrate!(); static MIGRATOR: Migrator = sqlx::migrate!();
@ -165,9 +75,36 @@ async fn main() -> Result<(), Box<dyn Error>> {
info!("[tfapi] building rocket config"); info!("[tfapi] building rocket config");
let figment = rocket::Config::figment().merge(("port", config.listen_port)); let figment = rocket::Config::figment().merge(("port", config.listen_port));
/*
error_handler!(400, "ERR_MALFORMED_REQUEST", "unable to parse the request body, is it properly formatted?");
error_handler!(401, "ERR_AUTHENTICATION_REQUIRED", "this endpoint requires authentication but it was not provided");
error_handler!(403, "ERR_UNAUTHORIZED", "authorization was provided but it is expired or invalid");
error_handler!(404, "ERR_NOT_FOUND", "resource not found");
error_handler!(405, "ERR_METHOD_NOT_ALLOWED", "method not allowed for this endpoint");
error_handler!(500, "ERR_QL_QUERY_FAILED", "graphql query timed out");
error_handler!(501, "ERR_NOT_IMPLEMENTED", "query not supported by this version of graphql");
error_handler!(502, "ERR_PROXY_ERR", "servers under load, please try again later");
error_handler!(503, "ERR_SERVER_OVERLOADED", "servers under load, please try again later");
error_handler!(504, "ERR_PROXY_TIMEOUT", "servers under load, please try again later");
error_handler!(505, "ERR_CLIENT_UNSUPPORTED", "your version of dnclient is out of date, please update");
*/
let _ = rocket::custom(figment) let _ = rocket::custom(figment)
.mount("/", routes![enroll_endpoint, dnclient_endpoint]) .mount("/", routes![
crate::routes::v1::auth::verify_magic_link::verify_magic_link
])
.register("/", catchers![
crate::routes::handler_400,
crate::routes::handler_401,
crate::routes::handler_403,
crate::routes::handler_404,
crate::routes::handler_500,
crate::routes::handler_501,
crate::routes::handler_502,
crate::routes::handler_503,
crate::routes::handler_504,
crate::routes::handler_505,
])
.manage(pool) .manage(pool)
.manage(config) .manage(config)
.launch().await?; .launch().await?;

View File

@ -0,0 +1,43 @@
pub mod v1;
use rocket::catch;
use serde::{Serialize};
use rocket::http::Status;
pub const ERR_MSG_MALFORMED_REQUEST: &str = "unable to parse the request body - is it valid JSON, using correct types?";
pub const ERR_MSG_MALFORMED_REQUEST_CODE: &str = "ERR_MALFORMED_REQUEST";
#[derive(Serialize)]
#[serde(crate = "rocket::serde")]
pub struct APIError {
errors: Vec<APIErrorSingular>
}
#[derive(Serialize)]
#[serde(crate = "rocket::serde")]
pub struct APIErrorSingular {
code: String,
message: String
}
macro_rules! error_handler {
($code: expr, $err: expr, $msg: expr) => {
::paste::paste! {
#[catch($code)]
pub fn [<handler_ $code>]() -> (Status, String) {
(Status::from_code($code).unwrap(), format!("{{\"errors\":[{{\"code\":\"{}\",\"message\":\"{}\"}}]}}", $err, $msg))
}
}
};
}
error_handler!(400, "ERR_MALFORMED_REQUEST", "unable to parse the request body, is it properly formatted?");
error_handler!(401, "ERR_AUTHENTICATION_REQUIRED", "this endpoint requires authentication but it was not provided");
error_handler!(403, "ERR_UNAUTHORIZED", "authorization was provided but it is expired or invalid");
error_handler!(404, "ERR_NOT_FOUND", "resource not found");
error_handler!(405, "ERR_METHOD_NOT_ALLOWED", "method not allowed for this endpoint");
error_handler!(500, "ERR_QL_QUERY_FAILED", "graphql query timed out");
error_handler!(501, "ERR_NOT_IMPLEMENTED", "query not supported by this version of graphql");
error_handler!(502, "ERR_PROXY_ERR", "servers under load, please try again later");
error_handler!(503, "ERR_SERVER_OVERLOADED", "servers under load, please try again later");
error_handler!(504, "ERR_PROXY_TIMEOUT", "servers under load, please try again later");
error_handler!(505, "ERR_CLIENT_UNSUPPORTED", "your version of dnclient is out of date, please update");

View File

@ -0,0 +1 @@
pub mod verify_magic_link;

View File

@ -0,0 +1,38 @@
use rocket::http::{ContentType, Status};
use rocket::serde::json::Json;
use serde::{Serialize, Deserialize};
use crate::routes::{APIError, APIErrorSingular, ERR_MSG_MALFORMED_REQUEST, ERR_MSG_MALFORMED_REQUEST_CODE};
use rocket::post;
#[derive(Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct VerifyMagicLinkRequest {
#[serde(rename = "magicLinkToken")]
pub magic_link_token: String,
}
#[derive(Serialize, Deserialize)]
pub struct VerifyMagicLinkResponseMetadata {}
#[derive(Serialize, Deserialize)]
pub struct VerifyMagicLinkResponseData {
#[serde(rename = "sessionToken")]
pub session_token: String,
}
#[derive(Serialize, Deserialize)]
pub struct VerifyMagicLinkResponse {
pub data: VerifyMagicLinkResponseData,
pub metadata: VerifyMagicLinkResponseMetadata,
}
#[post("/v1/auth/verify-magic-link", data = "<req>")]
pub fn verify_magic_link(req: Json<VerifyMagicLinkRequest>) -> Result<(ContentType, Json<VerifyMagicLinkResponse>), (Status, Json<APIError>)> {
// handle request
Ok((ContentType::JSON, Json(VerifyMagicLinkResponse {
data: VerifyMagicLinkResponseData { session_token: "sd[if0sf0dsfsdf".to_string() },
metadata: VerifyMagicLinkResponseMetadata {},
})))
}

View File

@ -0,0 +1 @@
pub mod auth;