feat: added basic template management
This commit is contained in:
parent
71b2f70686
commit
ba5145c5ae
@ -5,7 +5,6 @@ use leptos_router::{
|
|||||||
hooks::use_query_map,
|
hooks::use_query_map,
|
||||||
path
|
path
|
||||||
};
|
};
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
|
|
||||||
use crate::users::User;
|
use crate::users::User;
|
||||||
|
|
||||||
@ -200,6 +199,16 @@ fn HomePage() -> impl IntoView {
|
|||||||
view! {
|
view! {
|
||||||
<main class="main">
|
<main class="main">
|
||||||
<h1>"Welcome to sparse!"</h1>
|
<h1>"Welcome to sparse!"</h1>
|
||||||
|
<p>"To get started:"</p>
|
||||||
|
<ol>
|
||||||
|
<li>"Sign in"</li>
|
||||||
|
<li>"Go to beacon management"</li>
|
||||||
|
<li>"Create a listener"</li>
|
||||||
|
<li>"(Optional) Create a category"</li>
|
||||||
|
<li>"Create a template"</li>
|
||||||
|
<li>"Download the installer"</li>
|
||||||
|
<li>"Run the installer on a target system"</li>
|
||||||
|
</ol>
|
||||||
</main>
|
</main>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,11 +8,17 @@ mod instances;
|
|||||||
mod listeners;
|
mod listeners;
|
||||||
mod templates;
|
mod templates;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub use categories::CategoriesView;
|
pub use categories::CategoriesView;
|
||||||
|
#[allow(dead_code)]
|
||||||
pub use commands::CommandsView;
|
pub use commands::CommandsView;
|
||||||
|
#[allow(dead_code)]
|
||||||
pub use configs::ConfigsView;
|
pub use configs::ConfigsView;
|
||||||
|
#[allow(dead_code)]
|
||||||
pub use instances::InstancesView;
|
pub use instances::InstancesView;
|
||||||
|
#[allow(dead_code)]
|
||||||
pub use listeners::ListenersView;
|
pub use listeners::ListenersView;
|
||||||
|
#[allow(dead_code)]
|
||||||
pub use templates::TemplatesView;
|
pub use templates::TemplatesView;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -24,10 +30,13 @@ pub struct BeaconResources {
|
|||||||
rename_category: ServerAction<categories::RenameCategory>,
|
rename_category: ServerAction<categories::RenameCategory>,
|
||||||
add_beacon_config: ServerAction<configs::AddBeaconConfig>,
|
add_beacon_config: ServerAction<configs::AddBeaconConfig>,
|
||||||
remove_beacon_config: ServerAction<configs::RemoveBeaconConfig>,
|
remove_beacon_config: ServerAction<configs::RemoveBeaconConfig>,
|
||||||
|
add_template: ServerAction<templates::AddTemplate>,
|
||||||
|
remove_template: ServerAction<templates::RemoveTemplate>,
|
||||||
|
|
||||||
listeners: Resource<Result<Vec<listeners::PubListener>, ServerFnError>>,
|
listeners: Resource<Result<Vec<listeners::PubListener>, ServerFnError>>,
|
||||||
categories: Resource<Result<Vec<categories::Category>, ServerFnError>>,
|
categories: Resource<Result<Vec<categories::Category>, ServerFnError>>,
|
||||||
configs: Resource<Result<Vec<configs::BeaconConfig>, ServerFnError>>
|
configs: Resource<Result<Vec<configs::BeaconConfig>, ServerFnError>>,
|
||||||
|
templates: Resource<Result<Vec<templates::BeaconTemplate>, ServerFnError>>
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn provide_beacon_resources() {
|
pub fn provide_beacon_resources() {
|
||||||
@ -43,6 +52,9 @@ pub fn provide_beacon_resources() {
|
|||||||
let add_beacon_config = ServerAction::<configs::AddBeaconConfig>::new();
|
let add_beacon_config = ServerAction::<configs::AddBeaconConfig>::new();
|
||||||
let remove_beacon_config = ServerAction::<configs::RemoveBeaconConfig>::new();
|
let remove_beacon_config = ServerAction::<configs::RemoveBeaconConfig>::new();
|
||||||
|
|
||||||
|
let add_template = ServerAction::<templates::AddTemplate>::new();
|
||||||
|
let remove_template = ServerAction::<templates::RemoveTemplate>::new();
|
||||||
|
|
||||||
let listeners = Resource::new(
|
let listeners = Resource::new(
|
||||||
move || (
|
move || (
|
||||||
user.get(),
|
user.get(),
|
||||||
@ -71,6 +83,15 @@ pub fn provide_beacon_resources() {
|
|||||||
|_| async { configs::get_beacon_configs().await }
|
|_| async { configs::get_beacon_configs().await }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let templates = Resource::new(
|
||||||
|
move || (
|
||||||
|
user.get(),
|
||||||
|
add_template.version().get(),
|
||||||
|
remove_template.version().get()
|
||||||
|
),
|
||||||
|
|_| async { templates::get_templates().await }
|
||||||
|
);
|
||||||
|
|
||||||
provide_context(BeaconResources {
|
provide_context(BeaconResources {
|
||||||
add_listener,
|
add_listener,
|
||||||
remove_listener,
|
remove_listener,
|
||||||
@ -79,10 +100,13 @@ pub fn provide_beacon_resources() {
|
|||||||
rename_category,
|
rename_category,
|
||||||
add_beacon_config,
|
add_beacon_config,
|
||||||
remove_beacon_config,
|
remove_beacon_config,
|
||||||
|
add_template,
|
||||||
|
remove_template,
|
||||||
|
|
||||||
listeners,
|
listeners,
|
||||||
categories,
|
categories,
|
||||||
configs
|
configs,
|
||||||
|
templates
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,8 +11,8 @@ use super::BeaconResources;
|
|||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct Category {
|
pub struct Category {
|
||||||
category_id: i64,
|
pub category_id: i64,
|
||||||
category_name: String
|
pub category_name: String
|
||||||
}
|
}
|
||||||
|
|
||||||
#[server]
|
#[server]
|
||||||
|
|||||||
@ -44,10 +44,10 @@ impl FromRow<'_, SqliteRow> for BeaconConfigTypes {
|
|||||||
#[cfg_attr(feature = "ssr", derive(FromRow))]
|
#[cfg_attr(feature = "ssr", derive(FromRow))]
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct BeaconConfig {
|
pub struct BeaconConfig {
|
||||||
config_id: i64,
|
pub config_id: i64,
|
||||||
config_name: String,
|
pub config_name: String,
|
||||||
#[cfg_attr(feature = "ssr", sqlx(flatten))]
|
#[cfg_attr(feature = "ssr", sqlx(flatten))]
|
||||||
config_type: BeaconConfigTypes,
|
pub config_type: BeaconConfigTypes,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[server]
|
#[server]
|
||||||
@ -135,7 +135,7 @@ pub async fn add_beacon_config(
|
|||||||
|
|
||||||
match &*cron_mode {
|
match &*cron_mode {
|
||||||
"local" | "utc" => {},
|
"local" | "utc" => {},
|
||||||
other => {
|
_ => {
|
||||||
return Err(ServerFnError::<NoCustomError>::ServerError("Unrecognized timezone specifier for cron".to_string()))
|
return Err(ServerFnError::<NoCustomError>::ServerError("Unrecognized timezone specifier for cron".to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,7 +159,22 @@ pub async fn add_beacon_config(
|
|||||||
|
|
||||||
#[server]
|
#[server]
|
||||||
pub async fn remove_beacon_config(id: i64) -> Result<(), ServerFnError> {
|
pub async fn remove_beacon_config(id: i64) -> Result<(), ServerFnError> {
|
||||||
unimplemented!()
|
let user = user::get_auth_session().await?;
|
||||||
|
|
||||||
|
if user.is_none() {
|
||||||
|
return Err(ServerFnError::<NoCustomError>::ServerError("You are not signed in!".to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let db = expect_context::<SqlitePool>();
|
||||||
|
|
||||||
|
sqlx::query!(
|
||||||
|
"DELETE FROM beacon_config WHERE config_id = ?",
|
||||||
|
id
|
||||||
|
)
|
||||||
|
.execute(&db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
|
|||||||
@ -7,8 +7,8 @@ use serde::{Serialize, Deserialize};
|
|||||||
use {
|
use {
|
||||||
sqlx::SqlitePool,
|
sqlx::SqlitePool,
|
||||||
leptos::server_fn::error::NoCustomError,
|
leptos::server_fn::error::NoCustomError,
|
||||||
crate::{db::user, beacon_handler::BeaconListenerMap},
|
|
||||||
rcgen::{generate_simple_self_signed, CertifiedKey},
|
rcgen::{generate_simple_self_signed, CertifiedKey},
|
||||||
|
crate::{db::user, beacon_handler::BeaconListenerMap},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::BeaconResources;
|
use super::BeaconResources;
|
||||||
@ -23,11 +23,11 @@ struct DbListener {
|
|||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
pub struct PubListener {
|
pub struct PubListener {
|
||||||
listener_id: i64,
|
pub listener_id: i64,
|
||||||
port: i64,
|
pub port: i64,
|
||||||
public_ip: String,
|
pub public_ip: String,
|
||||||
domain_name: String,
|
pub domain_name: String,
|
||||||
active: bool
|
pub active: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
#[server]
|
#[server]
|
||||||
|
|||||||
@ -1,10 +1,452 @@
|
|||||||
use leptos::prelude::*;
|
use leptos::{either::Either, prelude::*};
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
use {
|
||||||
|
std::net::Ipv4Addr,
|
||||||
|
|
||||||
|
sqlx::{sqlite::SqliteRow, FromRow, Row, SqlitePool},
|
||||||
|
leptos::server_fn::error::NoCustomError,
|
||||||
|
|
||||||
|
crate::db::user
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::beacons::BeaconResources;
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub enum BeaconSourceMode {
|
||||||
|
Host,
|
||||||
|
Custom(i64, String)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
impl FromRow<'_, SqliteRow> for BeaconSourceMode {
|
||||||
|
fn from_row(row: &SqliteRow) -> sqlx::Result<Self> {
|
||||||
|
match row.try_get("source_mode")? {
|
||||||
|
"host" => Ok(Self::Host),
|
||||||
|
"custom" => Ok(Self::Custom(
|
||||||
|
row.try_get("source_netmask")?,
|
||||||
|
row.try_get("source_gateway")?
|
||||||
|
)),
|
||||||
|
type_name => Err(sqlx::Error::TypeNotFound {
|
||||||
|
type_name: type_name.to_string(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "ssr", derive(FromRow))]
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub struct BeaconTemplate {
|
||||||
|
template_id: i64,
|
||||||
|
template_name: String,
|
||||||
|
operating_system: String,
|
||||||
|
|
||||||
|
source_ip: String,
|
||||||
|
source_mac: Option<String>,
|
||||||
|
#[cfg_attr(feature = "ssr", sqlx(flatten))]
|
||||||
|
source_mode: BeaconSourceMode,
|
||||||
|
|
||||||
|
config_id: i64,
|
||||||
|
listener_id: i64,
|
||||||
|
default_category: Option<i64>
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(feature = "ssr")] {
|
||||||
|
macro_rules! srverr {
|
||||||
|
($err:expr) => {
|
||||||
|
return Err(ServerFnError::<NoCustomError>::ServerError($err.to_owned()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[server]
|
||||||
|
pub async fn add_template(
|
||||||
|
template_name: String,
|
||||||
|
operating_system: String,
|
||||||
|
listener_id: i64,
|
||||||
|
config_id: i64,
|
||||||
|
default_category: i64,
|
||||||
|
source_ip: String,
|
||||||
|
source_mac: String,
|
||||||
|
source_mode: String,
|
||||||
|
source_netmask: i64,
|
||||||
|
source_gateway: String
|
||||||
|
) -> Result<(), ServerFnError> {
|
||||||
|
let user = user::get_auth_session().await?;
|
||||||
|
|
||||||
|
if user.is_none() {
|
||||||
|
srverr!("You are not signed in!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if listener_id == 0 {
|
||||||
|
srverr!("You must specify a listener");
|
||||||
|
}
|
||||||
|
|
||||||
|
if config_id == 0 {
|
||||||
|
srverr!("You must specify a configuration");
|
||||||
|
}
|
||||||
|
|
||||||
|
if source_ip.parse::<Ipv4Addr>().is_err() {
|
||||||
|
srverr!("Source IP address is formatted incorrectly");
|
||||||
|
}
|
||||||
|
|
||||||
|
if &*source_mode == "custom" && source_gateway.parse::<Ipv4Addr>().is_err() {
|
||||||
|
srverr!("Gateway address is formatted incorrectly");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mac_parts = source_mac.split(":").collect::<Vec<_>>();
|
||||||
|
if mac_parts.len() != 6 || mac_parts.iter().any(|p| p.len() != 2 || u8::from_str_radix(p, 16).is_err()) {
|
||||||
|
srverr!("Source MAC address is formatted incorrectly");
|
||||||
|
}
|
||||||
|
|
||||||
|
let db = expect_context::<SqlitePool>();
|
||||||
|
|
||||||
|
match &*source_mode {
|
||||||
|
"host" => {
|
||||||
|
let source_mac = Some(source_mac).filter(|mac| mac != "00:00:00:00:00:00");
|
||||||
|
let default_category = Some(default_category).filter(|dc| *dc != 0);
|
||||||
|
|
||||||
|
sqlx::query!(
|
||||||
|
r"INSERT INTO beacon_template
|
||||||
|
(template_name, operating_system, config_id, listener_id, source_ip, source_mac, source_mode, default_category)
|
||||||
|
VALUES
|
||||||
|
(?, ?, ?, ?, ?, ?, 'host', ?)",
|
||||||
|
template_name,
|
||||||
|
operating_system,
|
||||||
|
config_id,
|
||||||
|
listener_id,
|
||||||
|
source_ip,
|
||||||
|
source_mac,
|
||||||
|
default_category
|
||||||
|
)
|
||||||
|
.execute(&db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
"custom" => {
|
||||||
|
let source_mac = Some(source_mac).filter(|mac| mac != "00:00:00:00:00:00");
|
||||||
|
let default_category = Some(default_category).filter(|dc| *dc != 0);
|
||||||
|
|
||||||
|
sqlx::query!(
|
||||||
|
r"INSERT INTO beacon_template
|
||||||
|
(template_name, operating_system, config_id, listener_id, source_ip, source_mac, source_mode, source_netmask, source_gateway, default_category)
|
||||||
|
VALUES
|
||||||
|
(?, ?, ?, ?, ?, ?, 'host', ?, ?, ?)",
|
||||||
|
template_name,
|
||||||
|
operating_system,
|
||||||
|
config_id,
|
||||||
|
listener_id,
|
||||||
|
source_ip,
|
||||||
|
source_mac,
|
||||||
|
source_netmask,
|
||||||
|
source_gateway,
|
||||||
|
default_category
|
||||||
|
)
|
||||||
|
.execute(&db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
_other => {
|
||||||
|
srverr!("Invalid type of source mode provided");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[server]
|
||||||
|
pub async fn remove_template(template_id: i64) -> Result<(), ServerFnError> {
|
||||||
|
let user = user::get_auth_session().await?;
|
||||||
|
|
||||||
|
if user.is_none() {
|
||||||
|
srverr!("You are not signed in!");
|
||||||
|
}
|
||||||
|
|
||||||
|
let db = expect_context::<SqlitePool>();
|
||||||
|
|
||||||
|
sqlx::query!("DELETE FROM beacon_template WHERE template_id = ?", template_id)
|
||||||
|
.execute(&db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[server]
|
||||||
|
pub async fn get_templates() -> Result<Vec<BeaconTemplate>, ServerFnError> {
|
||||||
|
let user = user::get_auth_session().await?;
|
||||||
|
|
||||||
|
if user.is_none() {
|
||||||
|
return Err(ServerFnError::<NoCustomError>::ServerError("You are not signed in!".to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let db = expect_context::<SqlitePool>();
|
||||||
|
|
||||||
|
Ok(sqlx::query_as("SELECT * FROM beacon_template")
|
||||||
|
.fetch_all(&db)
|
||||||
|
.await?)
|
||||||
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn TemplatesView() -> impl IntoView {
|
pub fn TemplatesView() -> impl IntoView {
|
||||||
view! {
|
let BeaconResources { configs, listeners, categories, templates, .. } = expect_context();
|
||||||
<div>
|
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<div class="templates">
|
||||||
|
<h2>"Beacon templates"</h2>
|
||||||
|
<p>
|
||||||
|
"Beacon templates indicate the information needed to spin up a new beacon. "
|
||||||
|
"Specifically, this is how to create a beacon for an operating system or network configuration."
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
"To create a new template, you will need:"
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>"Operating system: the OS the beacon will run on"</li>
|
||||||
|
<li>"Listener: where the beacon will call back to, and what cert will be used"</li>
|
||||||
|
<li>"Configuration: what configuration the beacon will use to call back with"</li>
|
||||||
|
<li>"Default category: when a new beacon calls back, place it in this category (can be left empty for no category)"</li>
|
||||||
|
<li>"Source IP address: Sparse can't use any IP addresses already in use on the network, so choose one here"</li>
|
||||||
|
<li>"Source MAC: Sparse can optionally use the host MAC address, or use one specified. Leave at 00:00:00:00:00:00 to use the host MAC address. Using a different MAC address will place the NIC in promiscuous mode, and hasn't been tested on Windows"</li>
|
||||||
|
<li>"Source networking mode: choose host to gather the gateway and netmask from the host, or specify your own (maybe custom NAT is going on)"</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<Suspense fallback=|| view! { <p>"Loading..."</p> }>
|
||||||
|
{ move || Suspend::new(async move {
|
||||||
|
let configs = match configs.await {
|
||||||
|
Ok(cs) => cs,
|
||||||
|
Err(e) => return Either::Left(view! {
|
||||||
|
<p>{"There was an error loading configs:".to_string()}</p>
|
||||||
|
<p>{format!("error: {}", e)}</p>
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let listeners = match listeners.await {
|
||||||
|
Ok(cs) => cs,
|
||||||
|
Err(e) => return Either::Left(view! {
|
||||||
|
<p>{"There was an error loading listeners:".to_string()}</p>
|
||||||
|
<p>{format!("error: {}", e)}</p>
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let categories = match categories.await {
|
||||||
|
Ok(cs) => cs,
|
||||||
|
Err(e) => return Either::Left(view! {
|
||||||
|
<p>{"There was an error loading categories:".to_string()}</p>
|
||||||
|
<p>{format!("error: {}", e)}</p>
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let templates = match templates.await {
|
||||||
|
Ok(ts) => ts,
|
||||||
|
Err(e) => return Either::Left(view! {
|
||||||
|
<p>{"There was an error loading templates:".to_string()}</p>
|
||||||
|
<p>{format!("error: {}", e)}</p>
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
Either::Right(view! {
|
||||||
|
<AddTemplateForm
|
||||||
|
configs=configs.clone()
|
||||||
|
listeners=listeners.clone()
|
||||||
|
categories=categories.clone()
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DisplayTemplates
|
||||||
|
configs
|
||||||
|
listeners
|
||||||
|
categories
|
||||||
|
templates
|
||||||
|
/>
|
||||||
|
})
|
||||||
|
})}
|
||||||
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn AddTemplateForm(
|
||||||
|
configs: Vec<super::configs::BeaconConfig>,
|
||||||
|
listeners: Vec<super::listeners::PubListener>,
|
||||||
|
categories: Vec<super::categories::Category>,
|
||||||
|
) -> impl IntoView {
|
||||||
|
let BeaconResources { add_template, .. } = expect_context();
|
||||||
|
|
||||||
|
view! {
|
||||||
|
{configs.is_empty()
|
||||||
|
.then(|| view! {
|
||||||
|
<span class="error">"Missing configurations! Cannot create a template without a configuration"</span>
|
||||||
|
})}
|
||||||
|
{listeners.is_empty()
|
||||||
|
.then(|| view! {
|
||||||
|
<span class="error">"Missing listeners! Cannot create a template without a listener"</span>
|
||||||
|
})}
|
||||||
|
|
||||||
|
<ActionForm action=add_template>
|
||||||
|
<fieldset>
|
||||||
|
{move || match add_template.value().get() {
|
||||||
|
Some(Ok(_)) => Either::Right(()),
|
||||||
|
None => Either::Right(()),
|
||||||
|
Some(Err(e)) => Either::Left(view! {
|
||||||
|
<p>"Error creating template:"</p>
|
||||||
|
<p>{format!("{e:?}")}</p>
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
<legend>"Create new template"</legend>
|
||||||
|
<label>"Name"</label>
|
||||||
|
<input name="template_name"/>
|
||||||
|
<label>"Operating System"</label>
|
||||||
|
<select name="operating_system">
|
||||||
|
<option value="linux">"Linux"</option>
|
||||||
|
<option value="windows">"Windows"</option>
|
||||||
|
<option value="freebsd">"Freebsd"</option>
|
||||||
|
</select>
|
||||||
|
<label>"Listener"</label>
|
||||||
|
<select name="listener_id">
|
||||||
|
<option value="0">"---"</option>
|
||||||
|
{listeners
|
||||||
|
.iter()
|
||||||
|
.map(|listener| view! {
|
||||||
|
<option value=listener.listener_id.to_string()>{listener.domain_name.clone()}</option>
|
||||||
|
})
|
||||||
|
.collect_view()}
|
||||||
|
</select>
|
||||||
|
<label>"Configuration"</label>
|
||||||
|
<select name="config_id">
|
||||||
|
<option value="0">"---"</option>
|
||||||
|
{configs
|
||||||
|
.iter()
|
||||||
|
.map(|config| view! {
|
||||||
|
<option value=config.config_id.to_string()>{config.config_name.clone()}</option>
|
||||||
|
})
|
||||||
|
.collect_view()}
|
||||||
|
</select>
|
||||||
|
<label>"Default category for new instances"</label>
|
||||||
|
<select name="default_category">
|
||||||
|
<option value="0">"---"</option>
|
||||||
|
{categories
|
||||||
|
.iter()
|
||||||
|
.map(|category| view! {
|
||||||
|
<option value=category.category_id.to_string()>{category.category_name.clone()}</option>
|
||||||
|
})
|
||||||
|
.collect_view()}
|
||||||
|
</select>
|
||||||
|
<label>"Source IP address"</label>
|
||||||
|
<input name="source_ip"/>
|
||||||
|
<label>"Source MAC address (can be empty)"</label>
|
||||||
|
<input name="source_mac" value="00:00:00:00:00:00"/>
|
||||||
|
<label>"Source networking mode"</label>
|
||||||
|
<select name="source_mode">
|
||||||
|
<option value="host">"Host"</option>
|
||||||
|
<option value="custom">"Custom"</option>
|
||||||
|
</select>
|
||||||
|
<label class="mode-custom">"Custom CIDR mask"</label>
|
||||||
|
<input class="mode-custom" type="number" name="source_netmask" value="24"/>
|
||||||
|
<label class="mode-custom">"Network gateway"</label>
|
||||||
|
<input class="mode-custom" name="source_gateway"/>
|
||||||
|
<div></div>
|
||||||
|
<input type="submit" value="Submit" />
|
||||||
|
</fieldset>
|
||||||
|
</ActionForm>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn DisplayTemplates(
|
||||||
|
configs: Vec<super::configs::BeaconConfig>,
|
||||||
|
listeners: Vec<super::listeners::PubListener>,
|
||||||
|
categories: Vec<super::categories::Category>,
|
||||||
|
templates: Vec<BeaconTemplate>
|
||||||
|
) -> impl IntoView {
|
||||||
|
let BeaconResources { remove_template, .. } = expect_context();
|
||||||
|
|
||||||
|
let templates_view = templates
|
||||||
|
.iter()
|
||||||
|
.map(|template| view! {
|
||||||
|
<li>
|
||||||
|
<h4>
|
||||||
|
{template.template_id}
|
||||||
|
": "
|
||||||
|
{template.template_name.clone()}
|
||||||
|
" ("
|
||||||
|
{template.operating_system.clone()}
|
||||||
|
")"
|
||||||
|
</h4>
|
||||||
|
<div>
|
||||||
|
<button>
|
||||||
|
"Download installer"
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
on:click={
|
||||||
|
let template_id = template.template_id;
|
||||||
|
move |_| {
|
||||||
|
remove_template.dispatch(RemoveTemplate { template_id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>
|
||||||
|
"Delete template"
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<ul>
|
||||||
|
<li>"Source IP: "{template.source_ip.clone()}</li>
|
||||||
|
<li>"Source MAC: "{template.source_mac.clone().unwrap_or("00:00:00:00:00:00".to_owned())}</li>
|
||||||
|
<li>
|
||||||
|
"Source mode: "
|
||||||
|
{match template.source_mode.clone() {
|
||||||
|
BeaconSourceMode::Host => "Host".to_owned(),
|
||||||
|
BeaconSourceMode::Custom(nm, gw) => format!("Custom; netmask: {nm}, gateway: {gw}")
|
||||||
|
}}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
"Configuration: "
|
||||||
|
{configs
|
||||||
|
.iter()
|
||||||
|
.find(|config| config.config_id == template.config_id)
|
||||||
|
.map(|config| config.config_name.clone())}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
"Listener: "
|
||||||
|
{listeners
|
||||||
|
.iter()
|
||||||
|
.find(|listener| listener.listener_id == template.listener_id)
|
||||||
|
.map(|listener| listener.domain_name.clone())}
|
||||||
|
</li>
|
||||||
|
{template
|
||||||
|
.default_category
|
||||||
|
.map(|cat_id|
|
||||||
|
categories
|
||||||
|
.iter()
|
||||||
|
.find(|cat| cat.category_id == cat_id)
|
||||||
|
)
|
||||||
|
.flatten()
|
||||||
|
.map(|cat| view! {
|
||||||
|
<li>
|
||||||
|
"Default category: "
|
||||||
|
{cat.category_name.clone()}
|
||||||
|
</li>
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
})
|
||||||
|
.collect_view();
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<h3>"Current beacon templates"</h3>
|
||||||
|
{move || match remove_template.value().get() {
|
||||||
|
Some(Ok(_)) => Either::Right(()),
|
||||||
|
None => Either::Right(()),
|
||||||
|
Some(Err(e)) => Either::Left(view! {
|
||||||
|
<p>"Error deleting template:"</p>
|
||||||
|
<p>{format!("{e:?}")}</p>
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
<ul>
|
||||||
|
{templates_view}
|
||||||
|
</ul>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -0,0 +1,26 @@
|
|||||||
|
main.beacons div.templates {
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 400px 200px;
|
||||||
|
grid-row-gap: 10px;
|
||||||
|
|
||||||
|
input, label {
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mode-host, .mode-custom {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
select[name="source_mode"]:has(> option[value="custom"]:checked) ~ .mode-custom {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
border-bottom: 1px solid #2e2e59;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -27,10 +27,10 @@ typedef struct Parameters {
|
|||||||
SourceIp_t source_ip;
|
SourceIp_t source_ip;
|
||||||
unsigned short destination_port;
|
unsigned short destination_port;
|
||||||
unsigned short pubkey_cert_size;
|
unsigned short pubkey_cert_size;
|
||||||
unsigned short beacon_name_length;
|
unsigned short template_name_length;
|
||||||
unsigned short domain_name_length;
|
unsigned short domain_name_length;
|
||||||
char pubkey_cert[1024];
|
char pubkey_cert[1024];
|
||||||
char beacon_identifier[64];
|
char beacon_identifier[64];
|
||||||
char beacon_name[128];
|
char template_name[128];
|
||||||
char domain_name[128];
|
char domain_name[128];
|
||||||
} Parameters_t;
|
} Parameters_t;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user