From ea3bc9e4290836adb2f2c06fdb3a18ed1acad300 Mon Sep 17 00:00:00 2001 From: core Date: Sun, 30 Jul 2023 21:09:51 -0400 Subject: [PATCH] create host and enroll error parity References: https://todo.e3t.cc/~core/trifid/1 --- trifid-api/src/routes/v1/hosts.rs | 254 ++++++++++++++++++++++-------- 1 file changed, 186 insertions(+), 68 deletions(-) diff --git a/trifid-api/src/routes/v1/hosts.rs b/trifid-api/src/routes/v1/hosts.rs index ec14f2e..59d96b7 100644 --- a/trifid-api/src/routes/v1/hosts.rs +++ b/trifid-api/src/routes/v1/hosts.rs @@ -195,7 +195,7 @@ pub async fn get_hosts( APIError { code: "ERR_AMBIGUOUS_AUTHENTICATION".to_string(), message: "Both a user token and an API token with the proper scope was provided. Please only provide one.".to_string(), - path: None + path: None, } ], }); @@ -238,7 +238,7 @@ pub async fn get_hosts( APIError { code: "ERR_NO_ORG".to_string(), message: "This user does not own any organizations. Try using an API token instead.".to_string(), - path: None + path: None, } ], }); @@ -417,7 +417,7 @@ pub async fn get_hosts( match (Cursor { page: cursor.page - 1, }) - .try_into() + .try_into() { Ok(r) => Some(r), Err(_) => None, @@ -429,7 +429,7 @@ pub async fn get_hosts( match (Cursor { page: cursor.page + 1, }) - .try_into() + .try_into() { Ok(r) => Some(r), Err(_) => None, @@ -515,7 +515,7 @@ pub async fn create_hosts_request( APIError { code: "ERR_AMBIGUOUS_AUTHENTICATION".to_string(), message: "Both a user token and an API token with the proper scope was provided. Please only provide one.".to_string(), - path: None + path: None, } ], }); @@ -558,7 +558,7 @@ pub async fn create_hosts_request( APIError { code: "ERR_NO_ORG".to_string(), message: "This user does not own any organizations. Try using an API token instead.".to_string(), - path: None + path: None, } ], }); @@ -639,7 +639,7 @@ pub async fn create_hosts_request( errors: vec![APIError { code: "ERR_INVALID_VALUE".to_string(), message: "lighthouse hosts must specify a static listen port".to_string(), - path: Some("listenPort".to_string()) + path: Some("listenPort".to_string()), }] }); } else if req.listen_port == 0 && req.is_relay { @@ -647,7 +647,7 @@ pub async fn create_hosts_request( errors: vec![APIError { code: "ERR_INVALID_VALUE".to_string(), message: "relay hosts must specify a static listen port".to_string(), - path: Some("listenPort".to_string()) + path: Some("listenPort".to_string()), }] }); } @@ -857,6 +857,7 @@ pub struct GetHostResponse { pub data: HostResponse, pub metadata: GetHostResponseMetadata, } + #[derive(Serialize, Deserialize)] pub struct GetHostResponseMetadata {} @@ -894,7 +895,7 @@ pub async fn get_host(id: Path, req_info: HttpRequest, db: Data, req_info: HttpRequest, db: Data, req_info: HttpRequest, db: Data, // t+features:extended_hosts - pub role: Option + pub role: Option, } #[derive(Serialize, Deserialize)] @@ -1386,7 +1389,7 @@ pub async fn edit_host( APIError { code: "ERR_AMBIGUOUS_AUTHENTICATION".to_string(), message: "Both a user token and an API token with the proper scope was provided. Please only provide one.".to_string(), - path: None + path: None, } ], }); @@ -1429,7 +1432,7 @@ pub async fn edit_host( APIError { code: "ERR_NO_ORG".to_string(), message: "This user does not own any organizations. Try using an API token instead.".to_string(), - path: None + path: None, } ], }); @@ -1498,11 +1501,11 @@ pub async fn edit_host( errors: vec![APIError { code: "ERR_NOT_FOUND".to_string(), message: - "resource not found" - .to_string(), + "resource not found" + .to_string(), path: None, }], - }) + }); } }; @@ -1545,8 +1548,8 @@ pub async fn edit_host( errors: vec![APIError { code: "ERR_DB_ERROR".to_string(), message: - "There was an error with the database query. Please try again later." - .to_string(), + "There was an error with the database query. Please try again later." + .to_string(), path: None, }], }); @@ -1713,7 +1716,7 @@ pub async fn block_host( APIError { code: "ERR_AMBIGUOUS_AUTHENTICATION".to_string(), message: "Both a user token and an API token with the proper scope was provided. Please only provide one.".to_string(), - path: None + path: None, } ], }); @@ -1756,7 +1759,7 @@ pub async fn block_host( APIError { code: "ERR_NO_ORG".to_string(), message: "This user does not own any organizations. Try using an API token instead.".to_string(), - path: None + path: None, } ], }); @@ -1825,11 +1828,11 @@ pub async fn block_host( errors: vec![APIError { code: "ERR_NOT_FOUND".to_string(), message: - "resource not found" - .to_string(), + "resource not found" + .to_string(), path: None, }], - }) + }); } }; @@ -1980,7 +1983,7 @@ pub async fn enroll_host( APIError { code: "ERR_AMBIGUOUS_AUTHENTICATION".to_string(), message: "Both a user token and an API token with the proper scope was provided. Please only provide one.".to_string(), - path: None + path: None, } ], }); @@ -2023,7 +2026,7 @@ pub async fn enroll_host( APIError { code: "ERR_NO_ORG".to_string(), message: "This user does not own any organizations. Try using an API token instead.".to_string(), - path: None + path: None, } ], }); @@ -2092,11 +2095,11 @@ pub async fn enroll_host( errors: vec![APIError { code: "ERR_NOT_FOUND".to_string(), message: - "resource not found" - .to_string(), + "resource not found" + .to_string(), path: None, }], - }) + }); } }; @@ -2198,7 +2201,7 @@ pub async fn create_host_and_enrollment_code( APIError { code: "ERR_AMBIGUOUS_AUTHENTICATION".to_string(), message: "Both a user token and an API token with the proper scope was provided. Please only provide one.".to_string(), - path: None + path: None, } ], }); @@ -2224,8 +2227,8 @@ pub async fn create_host_and_enrollment_code( return HttpResponse::InternalServerError().json(APIErrorsResponse { errors: vec![ APIError { - code: "ERR_DB_ERROR".to_string(), - message: "There was an error performing the database request, please try again later.".to_string(), + code: "ERR_MISSING_ORG".to_string(), + message: "The server was unable to locate your organization, please try again later or report this to your administrator if it continues to fail.".to_string(), path: None, } ], @@ -2241,7 +2244,7 @@ pub async fn create_host_and_enrollment_code( APIError { code: "ERR_NO_ORG".to_string(), message: "This user does not own any organizations. Try using an API token instead.".to_string(), - path: None + path: None, } ], }); @@ -2262,9 +2265,9 @@ pub async fn create_host_and_enrollment_code( return HttpResponse::InternalServerError().json(APIErrorsResponse { errors: vec![ APIError { - code: "ERR_DB_ERROR".to_string(), - message: "There was an error performing the database request, please try again later.".to_string(), - path: None, + code: "ERR_INVALID_REFERENCE".to_string(), + message: "referenced value is invalid (perhaps it does not exist?)".to_string(), + path: Some("networkID".to_string()), } ], }); @@ -2276,10 +2279,9 @@ pub async fn create_host_and_enrollment_code( } else { return HttpResponse::Unauthorized().json(APIErrorsResponse { errors: vec![APIError { - code: "ERR_NO_NET".to_string(), - message: "This user does not own any networks. Try using an API token instead." - .to_string(), - path: None, + code: "ERR_INVALID_REFERENCE".to_string(), + message: "referenced value is invalid (perhaps it does not exist?)".to_string(), + path: Some("networkID".to_string()), }], }); } @@ -2288,9 +2290,9 @@ pub async fn create_host_and_enrollment_code( return HttpResponse::Unauthorized().json(APIErrorsResponse { errors: vec![ APIError { - code: "ERR_WRONG_NET".to_string(), - message: "The network on the request does not match the network associated with this token or user.".to_string(), - path: None + code: "ERR_INVALID_REFERENCE".to_string(), + message: "referenced value is invalid (perhaps it does not exist?)".to_string(), + path: Some("networkID".to_string()), } ], }); @@ -2299,8 +2301,8 @@ pub async fn create_host_and_enrollment_code( if req.is_lighthouse && req.is_relay { return HttpResponse::BadRequest().json(APIErrorsResponse { errors: vec![APIError { - code: "ERR_CANNOT_BE_RELAY_AND_LIGHTHOUSE".to_string(), - message: "A host cannot be a relay and a lighthouse at the same time.".to_string(), + code: "ERR_INVALID_VALUE".to_string(), + message: "lighthouse hosts must not also be relay hosts".to_string(), path: None, }], }); @@ -2309,13 +2311,129 @@ pub async fn create_host_and_enrollment_code( if (req.is_lighthouse || req.is_relay) && req.static_addresses.is_empty() { return HttpResponse::BadRequest().json(APIErrorsResponse { errors: vec![APIError { - code: "ERR_NEEDS_STATIC_ADDR".to_string(), - message: "A relay or lighthouse requires at least one static address.".to_string(), - path: None, + code: "ERR_INVALID_VALUE".to_string(), + message: "lighthouse hosts must have at least one static ip address".to_string(), + path: Some("staticAddresses".to_string()), }], }); } + if req.listen_port == 0 && req.is_lighthouse { + return HttpResponse::BadRequest().json(APIErrorsResponse { + errors: vec![APIError { + code: "ERR_INVALID_VALUE".to_string(), + message: "lighthouse hosts must specify a static listen port".to_string(), + path: Some("listenPort".to_string()), + }] + }); + } else if req.listen_port == 0 && req.is_relay { + return HttpResponse::BadRequest().json(APIErrorsResponse { + errors: vec![APIError { + code: "ERR_INVALID_VALUE".to_string(), + message: "relay hosts must specify a static listen port".to_string(), + path: Some("listenPort".to_string()), + }] + }); + } + + if let Some(role) = &req.role_id { + let roles = match role::Entity::find() + .filter(role::Column::Id.eq(role)) + .all(&db.conn) + .await + { + Ok(r) => r, + Err(e) => { + error!("database error: {}", e); + return HttpResponse::InternalServerError().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_DB_ERROR".to_string(), + message: "There was an error validating the request. Please try again later.".to_string(), + path: Some("role".to_string()), + } + ], + }); + } + }; + + if roles.is_empty() { + return HttpResponse::BadRequest().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_INVALID_REFERENCE".to_string(), + message: "referenced value is invalid (perhaps it does not exist?)".to_string(), + path: Some("role".to_string()), + } + ], + }); + } + } + + let matching_hostname = match host::Entity::find() + .filter(host::Column::Name.eq(&req.name)) + .all(&db.conn) + .await + { + Ok(r) => r, + Err(e) => { + error!("database error: {}", e); + return HttpResponse::InternalServerError().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_DB_ERROR".to_string(), + message: "There was an error validating the request. Please try again later.".to_string(), + path: Some("name".to_string()), + } + ], + }); + } + }; + + if !matching_hostname.is_empty() { + return HttpResponse::BadRequest().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_DUPLICATE_VALUE".to_string(), + message: "value already exists".to_string(), + path: Some("name".to_string()), + } + ], + }); + } + + let matching_ip = match host::Entity::find() + .filter(host::Column::Ip.eq(&req.ip_address.to_string())) + .all(&db.conn) + .await + { + Ok(r) => r, + Err(e) => { + error!("database error: {}", e); + return HttpResponse::InternalServerError().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_DB_ERROR".to_string(), + message: "There was an error validating the request. Please try again later.".to_string(), + path: Some("ipAddress".to_string()), + } + ], + }); + } + }; + + if !matching_ip.is_empty() { + return HttpResponse::BadRequest().json(APIErrorsResponse { + errors: vec![ + APIError { + code: "ERR_DUPLICATE_VALUE".to_string(), + message: "value already exists".to_string(), + path: Some("ipAddress".to_string()), + } + ], + }); + } + let new_host_model = host::Model { id: random_id("host"), name: req.name.clone(), @@ -2447,18 +2565,18 @@ pub async fn create_host_and_enrollment_code( #[derive(Serialize, Deserialize)] pub struct HostConfigOverrideResponse { - pub data: HostConfigOverrideData + pub data: HostConfigOverrideData, } #[derive(Serialize, Deserialize)] pub struct HostConfigOverrideData { - pub overrides: Vec + pub overrides: Vec, } #[derive(Serialize, Deserialize)] pub struct HostConfigOverrideDataOverride { pub key: String, - pub value: HostConfigOverrideDataOverrideValue + pub value: HostConfigOverrideDataOverrideValue, } #[derive(Serialize, Deserialize)] @@ -2466,7 +2584,7 @@ pub struct HostConfigOverrideDataOverride { pub enum HostConfigOverrideDataOverrideValue { Boolean(bool), Numeric(i64), - Other(String) + Other(String), } #[get("/v1/hosts/{host_id}/config-overrides")] @@ -2503,7 +2621,7 @@ pub async fn get_host_overrides(id: Path, req_info: HttpRequest, db: Dat APIError { code: "ERR_AMBIGUOUS_AUTHENTICATION".to_string(), message: "Both a user token and an API token with the proper scope was provided. Please only provide one.".to_string(), - path: None + path: None, } ], }); @@ -2546,7 +2664,7 @@ pub async fn get_host_overrides(id: Path, req_info: HttpRequest, db: Dat APIError { code: "ERR_NO_ORG".to_string(), message: "This user does not own any organizations. Try using an API token instead.".to_string(), - path: None + path: None, } ], }); @@ -2619,7 +2737,7 @@ pub async fn get_host_overrides(id: Path, req_info: HttpRequest, db: Dat .to_string(), path: None, }], - }) + }); } }; @@ -2673,7 +2791,7 @@ pub async fn get_host_overrides(id: Path, req_info: HttpRequest, db: Dat #[derive(Serialize, Deserialize)] pub struct UpdateOverridesRequest { - pub overrides: Vec + pub overrides: Vec, } #[put("/v1/hosts/{host_id}/config-overrides")] @@ -2710,7 +2828,7 @@ pub async fn update_host_overrides(id: Path, req: Json, req: Json, req: Json