summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGalen Guyer <galen@galenguyer.com>2022-05-21 19:49:35 -0400
committerGalen Guyer <galen@galenguyer.com>2022-05-21 19:49:35 -0400
commit3613656a396b42d70259e9ff325ab9fcf0b55dd1 (patch)
treea33cbdd36da54b6dbef92feebd8945799c12292f
parentd2da324c4c15db3b137fbeaebf0568f65eb2ad67 (diff)
restore zone functionality without powerdns
-rw-r--r--Cargo.lock26
-rw-r--r--Cargo.toml1
-rw-r--r--src/db/models.rs12
-rw-r--r--src/db/zones.rs5
-rw-r--r--src/main.rs2
-rw-r--r--src/routes/v1/users.rs5
-rw-r--r--src/routes/v1/zones.rs85
7 files changed, 118 insertions, 18 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 5d78a4e..629c657 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,6 +3,16 @@
version = 3
[[package]]
+name = "addr"
+version = "0.15.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4299322d87d6ae73d3bd74918983e712acc3a1b73c880258eb5a0b45895e7b85"
+dependencies = [
+ "psl",
+ "psl-types",
+]
+
+[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -324,6 +334,7 @@ checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71"
name = "fdns-api"
version = "0.1.0"
dependencies = [
+ "addr",
"anyhow",
"axum",
"bcrypt",
@@ -892,6 +903,21 @@ dependencies = [
]
[[package]]
+name = "psl"
+version = "2.0.89"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "027244bd67547f5897fa12d991182bf3af92fcfd55860410fbb5ccbebd80b579"
+dependencies = [
+ "psl-types",
+]
+
+[[package]]
+name = "psl-types"
+version = "2.0.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8eda7c62d9ecaafdf8b62374c006de0adf61666ae96a96ba74a37134aa4e470"
+
+[[package]]
name = "quote"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 5a2c024..e70b26c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,6 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+addr = "0.15.3"
anyhow = "1.0.57"
axum = "0.5.6"
bcrypt = "0.13.0"
diff --git a/src/db/models.rs b/src/db/models.rs
index 0d52768..6af7e15 100644
--- a/src/db/models.rs
+++ b/src/db/models.rs
@@ -24,4 +24,16 @@ pub struct Zone {
pub owner_uuid: Uuid,
pub created_at: DateTime<Utc>,
pub modified_at: DateTime<Utc>,
+ // pub enabled: bool,
}
+
+#[derive(Serialize, Deserialize, FromRow, Debug)]
+pub struct Record {
+ pub id: u64,
+ pub zone_id: String,
+ pub r#type: String,
+ pub content: String,
+ pub ttl: u32,
+ pub created_at: DateTime<Utc>,
+ pub modified_at: DateTime<Utc>,
+} \ No newline at end of file
diff --git a/src/db/zones.rs b/src/db/zones.rs
index 0813bff..23bf566 100644
--- a/src/db/zones.rs
+++ b/src/db/zones.rs
@@ -26,10 +26,7 @@ pub async fn get_zones(pool: &Pool<Postgres>, owner_uuid: Uuid) -> Result<Vec<Zo
Ok(zones)
}
-pub async fn get_zone(
- pool: &Pool<Postgres>,
- id: &str,
-) -> Result<Zone, sqlx::Error> {
+pub async fn get_zone(pool: &Pool<Postgres>, id: &str) -> Result<Zone, sqlx::Error> {
let zone = sqlx::query_as::<_, Zone>(&strings::GET_ZONE)
.bind(id)
.fetch_one(pool)
diff --git a/src/main.rs b/src/main.rs
index ba08cb3..d8d88a6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -68,7 +68,7 @@ async fn main() {
.layer(
ServiceBuilder::new()
.layer(TraceLayer::new_for_http())
- .layer(Extension(pg_pool))
+ .layer(Extension(pg_pool)),
);
let addr = SocketAddr::from(([0, 0, 0, 0], 8000));
diff --git a/src/routes/v1/users.rs b/src/routes/v1/users.rs
index e3701eb..236e581 100644
--- a/src/routes/v1/users.rs
+++ b/src/routes/v1/users.rs
@@ -49,7 +49,10 @@ pub async fn create_user(
}
}
-pub async fn get_all_users(Jwt(user): Jwt, Extension(pool): Extension<Arc<Pool<Postgres>>>) -> impl IntoResponse {
+pub async fn get_all_users(
+ Jwt(user): Jwt,
+ Extension(pool): Extension<Arc<Pool<Postgres>>>,
+) -> impl IntoResponse {
if !user.admin {
return (
StatusCode::FORBIDDEN,
diff --git a/src/routes/v1/zones.rs b/src/routes/v1/zones.rs
index 18e14ec..9867162 100644
--- a/src/routes/v1/zones.rs
+++ b/src/routes/v1/zones.rs
@@ -5,13 +5,14 @@ use axum::extract::Path;
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::Extension;
+use lazy_static::lazy_static;
use serde_json::json;
-use sqlx::{Pool, Postgres};
+use sqlx::{Error, Pool, Postgres};
use std::sync::Arc;
-use lazy_static::lazy_static;
-lazy_static!{
- static ref NAMESERVERS: Vec<String> = Vec::from([String::from("ns1.fdns.dev."), String::from("ns2.fdns.dev.")]);
+lazy_static! {
+ static ref NAMESERVERS: Vec<String> =
+ Vec::from([String::from("ns1.fdns.dev."), String::from("ns2.fdns.dev."), String::from("ns3.fdns.dev."), String::from("ns4.fdns.dev.")]);
}
pub async fn list_zones(
@@ -29,17 +30,77 @@ pub async fn list_zones(
}
pub async fn create_zone(
- Path(_id): Path<String>,
- Jwt(_user): Jwt,
- Extension(_pool): Extension<Arc<Pool<Postgres>>>,
+ Path(id): Path<String>,
+ Jwt(user): Jwt,
+ Extension(pool): Extension<Arc<Pool<Postgres>>>,
) -> impl IntoResponse {
- (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "Not implemented"})));
+ let domain = ensure_trailing_dot(&id);
+ let domain = addr::parse_domain_name(&domain).unwrap();
+ if !domain.has_known_suffix() {
+ return (
+ StatusCode::BAD_REQUEST,
+ Json(json!({"error": "Invalid domain"})),
+ );
+ }
+
+ let zone_id = domain.root().unwrap().to_owned();
+ if zone_id != domain.as_str() {
+ return (
+ StatusCode::BAD_REQUEST,
+ Json(json!({
+ "error": "Invalid domain",
+ "message": "Domain must be a root domain"
+ })),
+ );
+ }
+
+ let zone = db::zones::create_zone(&pool, &zone_id, user.sub).await;
+ match zone {
+ Ok(zone) => (StatusCode::OK, Json(json!(zone))),
+ Err(err) => match err {
+ Error::Database(e) if e.code().unwrap_or(std::borrow::Cow::Borrowed("")) == "23505" => {
+ (
+ StatusCode::BAD_REQUEST,
+ Json(json!({"error": "That zone already exists"})),
+ )
+ }
+ _ => (
+ StatusCode::INTERNAL_SERVER_ERROR,
+ Json(json!({ "error": format!("{:?}", err) })),
+ ),
+ },
+ }
}
pub async fn get_zone(
- Path(_id): Path<String>,
- Jwt(_user): Jwt,
- Extension(_pool): Extension<Arc<Pool<Postgres>>>,
+ Path(id): Path<String>,
+ Jwt(user): Jwt,
+ Extension(pool): Extension<Arc<Pool<Postgres>>>,
) -> impl IntoResponse {
- (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({"error": "Not implemented"})));
+ let domain = ensure_trailing_dot(&id);
+
+ let zone = db::zones::get_zone(&pool, &domain).await;
+ if zone.is_err() {
+ return (
+ StatusCode::NOT_FOUND,
+ Json(json!({ "error": format!("Zone {domain} not found") })),
+ );
+ }
+ let zone = zone.unwrap();
+
+ if zone.owner_uuid != user.sub {
+ return (
+ StatusCode::FORBIDDEN,
+ Json(json!({ "error": "You do have permissions to view this zone" })),
+ );
+ }
+
+ (StatusCode::OK, Json(json!(zone)))
+}
+
+fn ensure_trailing_dot(domain: &str) -> String {
+ if domain.ends_with('.') {
+ return domain.to_string();
+ }
+ format!("{domain}.")
}