diff --git a/trifid-api/src/routes/v1/hosts.rs b/trifid-api/src/routes/v1/hosts.rs index f6fd78e..f611027 100644 --- a/trifid-api/src/routes/v1/hosts.rs +++ b/trifid-api/src/routes/v1/hosts.rs @@ -76,7 +76,7 @@ use serde::{Deserialize, Serialize}; use std::net::{Ipv4Addr, SocketAddrV4}; use std::str::FromStr; use std::time::{SystemTime, UNIX_EPOCH}; -use trifid_api_entities::entity::{host, host_config_override, host_static_address, network, organization}; +use trifid_api_entities::entity::{host, host_config_override, host_static_address, network, organization, role}; use trifid_api_entities::entity::prelude::HostConfigOverride; #[derive(Serialize, Deserialize)] @@ -542,8 +542,8 @@ pub async fn create_hosts_request( 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, } ], @@ -577,12 +577,12 @@ pub async fn create_hosts_request( Ok(r) => r, Err(e) => { error!("database error: {}", e); - return HttpResponse::InternalServerError().json(APIErrorsResponse { + return HttpResponse::BadRequest().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()), } ], }); @@ -592,23 +592,24 @@ pub async fn create_hosts_request( if let Some(net) = net { net_id = net.id; } 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, - }], + 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("networkID".to_string()), + } + ], }); } if net_id != req.network_id { - return HttpResponse::Unauthorized().json(APIErrorsResponse { + return HttpResponse::BadRequest().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()), } ], }); @@ -617,25 +618,139 @@ pub async fn create_hosts_request( 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, }], }); } - debug!("{:?}", req.static_addresses); - 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)) + .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(), @@ -674,6 +789,7 @@ pub async fn create_hosts_request( Ok(_) => (), Err(e) => { error!("database error: {}", e); + return HttpResponse::InternalServerError().json(APIErrorsResponse { errors: vec![APIError { code: "ERR_DB_ERROR".to_string(),