Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions crates/defguard_common/src/db/models/wireguard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,18 @@ pub enum NetworkAddressError {
}

impl WireguardNetwork {
pub async fn count<'e, E>(executor: E) -> sqlx::Result<usize>
where
E: PgExecutor<'e>,
{
let count = query_scalar!("SELECT COUNT(*) FROM wireguard_network")
.fetch_one(executor)
.await?
.unwrap_or_default();

Ok(count as usize)
}

#[allow(clippy::too_many_arguments)]
#[must_use]
pub fn new(
Expand Down
33 changes: 33 additions & 0 deletions crates/defguard_core/src/handlers/wireguard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ pub(crate) struct WireguardNetworkInfo {
has_devices: bool,
}

#[derive(Serialize, ToSchema)]
pub(crate) struct LocationsCount {
count: usize,
}

#[derive(Deserialize, Serialize, ToSchema)]
pub struct WireguardNetworkData {
pub name: String,
Expand Down Expand Up @@ -511,6 +516,34 @@ pub async fn list_networks(_role: AdminRole, State(appstate): State<AppState>) -
Ok(ApiResponse::json(network_info, StatusCode::OK))
}

/// Number of all networks
///
/// Retrieve count of all networks.
///
/// # Returns
/// - `LocationsCount` object
///
/// - `WebError` if error occurs
#[utoipa::path(
get,
path = "/api/v1/network/count",
responses(
(status = 200, description = "Count of all networks", body = LocationsCount),
(status = 401, description = "Unauthorized to count networks.", body = ApiResponse, example = json!({"msg": "Session is required"})),
(status = 403, description = "You don't have permission to count networks.", body = ApiResponse, example = json!({"msg": "access denied"})),
(status = 500, description = "Unable to count networks.", body = ApiResponse, example = json!({"msg": "Internal server error"}))
),
security(
("cookie" = []),
("api_token" = [])
)
)]
pub async fn count_networks(_role: AdminRole, State(appstate): State<AppState>) -> ApiResult {
debug!("Counting WireGuard networks");
let count = WireguardNetwork::count(&appstate.pool).await?;
Ok(ApiResponse::json(LocationsCount { count }, StatusCode::OK))
Comment thread
filipslezaklab marked this conversation as resolved.
}

/// Details of network
///
/// Retrieve details about network with `network_id`.
Expand Down
8 changes: 5 additions & 3 deletions crates/defguard_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,10 @@ use crate::{
add_webhook, change_enabled, change_webhook, delete_webhook, get_webhook, list_webhooks,
},
wireguard::{
add_device, add_user_devices, create_network, delete_device, delete_network,
download_config, gateway_status, get_device, import_network, list_devices,
list_networks, list_user_devices, modify_device, modify_network, network_details,
add_device, add_user_devices, count_networks, create_network, delete_device,
delete_network, download_config, gateway_status, get_device, import_network,
list_devices, list_networks, list_user_devices, modify_device, modify_network,
network_details,
},
worker::{create_job, create_worker_token, job_status, list_workers, remove_worker},
},
Expand Down Expand Up @@ -537,6 +538,7 @@ pub fn build_webapp(
post(start_network_device_setup_for_device),
)
.route("/network", post(create_network).get(list_networks))
.route("/network/count", get(count_networks))
.route("/network/display", get(get_locations_display))
.route("/network/import", post(import_network))
.route("/network/stats", get(locations_overview_stats))
Expand Down
1 change: 1 addition & 0 deletions crates/defguard_core/src/openapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ use super::{
network::modify_network,
network::delete_network,
network::list_networks,
network::count_networks,
network::network_details,
// /license
license::license_check,
Expand Down
63 changes: 2 additions & 61 deletions crates/defguard_setup/src/handlers/migration.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
use std::sync::{Arc, Mutex};

use axum::{Extension, Json};
use defguard_common::db::models::{
ActiveWizard, Settings, Wizard, group::Group, migration_wizard::MigrationWizardState,
settings::update_current_settings,
};
use defguard_common::db::models::{ActiveWizard, Wizard, migration_wizard::MigrationWizardState};
use defguard_core::{
auth::AdminOrSetupRole,
error::WebError,
Expand All @@ -15,7 +12,7 @@ use serde::{Deserialize, Serialize};
use serde_json::json;
use sqlx::PgPool;
use tokio::sync::oneshot;
use tracing::{debug, info};
use tracing::info;

pub async fn get_migration_state(
_: AdminOrSetupRole,
Expand All @@ -38,66 +35,10 @@ pub async fn update_migration_state(
#[derive(Deserialize, Serialize, Debug)]
pub struct GeneralConfig {
defguard_url: String,
default_admin_group_name: String,
default_authentication: u32,
default_mfa_code_lifetime: u32,
public_proxy_url: String,
}

pub async fn set_general_config(
_: AdminOrSetupRole,
Extension(pool): Extension<PgPool>,
Json(general_config): Json<GeneralConfig>,
) -> ApiResult {
info!("Applying initial general configuration settings");
debug!(
"General configuration received: defguard_url={}, default_admin_group_name={}, default_authentication={}, default_mfa_code_lifetime={}, public_proxy_url={}",
general_config.defguard_url,
general_config.default_admin_group_name,
general_config.default_authentication,
general_config.default_mfa_code_lifetime,
general_config.public_proxy_url,
);
let default_admin_group_name = general_config.default_admin_group_name.clone();
let mut settings = Settings::get_current_settings();
settings.public_proxy_url = general_config.public_proxy_url;
settings.defguard_url = general_config.defguard_url;
settings.default_admin_group_name = general_config.default_admin_group_name;
settings.authentication_period_days = general_config
.default_authentication
.try_into()
.map_err(|err| {
WebError::BadRequest(format!("Invalid authentication period days: {err}"))
})?;
settings.mfa_code_timeout_seconds = general_config
.default_mfa_code_lifetime
.try_into()
.map_err(|err| WebError::BadRequest(format!("Invalid MFA code timeout seconds: {err}")))?;
update_current_settings(&pool, settings).await?;
debug!("Settings persisted");

if let Some(mut group) = Group::find_by_name(&pool, &default_admin_group_name).await? {
debug!(
"Admin group {} found, marking as admin",
default_admin_group_name
);
group.is_admin = true;
group.save(&pool).await?;
} else {
debug!(
"Admin group {} not found, creating",
default_admin_group_name
);
let mut group = Group::new(&default_admin_group_name);
group.is_admin = true;
group.save(&pool).await?;
}

info!("Initial general configuration applied");

Ok(ApiResponse::with_status(StatusCode::OK))
}

pub async fn finish_setup(
_: AdminOrSetupRole,
Extension(pool): Extension<PgPool>,
Expand Down
6 changes: 3 additions & 3 deletions crates/defguard_setup/src/migration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use defguard_core::{
resource_display::get_locations_display,
session_info::get_session_info,
settings::{get_settings, get_settings_essentials, patch_settings},
wireguard::list_networks,
wireguard::{count_networks, list_networks},
},
health_check,
version::IncompatibleComponents,
Expand All @@ -45,7 +45,7 @@ use tracing::{info, instrument};

use crate::handlers::{
initial_wizard::{create_ca, get_ca, upload_ca},
migration::{finish_setup, get_migration_state, set_general_config, update_migration_state},
migration::{finish_setup, get_migration_state, update_migration_state},
};

/// FIXME: This is a workaround which enables us to reuse the same API handlers
Expand Down Expand Up @@ -118,6 +118,7 @@ pub fn build_migration_webapp(
.route("/auth/email/verify", post(email_mfa_code))
.route("/auth/recovery", post(recovery_code))
.route("/network", get(list_networks))
.route("/network/count", get(count_networks))
.route("/network/display", get(get_locations_display))
.route(
"/network/{network_id}/gateways/setup",
Expand All @@ -130,7 +131,6 @@ pub fn build_migration_webapp(
"/state",
get(get_migration_state).put(update_migration_state),
)
.route("/general_config", post(set_general_config))
.route("/ca", post(create_ca).get(get_ca))
.route("/ca/upload", post(upload_ca))
.route("/finish", post(finish_setup)),
Expand Down
23 changes: 10 additions & 13 deletions crates/defguard_setup/tests/migration_wizard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,17 +97,16 @@ async fn test_migration_full_flow(_: PgPoolOptions, options: PgConnectOptions) {
assert_migration_step(&pool, "general").await;

let resp = client
.post("/api/v1/migration/general_config")
.client
.patch(format!("{}/api/v1/settings", client.base_url()))
.json(&json!({
"defguard_url": "https://migration.example.com",
"default_admin_group_name": "admins",
"default_authentication": 14,
"default_mfa_code_lifetime": 120,
"public_proxy_url": "https://proxy.migration.example.com"
"authentication_period_days": 14,
"mfa_code_timeout_seconds": 120
}))
.send()
.await
.expect("Failed to POST /api/v1/migration/general_config");
.expect("Failed to PATCH /api/v1/settings");
assert_eq!(resp.status(), StatusCode::OK);

let settings = Settings::get(&pool)
Expand Down Expand Up @@ -225,22 +224,20 @@ async fn test_migration_auth_enforcement(_: PgPoolOptions, options: PgConnectOpt
);

let resp = unauth
.post(format!("{base}/api/v1/migration/general_config"))
.patch(format!("{base}/api/v1/settings"))
.header(USER_AGENT, "test/0.0")
.json(&json!({
"defguard_url": "https://x.example.com",
"default_admin_group_name": "admins",
"default_authentication": 14,
"default_mfa_code_lifetime": 120,
"public_proxy_url": "https://px.example.com"
"authentication_period_days": 14,
"mfa_code_timeout_seconds": 120
}))
.send()
.await
.expect("Failed POST migration/general_config");
.expect("Failed PATCH settings");
assert_eq!(
resp.status(),
StatusCode::UNAUTHORIZED,
"POST /migration/general_config should require auth"
"PATCH /settings should require auth"
);

let resp = unauth
Expand Down
32 changes: 16 additions & 16 deletions web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@
<meta content="Defguard team" name="author" />
<title>Defguard</title>
<!--Icons-->
<link rel="icon" type="image/ico" href="/src/assets/icons/favicon.ico" />
<link rel="icon" type="image/png" sizes="16x16" href="/src/assets/icons/favicon-16x16.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/src/assets/icons/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="96x96" href="/src/assets/icons/favicon-96x96.png" />
<link rel="icon" type="image/ico" href="/assets/icons/favicon.ico" />
<link rel="icon" type="image/png" sizes="16x16" href="/assets/icons/favicon-16x16.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/assets/icons/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="96x96" href="/assets/icons/favicon-96x96.png" />
<!--Apple-->
<link rer="apple-touch-icon" href="/src/assets/icons/app/512.png" />
<link rel="apple-touch-icon" sizes="57x57" href="/src/assets/icons/app/57.png" />
<link rel="apple-touch-icon" sizes="60x60" href="/src/assets/icons/app/60.png" />
<link rel="apple-touch-icon" sizes="72x72" href="/src/assets/icons/app/72.png" />
<link rel="apple-touch-icon" sizes="76x76" href="/src/assets/icons/app/76.png" />
<link rel="apple-touch-icon" sizes="114x114" href="/src/assets/icons/app/114.png" />
<link rel="apple-touch-icon" sizes="120x120" href="/src/assets/icons/app/120.png" />
<link rel="apple-touch-icon" sizes="144x144" href="/src/assets/icons/app/144.png" />
<link rel="apple-touch-icon" sizes="152x152" href="/src/assets/icons/app/152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/src/assets/icons/app/180.png" />
<link rel="apple-touch-icon" sizes="256x256" href="/src/assets/icons/app/256.png" />
<link rel="apple-touch-icon" sizes="512x512" href="/src/assets/icons/app/512.png" />
<link rer="apple-touch-icon" href="/assets/icons/app/512.png" />
<link rel="apple-touch-icon" sizes="57x57" href="/assets/icons/app/57.png" />
<link rel="apple-touch-icon" sizes="60x60" href="/assets/icons/app/60.png" />
<link rel="apple-touch-icon" sizes="72x72" href="/assets/icons/app/72.png" />
<link rel="apple-touch-icon" sizes="76x76" href="/assets/icons/app/76.png" />
<link rel="apple-touch-icon" sizes="114x114" href="/assets/icons/app/114.png" />
<link rel="apple-touch-icon" sizes="120x120" href="/assets/icons/app/120.png" />
<link rel="apple-touch-icon" sizes="144x144" href="/assets/icons/app/144.png" />
<link rel="apple-touch-icon" sizes="152x152" href="/assets/icons/app/152.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/assets/icons/app/180.png" />
<link rel="apple-touch-icon" sizes="256x256" href="/assets/icons/app/256.png" />
<link rel="apple-touch-icon" sizes="512x512" href="/assets/icons/app/512.png" />
</head>

<body>
Expand Down
7 changes: 5 additions & 2 deletions web/messages/en/edge_wizard.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@
"edge_setup_step_edge_adoption_description": "Review the system's checks and see if any issues need attention before deployment.",
"edge_setup_step_confirmation_label": "Confirmation",
"edge_setup_step_confirmation_description": "Your configuration was successful. You're all set.",
"edge_setup_component_label_common_name": "Common Name",
"edge_setup_component_label_common_name": "Edge Name",
"edge_setup_component_error_common_name_help": "This name will be visible in the Edge Components list and is used to identify a deployed instance, as there may be multiple instances for High Availability (HA).",
"edge_setup_component_label_ip_or_domain": "IP or Domain",
"edge_setup_component_label_ip_or_domain_help": "Enter the IP or domain name of the server where the Edge Component is deployed. Core will then connect to it and perform the adoption automatically.",
"edge_setup_component_label_grpc_port": "gRPC Port",
"edge_setup_component_error_common_name_required": "Common Name is required",
"edge_setup_component_label_grpc_port_help": "Specify the gRPC TCP port for Edge Component communication. If unchanged, leave the default value. Ensure this port is open and accessible to the Core instance through any server or network firewalls.",
"edge_setup_component_error_common_name_required": "Edge Name is required",
"edge_setup_component_error_ip_or_domain_required": "IP or Domain is required",
"edge_setup_component_error_grpc_port_required": "gRPC Port is required",
"edge_setup_component_error_grpc_port_max": "gRPC Port must be less than 65536",
Expand Down
6 changes: 4 additions & 2 deletions web/messages/en/initial_wizard.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,11 @@
"initial_setup_ca_error_upload_failed": "Failed to upload CA. Please ensure the certificate file is valid and try again.",
"initial_setup_ca_option_create_title": "Create a certificate authority & configure all Defguard components",
"initial_setup_ca_option_create_description": "By choosing this option, Defguard will create its own certificate authority and automatically configure all components to use its certificates — no manual setup required.",
"initial_setup_ca_label_common_name": "Common Name",
"initial_setup_ca_label_common_name": "Certificate Authority Name",
"initial_setup_ca_helper_common_name": "Can be any name you wish.",
"initial_setup_ca_placeholder_common_name": "Defguard Certificate Authority",
"initial_setup_ca_label_email": "Email",
"initial_setup_ca_label_email": "Certificate Authority Email",
"initial_setup_ca_helper_email": "Each certificate authority (or any certificate) has a contact email field. Can be your email or general IT email.",
"initial_setup_ca_placeholder_email": "email@example.com",
"initial_setup_ca_label_validity": "Validity Period",
"initial_setup_ca_option_use_own_title": "Use your own certificate authority",
Expand Down
Loading
Loading