diff options
author | Galen Guyer <galen@galenguyer.com> | 2022-05-21 19:49:35 -0400 |
---|---|---|
committer | Galen Guyer <galen@galenguyer.com> | 2022-05-21 19:49:35 -0400 |
commit | 3613656a396b42d70259e9ff325ab9fcf0b55dd1 (patch) | |
tree | a33cbdd36da54b6dbef92feebd8945799c12292f | |
parent | d2da324c4c15db3b137fbeaebf0568f65eb2ad67 (diff) |
restore zone functionality without powerdns
-rw-r--r-- | Cargo.lock | 26 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/db/models.rs | 12 | ||||
-rw-r--r-- | src/db/zones.rs | 5 | ||||
-rw-r--r-- | src/main.rs | 2 | ||||
-rw-r--r-- | src/routes/v1/users.rs | 5 | ||||
-rw-r--r-- | src/routes/v1/zones.rs | 85 |
7 files changed, 118 insertions, 18 deletions
@@ -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" @@ -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}.") } |