From ebe1552032a1bc245142a19769176fc2c5718e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Sat, 3 Apr 2021 02:16:54 -0400 Subject: [PATCH] use enum for role and fix dns client for async --- Cargo.lock | 1 + Cargo.toml | 1 + diesel.toml | 1 + .../2021-03-26-164945_create_users/up.sql | 2 +- src/main.rs | 24 +++++++++++++------ src/models/users.rs | 11 +++++---- src/routes/zones.rs | 15 +++++++----- src/schema.rs | 7 +++++- 8 files changed, 42 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c6e4aaf..d8a74cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -795,6 +795,7 @@ dependencies = [ "rocket_contrib", "serde", "serde_json", + "tokio", "toml", "trust-dns-client", "trust-dns-proto", diff --git a/Cargo.toml b/Cargo.toml index 2a86380..c6da57c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,3 +22,4 @@ djangohashers = { version = "1.4.0", features = ["with_argon2"], default-feature jsonwebtoken = "7.2.0" chrono = { version = "0.4", features = ["serde"] } humantime = "2.1.0" +tokio = "1" diff --git a/diesel.toml b/diesel.toml index 92267c8..764d6e0 100644 --- a/diesel.toml +++ b/diesel.toml @@ -3,3 +3,4 @@ [print_schema] file = "src/schema.rs" +import_types = ["diesel::sql_types::*", "crate::models::users::*"] diff --git a/migrations/2021-03-26-164945_create_users/up.sql b/migrations/2021-03-26-164945_create_users/up.sql index c9753af..0198256 100644 --- a/migrations/2021-03-26-164945_create_users/up.sql +++ b/migrations/2021-03-26-164945_create_users/up.sql @@ -8,5 +8,5 @@ CREATE TABLE localuser ( CREATE TABLE user ( id VARCHAR NOT NULL PRIMARY KEY, - role VARCHAR NOT NULL + role TEXT CHECK(role IN ('admin', 'zoneadmin')) NOT NULL -- note: migrate to postgres so enum are actually a thing ); diff --git a/src/main.rs b/src/main.rs index b0baa7f..b24f9b6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,8 +4,15 @@ #[macro_use] extern crate rocket_contrib; #[macro_use] extern crate diesel; -use trust_dns_client::client::SyncClient; -use trust_dns_client::tcp::TcpClientConnection; +use trust_dns_client::client::AsyncClient; +use trust_dns_client::tcp::TcpClientStream; +use trust_dns_proto::xfer::dns_multiplexer::DnsMultiplexer; +use trust_dns_proto::iocompat::AsyncIoTokioAsStd; +use trust_dns_client::rr::dnssec::Signer; +use tokio::net::TcpStream as TokioTcpStream; +use tokio::task; + +use std::sync::{Arc, Mutex}; mod models; mod config; @@ -19,19 +26,22 @@ use routes::zones::*; #[database("db")] pub struct DbConn(diesel::SqliteConnection); -type DnsClient = SyncClient; +type DnsClient = Arc>; #[launch] -fn rocket() -> rocket::Rocket { +async fn rocket() -> rocket::Rocket { let app_config = config::load("config.toml".into()); println!("{:#?}", app_config); - let conn = TcpClientConnection::new(app_config.dns.server).unwrap(); - let client = SyncClient::new(conn); + let (stream, handle) = TcpClientStream::>::new(app_config.dns.server); + let multiplexer = DnsMultiplexer::<_, Signer>::new(stream, handle, None); + let client = AsyncClient::connect(multiplexer); + let (client, bg) = client.await.expect("connection failed"); + task::spawn(bg); rocket::ignite() - .manage(client) + .manage(Arc::new(Mutex::new(client))) .manage(app_config) .attach(DbConn::fairing()) .mount("/api/v1", routes![get_zone_records, create_auth_token, create_user]) diff --git a/src/models/users.rs b/src/models/users.rs index 67e5df5..a770b66 100644 --- a/src/models/users.rs +++ b/src/models/users.rs @@ -27,10 +27,11 @@ const BEARER: &'static str = "Bearer "; const AUTH_HEADER: &'static str = "Authentication"; -#[derive(Debug, DbEnum, Deserialize)] -#[serde(rename_all = "snake_case")] +#[derive(Debug, DbEnum, Deserialize, Clone)] +#[serde(rename_all = "kebab-case")] pub enum Role { Admin, + #[db_rename = "zoneadmin"] ZoneAdmin, } @@ -40,7 +41,7 @@ pub enum Role { #[table_name = "user"] pub struct User { pub id: String, - pub role: String, + pub role: Role, } #[derive(Debug, Queryable, Identifiable, Insertable)] @@ -89,7 +90,7 @@ pub struct AuthTokenRequest { #[derive(Debug)] pub struct UserInfo { pub id: String, - pub role: String, + pub role: Role, pub username: String, } @@ -176,7 +177,7 @@ impl LocalUser { let new_user = User { id: new_user_id.clone(), // TODO: Use role from request - role: "zoneadmin".into(), + role: Role::ZoneAdmin, }; let new_localuser = LocalUser { diff --git a/src/routes/zones.rs b/src/routes/zones.rs index bc72497..0d1ec2d 100644 --- a/src/routes/zones.rs +++ b/src/routes/zones.rs @@ -3,28 +3,31 @@ use rocket::http::Status; use rocket_contrib::json::Json; -use trust_dns_client::client::{Client}; +use trust_dns_client::client::ClientHandle; use trust_dns_client::op::{DnsResponse, ResponseCode}; use trust_dns_client::rr::{DNSClass, Name, RecordType}; use crate::models::dns; -use crate::models::errors::ErrorResponse; +use crate::models::errors::{ErrorResponse, make_500}; use crate::models::users::UserInfo; use crate::DnsClient; #[get("/zones//records")] -pub fn get_zone_records( - client: State, +pub async fn get_zone_records( + client: State<'_, DnsClient>, user_info: Result, zone: String ) -> Result>, ErrorResponse> { - user_info?; + println!("{:#?}", user_info?); // TODO: Implement FromParam for Name let name = Name::from_utf8(&zone).unwrap(); - let response: DnsResponse = client.query(&name, DNSClass::IN, RecordType::AXFR).unwrap(); + let response: DnsResponse = { + let query = client.lock().unwrap().query(name.clone(), DNSClass::IN, RecordType::AXFR); + query.await.map_err(make_500)? + }; if response.response_code() != ResponseCode::NoError { return ErrorResponse::new( diff --git a/src/schema.rs b/src/schema.rs index 21093d7..c1bd1aa 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -1,4 +1,6 @@ table! { + use diesel::sql_types::*; + localuser (user_id) { user_id -> Text, username -> Text, @@ -7,9 +9,12 @@ table! { } table! { + use diesel::sql_types::*; + use crate::models::users::*; + user (id) { id -> Text, - role -> Text, + role -> RoleMapping, } }