fix updating, PUT /v1/hosts/host
This commit is contained in:
parent
eaa1e86fef
commit
a068741986
|
@ -99,6 +99,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
.service(routes::v1::hosts::create_hosts_request)
|
.service(routes::v1::hosts::create_hosts_request)
|
||||||
.service(routes::v1::hosts::get_host)
|
.service(routes::v1::hosts::get_host)
|
||||||
.service(routes::v1::hosts::delete_host)
|
.service(routes::v1::hosts::delete_host)
|
||||||
|
.service(routes::v1::hosts::edit_host)
|
||||||
}).bind(CONFIG.server.bind)?.run().await?;
|
}).bind(CONFIG.server.bind)?.run().await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -33,21 +33,29 @@
|
||||||
// This endpoint has full parity with the original API. It has been recreated from the original API documentation.
|
// This endpoint has full parity with the original API. It has been recreated from the original API documentation.
|
||||||
// This endpoint is considered done. No major features should be added or removed, unless it fixes bugs.
|
// This endpoint is considered done. No major features should be added or removed, unless it fixes bugs.
|
||||||
// This endpoint requires the `definednetworking` extension to be enabled to be used.
|
// This endpoint requires the `definednetworking` extension to be enabled to be used.
|
||||||
|
//
|
||||||
|
//#PUT /v1/hosts/{host_id} t+parity:full t+type:documented t+status:done t+feature:definednetworking t+ext:t+feature:extended_hosts
|
||||||
|
// This endpoint has full parity with the original API. It has been recreated from the original API documentation.
|
||||||
|
// This endpoint is considered done. No major features should be added or removed, unless it fixes bugs.
|
||||||
|
// This endpoint requires the `definednetworking` extension to be enabled to be used.
|
||||||
|
// This endpoint has additional functionality enabled by the extended_hosts feature flag.
|
||||||
|
|
||||||
use std::net::{Ipv4Addr, SocketAddrV4};
|
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
use actix_web::{HttpRequest, HttpResponse, get, post, delete};
|
use actix_web::{HttpRequest, HttpResponse, get, post, delete, put};
|
||||||
use actix_web::web::{Data, Json, Path, Query};
|
use actix_web::web::{Data, Json, Path, Query};
|
||||||
use chrono::{TimeZone, Utc};
|
use chrono::{TimeZone, Utc};
|
||||||
use log::error;
|
use log::{debug, error};
|
||||||
use sea_orm::{EntityTrait, QueryFilter, ColumnTrait, QueryOrder, PaginatorTrait, IntoActiveModel, ActiveModelTrait, ModelTrait};
|
use sea_orm::{EntityTrait, QueryFilter, ColumnTrait, QueryOrder, PaginatorTrait, IntoActiveModel, ActiveModelTrait, ModelTrait};
|
||||||
|
use sea_orm::ActiveValue::Set;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use trifid_api_entities::entity::{host, host_static_address, network, organization};
|
use trifid_api_entities::entity::{host, host_static_address, network, organization};
|
||||||
use crate::AppState;
|
use crate::AppState;
|
||||||
use crate::auth_tokens::{enforce_2fa, enforce_api_token, TokenInfo};
|
use crate::auth_tokens::{enforce_2fa, enforce_api_token, TokenInfo};
|
||||||
use crate::cursor::Cursor;
|
use crate::cursor::Cursor;
|
||||||
use crate::error::{APIError, APIErrorsResponse};
|
use crate::error::{APIError, APIErrorsResponse};
|
||||||
|
use crate::routes::v1::trifid::SUPPORTED_EXTENSIONS;
|
||||||
use crate::timers::TIME_FORMAT;
|
use crate::timers::TIME_FORMAT;
|
||||||
use crate::tokens::random_id;
|
use crate::tokens::random_id;
|
||||||
|
|
||||||
|
@ -1044,3 +1052,298 @@ pub async fn delete_host(id: Path<String>, req_info: HttpRequest, db: Data<AppSt
|
||||||
metadata: DeleteHostMetadata {},
|
metadata: DeleteHostMetadata {},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct EditHostRequest {
|
||||||
|
#[serde(rename = "staticAddresses")]
|
||||||
|
pub static_addresses: Vec<SocketAddrV4>,
|
||||||
|
#[serde(rename = "listenPort")]
|
||||||
|
pub listen_port: u16,
|
||||||
|
// t+features:extended_hosts
|
||||||
|
pub name: Option<String>,
|
||||||
|
// t+features:extended_hosts
|
||||||
|
pub ip: Option<Ipv4Addr>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct EditHostExtensionQuery {
|
||||||
|
pub extension: Option<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct EditHostResponse {
|
||||||
|
pub data: HostResponse,
|
||||||
|
pub metadata: EditHostResponseMetadata
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct EditHostResponseMetadata {}
|
||||||
|
|
||||||
|
#[put("/v1/hosts/{host_id}")]
|
||||||
|
pub async fn edit_host(id: Path<String>, query: Query<EditHostExtensionQuery>, req: Json<EditHostRequest>, req_info: HttpRequest, db: Data<AppState>) -> HttpResponse {
|
||||||
|
// For this endpoint, you either need to be a fully authenticated user OR a token with hosts:edit
|
||||||
|
let session_info = enforce_2fa(&req_info, &db.conn).await.unwrap_or(TokenInfo::NotPresent);
|
||||||
|
let api_token_info = enforce_api_token(&req_info, &["hosts:edit"], &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:edit 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 static_addresses = match host_static_address::Entity::find().filter(host_static_address::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 address in static_addresses {
|
||||||
|
match address.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
|
||||||
|
}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut host_clone = host.clone();
|
||||||
|
let mut host_active_model = host_clone.into_active_model();
|
||||||
|
|
||||||
|
host_active_model.listen_port = Set(req.listen_port as i32);
|
||||||
|
|
||||||
|
debug!("{:?} {} {:?} {:?} {}", query.extension, SUPPORTED_EXTENSIONS.contains(&"extended_hosts"), req.name, req.ip, query.extension == Some("extended_hosts".to_string()));
|
||||||
|
|
||||||
|
if query.extension == Some("extended_hosts".to_string()) && SUPPORTED_EXTENSIONS.contains(&"extended_hosts") {
|
||||||
|
if let Some(new_host_name) = req.name.clone() {
|
||||||
|
debug!("updated host name");
|
||||||
|
host_active_model.name = Set(new_host_name);
|
||||||
|
}
|
||||||
|
if let Some(new_host_ip) = req.ip {
|
||||||
|
debug!("updated host ip");
|
||||||
|
host_active_model.ip = Set(new_host_ip.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let host = match host_active_model.update(&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 static_addresses: Vec<host_static_address::Model> = req.static_addresses.iter().map(|u| {
|
||||||
|
host_static_address::Model {
|
||||||
|
id: random_id("hsaddress"),
|
||||||
|
host: host.id.clone(),
|
||||||
|
address: u.to_string(),
|
||||||
|
}
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
for rule in &static_addresses {
|
||||||
|
let active_model = rule.clone().into_active_model();
|
||||||
|
match 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 creating the new host. Please try again later".to_string(),
|
||||||
|
path: None
|
||||||
|
}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpResponse::Ok().json(EditHostResponse {
|
||||||
|
data: HostResponse {
|
||||||
|
id: host.id,
|
||||||
|
organization_id: org_id,
|
||||||
|
network_id: net_id,
|
||||||
|
role_id: host.role,
|
||||||
|
name: host.name,
|
||||||
|
ip_address: host.ip.to_string(),
|
||||||
|
static_addresses: req.static_addresses.clone(),
|
||||||
|
listen_port: host.listen_port as u16,
|
||||||
|
is_lighthouse: host.is_lighthouse,
|
||||||
|
is_relay: host.is_relay,
|
||||||
|
created_at: Utc.timestamp_opt(host.created_at, 0).unwrap().format(TIME_FORMAT).to_string(),
|
||||||
|
is_blocked: host.is_blocked,
|
||||||
|
metadata: HostResponseMetadata {
|
||||||
|
last_seen_at: Some(Utc.timestamp_opt(host.last_seen_at, 0).unwrap().format(TIME_FORMAT).to_string()),
|
||||||
|
version: host.last_version.to_string(),
|
||||||
|
platform: host.last_platform,
|
||||||
|
update_available: host.last_out_of_date,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
metadata: EditHostResponseMetadata {},
|
||||||
|
})
|
||||||
|
}
|
|
@ -51,6 +51,7 @@ use trifid_api_entities::entity::role;
|
||||||
use crate::cursor::Cursor;
|
use crate::cursor::Cursor;
|
||||||
use crate::tokens::random_id;
|
use crate::tokens::random_id;
|
||||||
use actix_web::delete;
|
use actix_web::delete;
|
||||||
|
use sea_orm::ActiveValue::Set;
|
||||||
use crate::timers::TIME_FORMAT;
|
use crate::timers::TIME_FORMAT;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
@ -770,9 +771,15 @@ pub struct RoleDeleteResponseData {}
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct RoleDeleteResponseMetadata {}
|
pub struct RoleDeleteResponseMetadata {}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct RoleUpdateRequest {
|
||||||
|
pub description: String,
|
||||||
|
#[serde(rename = "firewallRules")]
|
||||||
|
pub firewall_rules: Vec<RoleFirewallRule>
|
||||||
|
}
|
||||||
|
|
||||||
#[put("/v1/roles/{role_id}")]
|
#[put("/v1/roles/{role_id}")]
|
||||||
pub async fn update_role_request(role: Path<String>, req: Json<CreateRoleRequest>, req_info: HttpRequest, db: Data<AppState>) -> HttpResponse {
|
pub async fn update_role_request(role: Path<String>, req: Json<RoleUpdateRequest>, req_info: HttpRequest, db: Data<AppState>) -> HttpResponse {
|
||||||
// For this endpoint, you either need to be a fully authenticated user OR a token with roles:create
|
// For this endpoint, you either need to be a fully authenticated user OR a token with roles:create
|
||||||
let session_info = enforce_2fa(&req_info, &db.conn).await.unwrap_or(TokenInfo::NotPresent);
|
let session_info = enforce_2fa(&req_info, &db.conn).await.unwrap_or(TokenInfo::NotPresent);
|
||||||
let api_token_info = enforce_api_token(&req_info, &["roles:create"], &db.conn).await.unwrap_or(TokenInfo::NotPresent);
|
let api_token_info = enforce_api_token(&req_info, &["roles:create"], &db.conn).await.unwrap_or(TokenInfo::NotPresent);
|
||||||
|
@ -878,19 +885,62 @@ pub async fn update_role_request(role: Path<String>, req: Json<CreateRoleRequest
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_role_model = role::Model {
|
let role = match role::Entity::find().filter(role::Column::Id.eq(role.as_str())).one(&db.conn).await {
|
||||||
id: role.into_inner(),
|
Ok(r) => r,
|
||||||
name: req.name.clone(),
|
Err(e) => {
|
||||||
description: req.description.clone(),
|
error!("database error: {}", e);
|
||||||
organization: org,
|
return HttpResponse::InternalServerError().json(APIErrorsResponse {
|
||||||
created_at: SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards").as_secs() as i64,
|
errors: vec![
|
||||||
modified_at: SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards").as_secs() as i64,
|
APIError {
|
||||||
|
code: "ERR_DB_ERROR".to_string(),
|
||||||
|
message: "There was an error performing the database request, please try again later.".to_string(),
|
||||||
|
path: None,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let role = match role {
|
||||||
|
Some(r) => r,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut role_active_model = role.clone().into_active_model();
|
||||||
|
|
||||||
|
role_active_model.modified_at = Set(SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards").as_secs() as i64);
|
||||||
|
role_active_model.description = Set(req.description.clone());
|
||||||
|
|
||||||
|
let role = match role_active_model.update(&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,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let firewall_rules: Vec<firewall_rule::Model> = req.firewall_rules.iter().map(|i| {
|
let firewall_rules: Vec<firewall_rule::Model> = req.firewall_rules.iter().map(|i| {
|
||||||
firewall_rule::Model {
|
firewall_rule::Model {
|
||||||
id: random_id("rule"),
|
id: random_id("rule"),
|
||||||
role: new_role_model.id.clone(),
|
role: role.id.clone(),
|
||||||
protocol: i.protocol.to_string(),
|
protocol: i.protocol.to_string(),
|
||||||
description: i.description.clone(),
|
description: i.description.clone(),
|
||||||
allowed_role_id: i.allowed_role_id.clone(),
|
allowed_role_id: i.allowed_role_id.clone(),
|
||||||
|
@ -899,26 +949,8 @@ pub async fn update_role_request(role: Path<String>, req: Json<CreateRoleRequest
|
||||||
}
|
}
|
||||||
}).collect();
|
}).collect();
|
||||||
|
|
||||||
let new_role_model_clone = new_role_model.clone();
|
|
||||||
let firewall_rules_clone = firewall_rules.clone();
|
let firewall_rules_clone = firewall_rules.clone();
|
||||||
|
|
||||||
let new_role_active_model = new_role_model.into_active_model();
|
|
||||||
match new_role_active_model.update(&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 creating the new role. Please try again later".to_string(),
|
|
||||||
path: None
|
|
||||||
}
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for rule in &firewall_rules_clone {
|
for rule in &firewall_rules_clone {
|
||||||
let active_model = rule.clone().into_active_model();
|
let active_model = rule.clone().into_active_model();
|
||||||
match active_model.insert(&db.conn).await {
|
match active_model.insert(&db.conn).await {
|
||||||
|
@ -940,12 +972,12 @@ pub async fn update_role_request(role: Path<String>, req: Json<CreateRoleRequest
|
||||||
|
|
||||||
HttpResponse::Ok().json(RoleCreateResponse {
|
HttpResponse::Ok().json(RoleCreateResponse {
|
||||||
data: RoleResponse {
|
data: RoleResponse {
|
||||||
id: Some(new_role_model_clone.id.clone()),
|
id: Some(role.id.clone()),
|
||||||
name: Some(new_role_model_clone.name.clone()),
|
name: Some(role.name.clone()),
|
||||||
description: Some(new_role_model_clone.description),
|
description: Some(role.description),
|
||||||
firewall_rules: req.firewall_rules.clone(),
|
firewall_rules: req.firewall_rules.clone(),
|
||||||
created_at: Utc.timestamp_opt(new_role_model_clone.created_at, 0).unwrap().format(TIME_FORMAT).to_string(),
|
created_at: Utc.timestamp_opt(role.created_at, 0).unwrap().format(TIME_FORMAT).to_string(),
|
||||||
modified_at: Utc.timestamp_opt(new_role_model_clone.modified_at, 0).unwrap().format(TIME_FORMAT).to_string(),
|
modified_at: Utc.timestamp_opt(role.modified_at, 0).unwrap().format(TIME_FORMAT).to_string(),
|
||||||
},
|
},
|
||||||
metadata: RoleCreateResponseMetadata {},
|
metadata: RoleCreateResponseMetadata {},
|
||||||
})
|
})
|
||||||
|
|
|
@ -35,6 +35,8 @@
|
||||||
use actix_web::{HttpResponse, get};
|
use actix_web::{HttpResponse, get};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub const SUPPORTED_EXTENSIONS: &[&str] = &["definednetworking", "trifidextensions", "extended_roles", "extended_hosts"];
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct TrifidExtensionsResponse {
|
pub struct TrifidExtensionsResponse {
|
||||||
pub extensions: Vec<String>
|
pub extensions: Vec<String>
|
||||||
|
@ -43,6 +45,6 @@ pub struct TrifidExtensionsResponse {
|
||||||
#[get("/v1/trifid_extensions")]
|
#[get("/v1/trifid_extensions")]
|
||||||
pub async fn trifid_extensions() -> HttpResponse {
|
pub async fn trifid_extensions() -> HttpResponse {
|
||||||
HttpResponse::Ok().json(TrifidExtensionsResponse {
|
HttpResponse::Ok().json(TrifidExtensionsResponse {
|
||||||
extensions: vec!["definednetworking".to_string(), "trifidextensions".to_string(), "extended_roles".to_string(), "extended_hosts".to_string()],
|
extensions: SUPPORTED_EXTENSIONS.iter().map(|u| u.to_string()).collect(),
|
||||||
})
|
})
|
||||||
}
|
}
|
|
@ -42,12 +42,20 @@ impl MigrationTrait for Migration {
|
||||||
)
|
)
|
||||||
.index(
|
.index(
|
||||||
Index::create()
|
Index::create()
|
||||||
.name("idx-hosts-id-name-unique")
|
.name("idx-hosts-net-name-unique")
|
||||||
.table(Host::Table)
|
.table(Host::Table)
|
||||||
.col(Host::Id)
|
.col(Host::Network)
|
||||||
.col(Host::Name)
|
.col(Host::Name)
|
||||||
.unique()
|
.unique()
|
||||||
)
|
)
|
||||||
|
.index(
|
||||||
|
Index::create()
|
||||||
|
.name("idx-hosts-net-ip-unique")
|
||||||
|
.table(Host::Table)
|
||||||
|
.col(Host::Network)
|
||||||
|
.col(Host::IP)
|
||||||
|
.unique()
|
||||||
|
)
|
||||||
.to_owned()
|
.to_owned()
|
||||||
).await
|
).await
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue