2023-06-16 02:00:09 +00:00
|
|
|
use std::error::Error;
|
|
|
|
use std::fs;
|
2023-09-30 21:48:29 +00:00
|
|
|
use comfy_table::modifiers::UTF8_ROUND_CORNERS;
|
|
|
|
use comfy_table::presets::UTF8_FULL;
|
|
|
|
use comfy_table::Table;
|
2023-06-16 02:00:09 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use url::Url;
|
|
|
|
use crate::api::APIErrorResponse;
|
2023-09-30 21:48:29 +00:00
|
|
|
use crate::{RoleCommands, TableStyle};
|
2023-06-16 02:00:09 +00:00
|
|
|
|
|
|
|
pub async fn role_main(command: RoleCommands, server: Url) -> Result<(), Box<dyn Error>> {
|
|
|
|
match command {
|
2023-09-30 21:48:29 +00:00
|
|
|
RoleCommands::List { table_style } => list_roles(server, table_style).await,
|
2023-06-16 02:00:09 +00:00
|
|
|
RoleCommands::Lookup {id} => get_role(id, server).await,
|
|
|
|
RoleCommands::Create { name, description, rules_json } => create_role(name, description, rules_json, server).await,
|
|
|
|
RoleCommands::Delete { id } => delete_role(id, server).await,
|
2023-11-23 20:23:52 +00:00
|
|
|
RoleCommands::Update {
|
|
|
|
id,
|
|
|
|
description,
|
|
|
|
rules_json,
|
|
|
|
} => update_role(id, description, rules_json, server).await,
|
2023-06-16 02:00:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
pub struct RoleListResp {
|
2023-11-23 20:23:52 +00:00
|
|
|
pub data: Vec<Role>,
|
2023-06-16 02:00:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
pub struct Role {
|
|
|
|
pub id: String,
|
|
|
|
pub name: String,
|
|
|
|
pub description: String,
|
|
|
|
#[serde(rename = "firewallRules")]
|
|
|
|
pub firewall_rules: Vec<RoleFirewallRule>,
|
|
|
|
#[serde(rename = "createdAt")]
|
|
|
|
pub created_at: String,
|
|
|
|
#[serde(rename = "modifiedAt")]
|
2023-11-23 20:23:52 +00:00
|
|
|
pub modified_at: String,
|
2023-06-16 02:00:09 +00:00
|
|
|
}
|
|
|
|
#[derive(Deserialize, Serialize)]
|
|
|
|
pub struct RoleFirewallRule {
|
|
|
|
pub protocol: String,
|
|
|
|
pub description: String,
|
|
|
|
#[serde(rename = "allowedRoleID")]
|
|
|
|
pub allowed_role_id: Option<String>,
|
|
|
|
#[serde(rename = "portRange")]
|
2023-11-23 20:23:52 +00:00
|
|
|
pub port_range: Option<RoleFirewallRulePortRange>,
|
2023-06-16 02:00:09 +00:00
|
|
|
}
|
|
|
|
#[derive(Deserialize, Serialize)]
|
|
|
|
pub struct RoleFirewallRulePortRange {
|
|
|
|
pub from: u16,
|
2023-11-23 20:23:52 +00:00
|
|
|
pub to: u16,
|
2023-06-16 02:00:09 +00:00
|
|
|
}
|
|
|
|
|
2023-09-30 21:48:29 +00:00
|
|
|
pub async fn list_roles(server: Url, table_style: TableStyle) -> Result<(), Box<dyn Error>> {
|
2023-06-16 02:00:09 +00:00
|
|
|
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);
|
|
|
|
|
2023-11-23 20:23:52 +00:00
|
|
|
let res = client
|
|
|
|
.get(server.join("/v1/roles")?)
|
|
|
|
.bearer_auth(token)
|
|
|
|
.send()
|
|
|
|
.await?;
|
2023-06-16 02:00:09 +00:00
|
|
|
|
|
|
|
if res.status().is_success() {
|
|
|
|
let resp: RoleListResp = res.json().await?;
|
|
|
|
|
2023-09-30 21:48:29 +00:00
|
|
|
if resp.data.is_empty() {
|
|
|
|
println!("No roles found");
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
if matches!(table_style, TableStyle::List) {
|
|
|
|
for role in &resp.data {
|
|
|
|
println!(" Role: {} ({})", role.name, role.id);
|
|
|
|
println!(" Description: {}", role.description);
|
|
|
|
for rule in &role.firewall_rules {
|
|
|
|
println!("Rule Description: {}", rule.description);
|
|
|
|
println!(" Allowed Role: {}", rule.allowed_role_id.as_ref().unwrap_or(&"All roles".to_string()));
|
|
|
|
println!(" Protocol: {}", rule.protocol);
|
|
|
|
println!(" Port Range: {}", if let Some(pr) = rule.port_range.as_ref() { format!("{}-{}", pr.from, pr.to) } else { "Any".to_string() });
|
|
|
|
}
|
|
|
|
println!(" Created: {}", role.created_at);
|
|
|
|
println!(" Updated: {}", role.modified_at);
|
2023-06-16 02:00:09 +00:00
|
|
|
}
|
2023-09-30 21:48:29 +00:00
|
|
|
return Ok(());
|
2023-06-16 02:00:09 +00:00
|
|
|
}
|
|
|
|
|
2023-09-30 21:48:29 +00:00
|
|
|
let mut table = Table::new();
|
|
|
|
match table_style {
|
|
|
|
TableStyle::List => unreachable!(),
|
|
|
|
TableStyle::Basic => (),
|
|
|
|
TableStyle::Pretty => { table.load_preset(UTF8_FULL).apply_modifier(UTF8_ROUND_CORNERS); },
|
|
|
|
};
|
|
|
|
|
|
|
|
table.set_header(vec!["ID", "Name", "Description", "Rule Count", "Created", "Updated"]);
|
|
|
|
for role in &resp.data {
|
|
|
|
table.add_row(vec![&role.id, &role.name, &role.description, role.firewall_rules.len().to_string().as_str(), &role.created_at, &role.modified_at]);
|
2023-06-16 02:00:09 +00:00
|
|
|
}
|
2023-09-30 21:48:29 +00:00
|
|
|
|
|
|
|
println!("{table}");
|
2023-06-16 02:00:09 +00:00
|
|
|
} else {
|
|
|
|
let resp: APIErrorResponse = res.json().await?;
|
|
|
|
|
2023-11-23 20:23:52 +00:00
|
|
|
eprintln!(
|
|
|
|
"[error] Error listing roles: {} {}",
|
|
|
|
resp.errors[0].code, resp.errors[0].message
|
|
|
|
);
|
2023-06-16 02:00:09 +00:00
|
|
|
|
|
|
|
std::process::exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
pub struct RoleGetResponse {
|
2023-11-23 20:23:52 +00:00
|
|
|
pub data: Role,
|
2023-06-16 02:00:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn get_role(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);
|
|
|
|
|
2023-11-23 20:23:52 +00:00
|
|
|
let res = client
|
|
|
|
.get(server.join(&format!("/v1/roles/{}", id))?)
|
|
|
|
.bearer_auth(token)
|
|
|
|
.send()
|
|
|
|
.await?;
|
2023-06-16 02:00:09 +00:00
|
|
|
|
|
|
|
if res.status().is_success() {
|
|
|
|
let role: Role = res.json::<RoleGetResponse>().await?.data;
|
|
|
|
|
|
|
|
println!(" Role: {} ({})", role.name, role.id);
|
|
|
|
println!(" Description: {}", role.description);
|
|
|
|
for rule in &role.firewall_rules {
|
|
|
|
println!("Rule Description: {}", rule.description);
|
2023-11-23 20:23:52 +00:00
|
|
|
println!(
|
|
|
|
" Allowed Role: {}",
|
|
|
|
rule.allowed_role_id
|
|
|
|
.as_ref()
|
|
|
|
.unwrap_or(&"All roles".to_string())
|
|
|
|
);
|
2023-06-16 02:00:09 +00:00
|
|
|
println!(" Protocol: {}", rule.protocol);
|
2023-11-23 20:23:52 +00:00
|
|
|
println!(
|
|
|
|
" Port Range: {}",
|
|
|
|
if let Some(pr) = rule.port_range.as_ref() {
|
|
|
|
format!("{}-{}", pr.from, pr.to)
|
|
|
|
} else {
|
|
|
|
"Any".to_string()
|
|
|
|
}
|
|
|
|
);
|
2023-06-16 02:00:09 +00:00
|
|
|
}
|
|
|
|
println!(" Created: {}", role.created_at);
|
|
|
|
println!(" Updated: {}", role.modified_at);
|
|
|
|
} else {
|
|
|
|
let resp: APIErrorResponse = res.json().await?;
|
|
|
|
|
2023-11-23 20:23:52 +00:00
|
|
|
eprintln!(
|
|
|
|
"[error] Error listing roles: {} {}",
|
|
|
|
resp.errors[0].code, resp.errors[0].message
|
|
|
|
);
|
2023-06-16 02:00:09 +00:00
|
|
|
|
|
|
|
std::process::exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize)]
|
|
|
|
pub struct RoleCreateBody {
|
|
|
|
pub name: String,
|
|
|
|
pub description: String,
|
|
|
|
#[serde(rename = "firewallRules")]
|
2023-11-23 20:23:52 +00:00
|
|
|
pub firewall_rules: Vec<RoleFirewallRule>,
|
2023-06-16 02:00:09 +00:00
|
|
|
}
|
|
|
|
|
2023-11-23 20:23:52 +00:00
|
|
|
pub async fn create_role(
|
|
|
|
name: String,
|
|
|
|
description: String,
|
|
|
|
rules_json: String,
|
|
|
|
server: Url,
|
|
|
|
) -> Result<(), Box<dyn Error>> {
|
2023-06-16 02:00:09 +00:00
|
|
|
let client = reqwest::Client::new();
|
|
|
|
|
|
|
|
let rules: Vec<RoleFirewallRule> = match serde_json::from_str(&rules_json) {
|
|
|
|
Ok(r) => r,
|
|
|
|
Err(e) => {
|
|
|
|
eprintln!("[error] error parsing rulesJson: {}", e);
|
|
|
|
std::process::exit(1);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
2023-11-23 20:23:52 +00:00
|
|
|
let res = client
|
|
|
|
.post(server.join("/v1/roles")?)
|
|
|
|
.json(&RoleCreateBody {
|
|
|
|
name,
|
|
|
|
description,
|
|
|
|
firewall_rules: rules,
|
|
|
|
})
|
|
|
|
.bearer_auth(token)
|
|
|
|
.send()
|
|
|
|
.await?;
|
2023-06-16 02:00:09 +00:00
|
|
|
|
|
|
|
if res.status().is_success() {
|
|
|
|
let role: Role = res.json::<RoleGetResponse>().await?.data;
|
|
|
|
|
|
|
|
println!(" Role: {} ({})", role.name, role.id);
|
|
|
|
println!(" Description: {}", role.description);
|
|
|
|
for rule in &role.firewall_rules {
|
|
|
|
println!("Rule Description: {}", rule.description);
|
2023-11-23 20:23:52 +00:00
|
|
|
println!(
|
|
|
|
" Allowed Role: {}",
|
|
|
|
rule.allowed_role_id
|
|
|
|
.as_ref()
|
|
|
|
.unwrap_or(&"All roles".to_string())
|
|
|
|
);
|
2023-06-16 02:00:09 +00:00
|
|
|
println!(" Protocol: {}", rule.protocol);
|
2023-11-23 20:23:52 +00:00
|
|
|
println!(
|
|
|
|
" Port Range: {}",
|
|
|
|
if let Some(pr) = rule.port_range.as_ref() {
|
|
|
|
format!("{}-{}", pr.from, pr.to)
|
|
|
|
} else {
|
|
|
|
"Any".to_string()
|
|
|
|
}
|
|
|
|
);
|
2023-06-16 02:00:09 +00:00
|
|
|
}
|
|
|
|
println!(" Created: {}", role.created_at);
|
|
|
|
println!(" Updated: {}", role.modified_at);
|
|
|
|
} else {
|
|
|
|
let resp: APIErrorResponse = res.json().await?;
|
|
|
|
|
2023-11-23 20:23:52 +00:00
|
|
|
eprintln!(
|
|
|
|
"[error] Error creating role: {} {}",
|
|
|
|
resp.errors[0].code, resp.errors[0].message
|
|
|
|
);
|
2023-06-16 02:00:09 +00:00
|
|
|
|
|
|
|
std::process::exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize)]
|
|
|
|
pub struct RoleUpdateBody {
|
|
|
|
pub description: String,
|
|
|
|
#[serde(rename = "firewallRules")]
|
2023-11-23 20:23:52 +00:00
|
|
|
pub firewall_rules: Vec<RoleFirewallRule>,
|
2023-06-16 02:00:09 +00:00
|
|
|
}
|
|
|
|
|
2023-11-23 20:23:52 +00:00
|
|
|
pub async fn update_role(
|
|
|
|
id: String,
|
|
|
|
description: String,
|
|
|
|
rules_json: String,
|
|
|
|
server: Url,
|
|
|
|
) -> Result<(), Box<dyn Error>> {
|
2023-06-16 02:00:09 +00:00
|
|
|
let client = reqwest::Client::new();
|
|
|
|
|
|
|
|
let rules: Vec<RoleFirewallRule> = match serde_json::from_str(&rules_json) {
|
|
|
|
Ok(r) => r,
|
|
|
|
Err(e) => {
|
|
|
|
eprintln!("[error] error parsing rulesJson: {}", e);
|
|
|
|
std::process::exit(1);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
2023-11-23 20:23:52 +00:00
|
|
|
let res = client
|
|
|
|
.put(server.join(&format!("/v1/roles/{}", id))?)
|
|
|
|
.json(&RoleUpdateBody {
|
|
|
|
description,
|
|
|
|
firewall_rules: rules,
|
|
|
|
})
|
|
|
|
.bearer_auth(token)
|
|
|
|
.send()
|
|
|
|
.await?;
|
2023-06-16 02:00:09 +00:00
|
|
|
|
|
|
|
if res.status().is_success() {
|
|
|
|
let role: Role = res.json::<RoleGetResponse>().await?.data;
|
|
|
|
|
|
|
|
println!(" Role: {} ({})", role.name, role.id);
|
|
|
|
println!(" Description: {}", role.description);
|
|
|
|
for rule in &role.firewall_rules {
|
|
|
|
println!("Rule Description: {}", rule.description);
|
2023-11-23 20:23:52 +00:00
|
|
|
println!(
|
|
|
|
" Allowed Role: {}",
|
|
|
|
rule.allowed_role_id
|
|
|
|
.as_ref()
|
|
|
|
.unwrap_or(&"All roles".to_string())
|
|
|
|
);
|
2023-06-16 02:00:09 +00:00
|
|
|
println!(" Protocol: {}", rule.protocol);
|
2023-11-23 20:23:52 +00:00
|
|
|
println!(
|
|
|
|
" Port Range: {}",
|
|
|
|
if let Some(pr) = rule.port_range.as_ref() {
|
|
|
|
format!("{}-{}", pr.from, pr.to)
|
|
|
|
} else {
|
|
|
|
"Any".to_string()
|
|
|
|
}
|
|
|
|
);
|
2023-06-16 02:00:09 +00:00
|
|
|
}
|
|
|
|
println!(" Created: {}", role.created_at);
|
|
|
|
println!(" Updated: {}", role.modified_at);
|
|
|
|
} else {
|
|
|
|
let resp: APIErrorResponse = res.json().await?;
|
|
|
|
|
2023-11-23 20:23:52 +00:00
|
|
|
eprintln!(
|
|
|
|
"[error] Error updating role: {} {}",
|
|
|
|
resp.errors[0].code, resp.errors[0].message
|
|
|
|
);
|
2023-06-16 02:00:09 +00:00
|
|
|
|
|
|
|
std::process::exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn delete_role(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);
|
|
|
|
|
2023-11-23 20:23:52 +00:00
|
|
|
let res = client
|
|
|
|
.delete(server.join(&format!("/v1/roles/{}", id))?)
|
|
|
|
.bearer_auth(token)
|
|
|
|
.send()
|
|
|
|
.await?;
|
2023-06-16 02:00:09 +00:00
|
|
|
|
|
|
|
if res.status().is_success() {
|
|
|
|
println!("Role removed");
|
|
|
|
} else {
|
|
|
|
let resp: APIErrorResponse = res.json().await?;
|
|
|
|
|
2023-11-23 20:23:52 +00:00
|
|
|
eprintln!(
|
|
|
|
"[error] Error removing role: {} {}",
|
|
|
|
resp.errors[0].code, resp.errors[0].message
|
|
|
|
);
|
2023-06-16 02:00:09 +00:00
|
|
|
|
|
|
|
std::process::exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
2023-11-23 20:23:52 +00:00
|
|
|
}
|