trifid/tfcli/src/role.rs

414 lines
12 KiB
Rust
Raw Normal View History

use crate::api::APIErrorResponse;
use crate::{RoleCommands, TableStyle};
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 std::error::Error;
use std::fs;
2023-06-16 02:00:09 +00:00
use url::Url;
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,
RoleCommands::Lookup { id } => get_role(id, server).await,
RoleCommands::Create {
name,
description,
rules_json,
} => create_role(name, description, rules_json, server).await,
2023-06-16 02:00:09 +00:00
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())
);
2023-09-30 21:48:29 +00:00
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()
}
);
2023-09-30 21:48:29 +00:00
}
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);
}
2023-09-30 21:48:29 +00:00
};
table.set_header(vec![
"ID",
"Name",
"Description",
"Rule Count",
"Created",
"Updated",
]);
2023-09-30 21:48:29 +00:00
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
}