summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGalen Guyer <galen@galenguyer.com>2022-08-31 16:59:43 -0400
committerGalen Guyer <galen@galenguyer.com>2022-08-31 17:03:14 -0400
commitee62cd357fdb0cfc9116aec7e28162ad684a8599 (patch)
treef8ceacfa8223e7bbdb94461877f5d07377311fac
parentbcb51966f3446f671558e615cb3dfbe700bc3e23 (diff)
Validate record before creating or updating
-rw-r--r--Cargo.lock3
-rw-r--r--Cargo.toml3
-rw-r--r--src/db/metrics.rs5
-rw-r--r--src/db/mod.rs3
-rw-r--r--src/db/models.rs2
-rw-r--r--src/db/strings.rs1
-rw-r--r--src/features.rs4
-rw-r--r--src/routes/v1/metrics.rs14
-rw-r--r--src/routes/v1/mod.rs2
-rw-r--r--src/routes/v1/records.rs28
-rw-r--r--src/routes/v1/zones.rs12
11 files changed, 53 insertions, 24 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 83330b6..03ae4c4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -545,7 +545,7 @@ dependencies = [
[[package]]
name = "hdt-api"
-version = "0.1.5"
+version = "0.2.0"
dependencies = [
"addr",
"anyhow",
@@ -569,6 +569,7 @@ dependencies = [
"tower",
"tower-http",
"tracing-subscriber",
+ "trust-dns-proto",
"uuid",
"whois-rust",
"whoisthere",
diff --git a/Cargo.toml b/Cargo.toml
index a4c96f6..4b1e52a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "hdt-api"
-version = "0.1.5"
+version = "0.2.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -28,6 +28,7 @@ totp-rs = "2.0.0"
tower = "0.4.12"
tower-http = { version = "0.3.3", features = ["cors", "trace"] }
tracing-subscriber = "0.3.11"
+trust-dns-proto = "0.21.2"
# uuid v1.0.0 is out, but it breaks sqlx... we just have to wait for sqlx to release v0.6
uuid = { version = "0.8.2", features = ["serde", "v4"] }
whois-rust = "1.5.0"
diff --git a/src/db/metrics.rs b/src/db/metrics.rs
index 7557d47..f3ccb77 100644
--- a/src/db/metrics.rs
+++ b/src/db/metrics.rs
@@ -2,10 +2,7 @@ use crate::db::models::Metrics;
use crate::db::strings;
use sqlx::{Pool, Postgres};
-
-pub async fn get_metrics(
- pool: &Pool<Postgres>,
-) -> Result<Metrics, sqlx::Error> {
+pub async fn get_metrics(pool: &Pool<Postgres>) -> Result<Metrics, sqlx::Error> {
let mut transaction = pool.begin().await?;
let metrics = sqlx::query_as::<_, Metrics>(&strings::GET_METRICS)
.fetch_one(&mut transaction)
diff --git a/src/db/mod.rs b/src/db/mod.rs
index f06dd3b..6af9b2d 100644
--- a/src/db/mod.rs
+++ b/src/db/mod.rs
@@ -1,8 +1,7 @@
+pub mod metrics;
pub mod records;
pub mod users;
pub mod zones;
-pub mod metrics;
pub mod models;
mod strings;
-
diff --git a/src/db/models.rs b/src/db/models.rs
index 0d82018..64b9290 100644
--- a/src/db/models.rs
+++ b/src/db/models.rs
@@ -49,4 +49,4 @@ pub struct Metrics {
pub p99: f64,
pub avg: f64,
pub count: i64,
-} \ No newline at end of file
+}
diff --git a/src/db/strings.rs b/src/db/strings.rs
index dc6e44d..b8812e5 100644
--- a/src/db/strings.rs
+++ b/src/db/strings.rs
@@ -44,7 +44,6 @@ lazy_static! {
WHERE api_keys.token_hash = $1
AND api_keys.expires_at > (now() AT TIME ZONE 'UTC');
";
-
pub(crate) static ref GET_METRICS: &'static str = r#"
SELECT
percentile_cont(0.50) WITHIN GROUP (ORDER BY queries.duration_us) AS p50,
diff --git a/src/features.rs b/src/features.rs
index 378bc46..8f8ff27 100644
--- a/src/features.rs
+++ b/src/features.rs
@@ -6,6 +6,6 @@ lazy_static! {
env::var("SIGNUPS_ENABLED").unwrap_or_else(|_| String::from("false")) == "true";
pub static ref TOTP_ENABLED: bool =
env::var("TOTP_ENABLED").unwrap_or_else(|_| String::from("false")) == "true";
- pub static ref METRICS_ENABLED: bool =
- env::var("METRICS_URL").is_ok() && env::var("METRICS_ENABLED").unwrap_or_else(|_| String::from("false")) == "true";
+ pub static ref METRICS_ENABLED: bool = env::var("METRICS_URL").is_ok()
+ && env::var("METRICS_ENABLED").unwrap_or_else(|_| String::from("false")) == "true";
}
diff --git a/src/routes/v1/metrics.rs b/src/routes/v1/metrics.rs
index 38485d0..074bfa0 100644
--- a/src/routes/v1/metrics.rs
+++ b/src/routes/v1/metrics.rs
@@ -1,5 +1,5 @@
+use crate::extractors::Json;
use crate::{db, features};
-use crate::extractors::{Json};
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::Extension;
@@ -11,12 +11,18 @@ pub async fn get_metrics(
Extension(metrics_pool): Extension<Option<Arc<Pool<Postgres>>>>,
) -> impl IntoResponse {
if !*features::METRICS_ENABLED {
- return (StatusCode::NOT_FOUND, Json(json!({"error": "Metrics not enabled"})));
+ return (
+ StatusCode::NOT_FOUND,
+ Json(json!({"error": "Metrics not enabled"})),
+ );
}
- let metrics = db::metrics::get_metrics(&metrics_pool.as_ref().unwrap()).await;
+ let metrics = db::metrics::get_metrics(&metrics_pool.unwrap()).await;
match metrics {
Ok(metrics) => (StatusCode::OK, Json(json!(metrics))),
- Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": err.to_string()}))),
+ Err(err) => (
+ StatusCode::INTERNAL_SERVER_ERROR,
+ Json(json!({"error": err.to_string()})),
+ ),
}
}
diff --git a/src/routes/v1/mod.rs b/src/routes/v1/mod.rs
index df0880d..b1d2e44 100644
--- a/src/routes/v1/mod.rs
+++ b/src/routes/v1/mod.rs
@@ -1,7 +1,7 @@
pub mod features;
+pub mod metrics;
pub mod records;
pub mod users;
pub mod zones;
-pub mod metrics;
mod requests;
diff --git a/src/routes/v1/records.rs b/src/routes/v1/records.rs
index fd72663..3c424c9 100644
--- a/src/routes/v1/records.rs
+++ b/src/routes/v1/records.rs
@@ -7,7 +7,10 @@ use axum::response::IntoResponse;
use axum::Extension;
use serde_json::json;
use sqlx::{Pool, Postgres};
+use std::net::{Ipv4Addr, Ipv6Addr};
+use std::str::FromStr;
use std::sync::Arc;
+use trust_dns_proto::rr::RecordType;
use uuid::Uuid;
pub async fn get_records(
@@ -68,6 +71,10 @@ pub async fn create_record(
);
}
+ if let Err(e) = validate_record(&data.record_type, &data.content) {
+ return (StatusCode::BAD_REQUEST, Json(json!({ "error": e })));
+ }
+
let record = db::records::create_record(
&pool,
&zone.id,
@@ -120,6 +127,10 @@ pub async fn update_record(
);
}
+ if let Err(e) = validate_record(&data.record_type, &data.content) {
+ return (StatusCode::BAD_REQUEST, Json(json!({ "error": e })));
+ }
+
let record = db::records::update_record(
&pool,
&zone_id,
@@ -182,3 +193,20 @@ pub async fn delete_record(
})),
)
}
+
+fn validate_record(rtype: &str, content: &str) -> Result<(), String> {
+ match RecordType::from_str(rtype) {
+ Ok(rtype) => match rtype {
+ RecordType::A => content
+ .parse::<Ipv4Addr>()
+ .map(|_| ())
+ .map_err(|_| String::from("Invalid IPv4 address")),
+ RecordType::AAAA => content
+ .parse::<Ipv6Addr>()
+ .map(|_| ())
+ .map_err(|_| String::from("Invalid IPv6 address")),
+ _ => Err(String::from("Unknown record type")),
+ },
+ _ => Err(String::from("Unknown record type")),
+ }
+}
diff --git a/src/routes/v1/zones.rs b/src/routes/v1/zones.rs
index ddbeb19..0281948 100644
--- a/src/routes/v1/zones.rs
+++ b/src/routes/v1/zones.rs
@@ -46,7 +46,7 @@ pub async fn get_root_domain(
Extension(pool): Extension<Arc<Pool<Postgres>>>,
) -> impl IntoResponse {
let zones = db::zones::get_zones(&pool, user.sub).await;
- let domain = match query.domain.ends_with(".") {
+ let domain = match query.domain.ends_with('.') {
true => query.domain,
false => format!("{}.", query.domain),
};
@@ -89,7 +89,7 @@ pub async fn create_zone(
);
}
- let lookup = match WhoIsLookupOptions::from_string(&*zone_id.trim_end_matches('.')) {
+ let lookup = match WhoIsLookupOptions::from_string(zone_id.trim_end_matches('.')) {
Ok(lookup) => lookup,
Err(e) => {
return (
@@ -102,9 +102,7 @@ pub async fn create_zone(
}
};
let whois_res = match whois_client.lookup(lookup) {
- Ok(res) => {
- res
- }
+ Ok(res) => res,
Err(e) => {
return (
StatusCode::BAD_REQUEST,
@@ -116,7 +114,7 @@ pub async fn create_zone(
}
};
- let whois_info = whoisthere::parse_info(&*zone_id.trim_end_matches('.'), &whois_res);
+ let whois_info = whoisthere::parse_info(zone_id.trim_end_matches('.'), &whois_res);
if !whois_info.is_registered || whois_info.is_under_grace_period {
return (
StatusCode::BAD_REQUEST,
@@ -124,7 +122,7 @@ pub async fn create_zone(
"error": "Invalid domain",
"message": "Domain is not registered or expired"
})),
- )
+ );
}
let zone = db::zones::create_zone(&pool, &zone_id, user.sub).await;