forgot about config overrides for a second there
This commit is contained in:
parent
0ffb04c1e7
commit
11320ba04a
|
@ -4,7 +4,7 @@ use std::net::{Ipv4Addr, SocketAddrV4};
|
|||
use serde::{Deserialize, Serialize};
|
||||
use url::{Url};
|
||||
use crate::api::APIErrorResponse;
|
||||
use crate::{HostCommands};
|
||||
use crate::{HostCommands, HostOverrideCommands};
|
||||
|
||||
pub async fn host_main(command: HostCommands, server: Url) -> Result<(), Box<dyn Error>> {
|
||||
match command {
|
||||
|
@ -14,7 +14,12 @@ pub async fn host_main(command: HostCommands, server: Url) -> Result<(), Box<dyn
|
|||
HostCommands::Delete { id } => delete_host(id, server).await,
|
||||
HostCommands::Update { id, listen_port, static_address, name, ip, role } => update_host(id, listen_port, static_address, name, ip, role, server).await,
|
||||
HostCommands::Block { id } => block_host(id, server).await,
|
||||
HostCommands::Enroll { id } => enroll_host(id, server).await
|
||||
HostCommands::Enroll { id } => enroll_host(id, server).await,
|
||||
HostCommands::Overrides { command } => match command {
|
||||
HostOverrideCommands::List { id } => list_overrides(id, server).await,
|
||||
HostOverrideCommands::Set { id, key, boolean, string, numeric } => set_override(id, key, boolean, numeric, string, server).await,
|
||||
HostOverrideCommands::Unset { id, key } => unset_override(id, key, server).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -414,3 +419,222 @@ pub async fn block_host(id: String, server: Url) -> Result<(), Box<dyn Error>> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct HostConfigOverrideResponse {
|
||||
pub data: HostConfigOverrideData
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct HostConfigOverrideData {
|
||||
pub overrides: Vec<HostConfigOverrideDataOverride>
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct HostConfigOverrideDataOverride {
|
||||
pub key: String,
|
||||
pub value: HostConfigOverrideDataOverrideValue
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum HostConfigOverrideDataOverrideValue {
|
||||
Boolean(bool),
|
||||
Numeric(i64),
|
||||
Other(String)
|
||||
}
|
||||
|
||||
pub async fn list_overrides(id: String, server: Url) -> Result<(), Box<dyn Error>> {
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
// load session token
|
||||
let sess_token_store = dirs::config_dir().unwrap().join("tfcli-session.token");
|
||||
let session_token = fs::read_to_string(&sess_token_store)?;
|
||||
let auth_token_store = dirs::config_dir().unwrap().join("tfcli-auth.token");
|
||||
let auth_token = fs::read_to_string(&auth_token_store)?;
|
||||
|
||||
let token = format!("{} {}", session_token, auth_token);
|
||||
|
||||
let res = client.get(server.join(&format!("/v1/hosts/{}/config-overrides", id))?).bearer_auth(token).send().await?;
|
||||
|
||||
if res.status().is_success() {
|
||||
let resp: HostConfigOverrideResponse = res.json().await?;
|
||||
|
||||
for c_override in &resp.data.overrides {
|
||||
println!(" Key: {}", c_override.key);
|
||||
println!("Value: {}", match &c_override.value {
|
||||
HostConfigOverrideDataOverrideValue::Boolean(v) => format!("bool:{}", v),
|
||||
HostConfigOverrideDataOverrideValue::Numeric(v) => format!("numeric:{}", v),
|
||||
HostConfigOverrideDataOverrideValue::Other(v) => format!("string:{}", v)
|
||||
});
|
||||
}
|
||||
|
||||
if resp.data.overrides.is_empty() {
|
||||
println!("No overrides found");
|
||||
}
|
||||
} else {
|
||||
let resp: APIErrorResponse = res.json().await?;
|
||||
|
||||
eprintln!("[error] Error looking up config overrides: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct SetOverrideRequest {
|
||||
pub overrides: Vec<HostConfigOverrideDataOverride>
|
||||
}
|
||||
|
||||
pub async fn set_override(id: String, key: String, boolean: Option<bool>, numeric: Option<i64>, other: Option<String>, server: Url) -> Result<(), Box<dyn Error>> {
|
||||
if boolean.is_none() && numeric.is_none() && other.is_none() {
|
||||
eprintln!("[error] no value provided: you must provide at least --boolean, --numeric, or --string");
|
||||
std::process::exit(1);
|
||||
} else if boolean.is_some() && numeric.is_some() || boolean.is_some() && other.is_some() || numeric.is_some() && other.is_some() {
|
||||
eprintln!("[error] multiple values provided: you must provide only one of --boolean, --numeric, or --string");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
let val;
|
||||
|
||||
if let Some(v) = boolean {
|
||||
val = HostConfigOverrideDataOverrideValue::Boolean(v);
|
||||
} else if let Some(v) = numeric {
|
||||
val = HostConfigOverrideDataOverrideValue::Numeric(v);
|
||||
} else if let Some(v) = other {
|
||||
val = HostConfigOverrideDataOverrideValue::Other(v);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
// load session token
|
||||
let sess_token_store = dirs::config_dir().unwrap().join("tfcli-session.token");
|
||||
let session_token = fs::read_to_string(&sess_token_store)?;
|
||||
let auth_token_store = dirs::config_dir().unwrap().join("tfcli-auth.token");
|
||||
let auth_token = fs::read_to_string(&auth_token_store)?;
|
||||
|
||||
let token = format!("{} {}", session_token, auth_token);
|
||||
|
||||
let res = client.get(server.join(&format!("/v1/hosts/{}/config-overrides", id))?).bearer_auth(token.clone()).send().await?;
|
||||
|
||||
if res.status().is_success() {
|
||||
let resp: HostConfigOverrideResponse = res.json().await?;
|
||||
|
||||
let mut others: Vec<HostConfigOverrideDataOverride> = vec![];
|
||||
|
||||
for c_override in resp.data.overrides {
|
||||
if c_override.key != key {
|
||||
others.push(c_override);
|
||||
}
|
||||
}
|
||||
|
||||
others.push(HostConfigOverrideDataOverride {
|
||||
key,
|
||||
value: val,
|
||||
});
|
||||
|
||||
let res = client.put(server.join(&format!("/v1/hosts/{}/config-overrides", id))?).bearer_auth(token.clone()).json(&SetOverrideRequest {
|
||||
overrides: others,
|
||||
}).send().await?;
|
||||
|
||||
if res.status().is_success() {
|
||||
let resp: HostConfigOverrideResponse = res.json().await?;
|
||||
|
||||
for c_override in &resp.data.overrides {
|
||||
println!(" Key: {}", c_override.key);
|
||||
println!("Value: {}", match &c_override.value {
|
||||
HostConfigOverrideDataOverrideValue::Boolean(v) => format!("bool:{}", v),
|
||||
HostConfigOverrideDataOverrideValue::Numeric(v) => format!("numeric:{}", v),
|
||||
HostConfigOverrideDataOverrideValue::Other(v) => format!("string:{}", v)
|
||||
});
|
||||
}
|
||||
|
||||
if resp.data.overrides.is_empty() {
|
||||
println!("No overrides found");
|
||||
}
|
||||
|
||||
println!("Override set successfully");
|
||||
} else {
|
||||
let resp: APIErrorResponse = res.json().await?;
|
||||
|
||||
eprintln!("[error] Error setting config overrides: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
} else {
|
||||
let resp: APIErrorResponse = res.json().await?;
|
||||
|
||||
eprintln!("[error] Error setting config overrides: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn unset_override(id: String, key: String, server: Url) -> Result<(), Box<dyn Error>> {
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
// load session token
|
||||
let sess_token_store = dirs::config_dir().unwrap().join("tfcli-session.token");
|
||||
let session_token = fs::read_to_string(&sess_token_store)?;
|
||||
let auth_token_store = dirs::config_dir().unwrap().join("tfcli-auth.token");
|
||||
let auth_token = fs::read_to_string(&auth_token_store)?;
|
||||
|
||||
let token = format!("{} {}", session_token, auth_token);
|
||||
|
||||
let res = client.get(server.join(&format!("/v1/hosts/{}/config-overrides", id))?).bearer_auth(token.clone()).send().await?;
|
||||
|
||||
if res.status().is_success() {
|
||||
let resp: HostConfigOverrideResponse = res.json().await?;
|
||||
|
||||
let mut others: Vec<HostConfigOverrideDataOverride> = vec![];
|
||||
|
||||
for c_override in resp.data.overrides {
|
||||
if c_override.key != key {
|
||||
others.push(c_override);
|
||||
}
|
||||
}
|
||||
|
||||
let res = client.put(server.join(&format!("/v1/hosts/{}/config-overrides", id))?).bearer_auth(token.clone()).json(&SetOverrideRequest {
|
||||
overrides: others,
|
||||
}).send().await?;
|
||||
|
||||
if res.status().is_success() {
|
||||
let resp: HostConfigOverrideResponse = res.json().await?;
|
||||
|
||||
for c_override in &resp.data.overrides {
|
||||
println!(" Key: {}", c_override.key);
|
||||
println!("Value: {}", match &c_override.value {
|
||||
HostConfigOverrideDataOverrideValue::Boolean(v) => format!("bool:{}", v),
|
||||
HostConfigOverrideDataOverrideValue::Numeric(v) => format!("numeric:{}", v),
|
||||
HostConfigOverrideDataOverrideValue::Other(v) => format!("string:{}", v)
|
||||
});
|
||||
}
|
||||
|
||||
if resp.data.overrides.is_empty() {
|
||||
println!("No overrides found");
|
||||
}
|
||||
|
||||
println!("Override unset successfully");
|
||||
} else {
|
||||
let resp: APIErrorResponse = res.json().await?;
|
||||
|
||||
eprintln!("[error] Error unsetting config overrides: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
} else {
|
||||
let resp: APIErrorResponse = res.json().await?;
|
||||
|
||||
eprintln!("[error] Error unsetting config overrides: {} {}", resp.errors[0].code, resp.errors[0].message);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -203,6 +203,40 @@ pub enum HostCommands {
|
|||
Enroll {
|
||||
#[clap(short, long)]
|
||||
id: String
|
||||
},
|
||||
/// Manage config overrides set on the host
|
||||
Overrides {
|
||||
#[command(subcommand)]
|
||||
command: HostOverrideCommands
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum HostOverrideCommands {
|
||||
/// List the config overrides set on the host
|
||||
List {
|
||||
#[clap(short, long)]
|
||||
id: String
|
||||
},
|
||||
/// Set a config override on the host
|
||||
Set {
|
||||
#[clap(short, long)]
|
||||
id: String,
|
||||
#[clap(short, long)]
|
||||
key: String,
|
||||
#[clap(short, long)]
|
||||
boolean: Option<bool>,
|
||||
#[clap(short, long)]
|
||||
numeric: Option<i64>,
|
||||
#[clap(short, long)]
|
||||
string: Option<String>
|
||||
},
|
||||
/// Unset a config override on the host
|
||||
Unset {
|
||||
#[clap(short, long)]
|
||||
id: String,
|
||||
#[clap(short, long)]
|
||||
key: String
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -119,6 +119,8 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
|||
.service(routes::v2::enroll::enroll)
|
||||
.service(routes::v1::dnclient::dnclient)
|
||||
.service(routes::v2::whoami::whoami)
|
||||
.service(routes::v1::hosts::get_host_overrides)
|
||||
.service(routes::v1::hosts::update_host_overrides)
|
||||
})
|
||||
.bind(CONFIG.server.bind)?
|
||||
.run()
|
||||
|
|
|
@ -76,7 +76,8 @@ 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_static_address, network, organization};
|
||||
use trifid_api_entities::entity::{host, host_config_override, host_static_address, network, organization};
|
||||
use trifid_api_entities::entity::prelude::HostConfigOverride;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ListHostsRequestOpts {
|
||||
|
@ -2328,3 +2329,495 @@ pub async fn create_host_and_enrollment_code(
|
|||
metadata: CreateHostAndCodeResponseMetadata {},
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct HostConfigOverrideResponse {
|
||||
pub data: HostConfigOverrideData
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct HostConfigOverrideData {
|
||||
pub overrides: Vec<HostConfigOverrideDataOverride>
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct HostConfigOverrideDataOverride {
|
||||
pub key: String,
|
||||
pub value: HostConfigOverrideDataOverrideValue
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum HostConfigOverrideDataOverrideValue {
|
||||
Boolean(bool),
|
||||
Numeric(i64),
|
||||
Other(String)
|
||||
}
|
||||
|
||||
#[get("/v1/hosts/{host_id}/config-overrides")]
|
||||
pub async fn get_host_overrides(id: Path<String>, req_info: HttpRequest, db: Data<AppState>) -> HttpResponse {
|
||||
// For this endpoint, you either need to be a fully authenticated user OR a token with hosts:read
|
||||
let session_info = enforce_2fa(&req_info, &db.conn)
|
||||
.await
|
||||
.unwrap_or(TokenInfo::NotPresent);
|
||||
let api_token_info = enforce_api_token(&req_info, &["hosts:read"], &db.conn)
|
||||
.await
|
||||
.unwrap_or(TokenInfo::NotPresent);
|
||||
|
||||
// If neither are present, throw an error
|
||||
if matches!(session_info, TokenInfo::NotPresent)
|
||||
&& matches!(api_token_info, TokenInfo::NotPresent)
|
||||
{
|
||||
return HttpResponse::Unauthorized().json(APIErrorsResponse {
|
||||
errors: vec![
|
||||
APIError {
|
||||
code: "ERR_UNAUTHORIZED".to_string(),
|
||||
message: "This endpoint requires either a fully authenticated user or a token with the hosts:read scope".to_string(),
|
||||
path: None,
|
||||
}
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
// If both are present, throw an error
|
||||
if matches!(session_info, TokenInfo::AuthToken(_))
|
||||
&& matches!(api_token_info, TokenInfo::ApiToken(_))
|
||||
{
|
||||
return HttpResponse::BadRequest().json(APIErrorsResponse {
|
||||
errors: vec![
|
||||
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
|
||||
}
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
let org_id = match api_token_info {
|
||||
TokenInfo::ApiToken(tkn) => tkn.organization,
|
||||
_ => {
|
||||
// we have a session token, which means we have to do a db request to get the organization that this user owns
|
||||
let user = match session_info {
|
||||
TokenInfo::AuthToken(tkn) => tkn.session_info.user,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let org = match organization::Entity::find()
|
||||
.filter(organization::Column::Owner.eq(user.id))
|
||||
.one(&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 performing the database request, please try again later.".to_string(),
|
||||
path: None,
|
||||
}
|
||||
],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(org) = org {
|
||||
org.id
|
||||
} else {
|
||||
return HttpResponse::Unauthorized().json(APIErrorsResponse {
|
||||
errors: vec![
|
||||
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
|
||||
}
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let net_id;
|
||||
|
||||
let net = match network::Entity::find()
|
||||
.filter(network::Column::Organization.eq(&org_id))
|
||||
.one(&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 performing the database request, please try again later.".to_string(),
|
||||
path: None,
|
||||
}
|
||||
],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
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,
|
||||
}],
|
||||
});
|
||||
}
|
||||
|
||||
let host = match host::Entity::find()
|
||||
.filter(host::Column::Id.eq(id.into_inner()))
|
||||
.one(&db.conn)
|
||||
.await
|
||||
{
|
||||
Ok(h) => h,
|
||||
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 with the database query. Please try again later."
|
||||
.to_string(),
|
||||
path: None,
|
||||
}],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let host = match host {
|
||||
Some(h) => h,
|
||||
None => {
|
||||
return HttpResponse::Unauthorized().json(APIErrorsResponse {
|
||||
errors: vec![APIError {
|
||||
code: "ERR_UNAUTHORIZED".to_string(),
|
||||
message:
|
||||
"This resource does not exist or you do not have permission to access it."
|
||||
.to_string(),
|
||||
path: None,
|
||||
}],
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
if host.network != net_id {
|
||||
return HttpResponse::Unauthorized().json(APIErrorsResponse {
|
||||
errors: vec![APIError {
|
||||
code: "ERR_UNAUTHORIZED".to_string(),
|
||||
message: "This resource does not exist or you do not have permission to access it."
|
||||
.to_string(),
|
||||
path: None,
|
||||
}],
|
||||
});
|
||||
}
|
||||
|
||||
let config_overrides = match trifid_api_entities::entity::host_config_override::Entity::find().filter(host_config_override::Column::Host.eq(host.id)).all(&db.conn).await {
|
||||
Ok(h) => h,
|
||||
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 with the database query. Please try again later."
|
||||
.to_string(),
|
||||
path: None,
|
||||
}],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let overrides: Vec<HostConfigOverrideDataOverride> = config_overrides.iter().map(|u| {
|
||||
let val;
|
||||
if u.value == "true" || u.value == "false" {
|
||||
val = HostConfigOverrideDataOverrideValue::Boolean(u.value == "true");
|
||||
} else if u.value.chars().all(|c| c.is_numeric()) {
|
||||
val = HostConfigOverrideDataOverrideValue::Numeric(u.value.parse().unwrap());
|
||||
} else {
|
||||
val = HostConfigOverrideDataOverrideValue::Other(u.value.clone());
|
||||
}
|
||||
HostConfigOverrideDataOverride {
|
||||
key: u.key.clone(),
|
||||
value: val,
|
||||
}
|
||||
}).collect();
|
||||
|
||||
HttpResponse::Ok().json(HostConfigOverrideResponse {
|
||||
data: HostConfigOverrideData {
|
||||
overrides,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct UpdateOverridesRequest {
|
||||
pub overrides: Vec<HostConfigOverrideDataOverride>
|
||||
}
|
||||
|
||||
#[put("/v1/hosts/{host_id}/config-overrides")]
|
||||
pub async fn update_host_overrides(id: Path<String>, req: Json<UpdateOverridesRequest>, req_info: HttpRequest, db: Data<AppState>) -> HttpResponse {
|
||||
// For this endpoint, you either need to be a fully authenticated user OR a token with hosts:read
|
||||
let session_info = enforce_2fa(&req_info, &db.conn)
|
||||
.await
|
||||
.unwrap_or(TokenInfo::NotPresent);
|
||||
let api_token_info = enforce_api_token(&req_info, &["hosts:read"], &db.conn)
|
||||
.await
|
||||
.unwrap_or(TokenInfo::NotPresent);
|
||||
|
||||
// If neither are present, throw an error
|
||||
if matches!(session_info, TokenInfo::NotPresent)
|
||||
&& matches!(api_token_info, TokenInfo::NotPresent)
|
||||
{
|
||||
return HttpResponse::Unauthorized().json(APIErrorsResponse {
|
||||
errors: vec![
|
||||
APIError {
|
||||
code: "ERR_UNAUTHORIZED".to_string(),
|
||||
message: "This endpoint requires either a fully authenticated user or a token with the hosts:read scope".to_string(),
|
||||
path: None,
|
||||
}
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
// If both are present, throw an error
|
||||
if matches!(session_info, TokenInfo::AuthToken(_))
|
||||
&& matches!(api_token_info, TokenInfo::ApiToken(_))
|
||||
{
|
||||
return HttpResponse::BadRequest().json(APIErrorsResponse {
|
||||
errors: vec![
|
||||
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
|
||||
}
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
let org_id = match api_token_info {
|
||||
TokenInfo::ApiToken(tkn) => tkn.organization,
|
||||
_ => {
|
||||
// we have a session token, which means we have to do a db request to get the organization that this user owns
|
||||
let user = match session_info {
|
||||
TokenInfo::AuthToken(tkn) => tkn.session_info.user,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let org = match organization::Entity::find()
|
||||
.filter(organization::Column::Owner.eq(user.id))
|
||||
.one(&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 performing the database request, please try again later.".to_string(),
|
||||
path: None,
|
||||
}
|
||||
],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(org) = org {
|
||||
org.id
|
||||
} else {
|
||||
return HttpResponse::Unauthorized().json(APIErrorsResponse {
|
||||
errors: vec![
|
||||
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
|
||||
}
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let net_id;
|
||||
|
||||
let net = match network::Entity::find()
|
||||
.filter(network::Column::Organization.eq(&org_id))
|
||||
.one(&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 performing the database request, please try again later.".to_string(),
|
||||
path: None,
|
||||
}
|
||||
],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
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,
|
||||
}],
|
||||
});
|
||||
}
|
||||
|
||||
let host = match host::Entity::find()
|
||||
.filter(host::Column::Id.eq(id.into_inner()))
|
||||
.one(&db.conn)
|
||||
.await
|
||||
{
|
||||
Ok(h) => h,
|
||||
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 with the database query. Please try again later."
|
||||
.to_string(),
|
||||
path: None,
|
||||
}],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let host = match host {
|
||||
Some(h) => h,
|
||||
None => {
|
||||
return HttpResponse::Unauthorized().json(APIErrorsResponse {
|
||||
errors: vec![APIError {
|
||||
code: "ERR_UNAUTHORIZED".to_string(),
|
||||
message:
|
||||
"This resource does not exist or you do not have permission to access it."
|
||||
.to_string(),
|
||||
path: None,
|
||||
}],
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
if host.network != net_id {
|
||||
return HttpResponse::Unauthorized().json(APIErrorsResponse {
|
||||
errors: vec![APIError {
|
||||
code: "ERR_UNAUTHORIZED".to_string(),
|
||||
message: "This resource does not exist or you do not have permission to access it."
|
||||
.to_string(),
|
||||
path: None,
|
||||
}],
|
||||
});
|
||||
}
|
||||
|
||||
let config_overrides = match trifid_api_entities::entity::host_config_override::Entity::find().filter(host_config_override::Column::Host.eq(&host.id)).all(&db.conn).await {
|
||||
Ok(h) => h,
|
||||
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 with the database query. Please try again later."
|
||||
.to_string(),
|
||||
path: None,
|
||||
}],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
for c_override in config_overrides {
|
||||
match c_override.delete(&db.conn).await {
|
||||
Ok(_) => (),
|
||||
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 with the database query. Please try again later."
|
||||
.to_string(),
|
||||
path: None,
|
||||
}],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for c_override in &req.overrides {
|
||||
let db_override = host_config_override::Model {
|
||||
id: random_id("override"),
|
||||
key: c_override.key.clone(),
|
||||
value: match &c_override.value {
|
||||
HostConfigOverrideDataOverrideValue::Boolean(v) => v.to_string(),
|
||||
HostConfigOverrideDataOverrideValue::Numeric(v) => v.to_string(),
|
||||
HostConfigOverrideDataOverrideValue::Other(v) => v.clone(),
|
||||
},
|
||||
host: host.id.clone(),
|
||||
};
|
||||
match db_override.into_active_model().insert(&db.conn).await {
|
||||
Ok(_) => (),
|
||||
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 with the database query. Please try again later."
|
||||
.to_string(),
|
||||
path: None,
|
||||
}],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let config_overrides = match trifid_api_entities::entity::host_config_override::Entity::find().filter(host_config_override::Column::Host.eq(&host.id)).all(&db.conn).await {
|
||||
Ok(h) => h,
|
||||
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 with the database query. Please try again later."
|
||||
.to_string(),
|
||||
path: None,
|
||||
}],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let overrides: Vec<HostConfigOverrideDataOverride> = config_overrides.iter().map(|u| {
|
||||
let val;
|
||||
if u.value == "true" || u.value == "false" {
|
||||
val = HostConfigOverrideDataOverrideValue::Boolean(u.value == "true");
|
||||
} else if u.value.chars().all(|c| c.is_numeric()) || u.value.starts_with('-') && u.value.chars().collect::<Vec<_>>()[1..].iter().all(|c| c.is_numeric()) {
|
||||
val = HostConfigOverrideDataOverrideValue::Numeric(u.value.parse().unwrap());
|
||||
} else {
|
||||
val = HostConfigOverrideDataOverrideValue::Other(u.value.clone());
|
||||
}
|
||||
HostConfigOverrideDataOverride {
|
||||
key: u.key.clone(),
|
||||
value: val,
|
||||
}
|
||||
}).collect();
|
||||
|
||||
HttpResponse::Ok().json(HostConfigOverrideResponse {
|
||||
data: HostConfigOverrideData {
|
||||
overrides,
|
||||
},
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue