proper error catchers
This commit is contained in:
parent
94d90c21a2
commit
a6ea23c32d
|
@ -1941,6 +1941,7 @@ dependencies = [
|
||||||
"base64 0.21.0",
|
"base64 0.21.0",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"log",
|
"log",
|
||||||
|
"paste",
|
||||||
"rocket",
|
"rocket",
|
||||||
"serde",
|
"serde",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
|
|
|
@ -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"
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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?;
|
||||||
|
|
|
@ -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");
|
|
@ -0,0 +1 @@
|
||||||
|
pub mod verify_magic_link;
|
|
@ -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 {},
|
||||||
|
})))
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
pub mod auth;
|
Loading…
Reference in New Issue