From fa1463683551c02333135c8c0728fe02f27af30b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Berthaud-M=C3=BCller?= Date: Mon, 5 Apr 2021 18:56:15 -0400 Subject: [PATCH] add some kind of authorizations --- .../2021-03-26-164945_create_users/down.sql | 1 + .../2021-03-26-164945_create_users/up.sql | 26 ++++++---- src/models/dns.rs | 1 - src/models/errors.rs | 1 + src/models/users.rs | 50 ++++++++++++++++--- src/routes/zones.rs | 17 +++++-- src/schema.rs | 33 ++++++++---- 7 files changed, 97 insertions(+), 32 deletions(-) diff --git a/migrations/2021-03-26-164945_create_users/down.sql b/migrations/2021-03-26-164945_create_users/down.sql index aa0235f..32dbc6c 100644 --- a/migrations/2021-03-26-164945_create_users/down.sql +++ b/migrations/2021-03-26-164945_create_users/down.sql @@ -1,4 +1,5 @@ -- This file should undo anything in `up.sql` DROP TABLE localuser; DROP TABLE user; +DROP TABLE zone; DROP TABLE user_zone; diff --git a/migrations/2021-03-26-164945_create_users/up.sql b/migrations/2021-03-26-164945_create_users/up.sql index 152e834..de90533 100644 --- a/migrations/2021-03-26-164945_create_users/up.sql +++ b/migrations/2021-03-26-164945_create_users/up.sql @@ -1,19 +1,25 @@ -- Your SQL goes here CREATE TABLE localuser ( - user_id VARCHAR NOT NULL PRIMARY KEY, - username VARCHAR NOT NULL UNIQUE, - password VARCHAR NOT NULL, + `user_id` VARCHAR NOT NULL PRIMARY KEY, + `username` VARCHAR NOT NULL UNIQUE, + `password` VARCHAR NOT NULL, + `role` TEXT CHECK(role IN ('admin', 'zoneadmin')) NOT NULL, -- note: migrate to postgres so enum are actually a thing FOREIGN KEY(user_id) REFERENCES user(id) ); CREATE TABLE user ( - id VARCHAR NOT NULL PRIMARY KEY, - role TEXT CHECK(role IN ('admin', 'zoneadmin')) NOT NULL -- note: migrate to postgres so enum are actually a thing + `id` VARCHAR NOT NULL PRIMARY KEY ); CREATE TABLE user_zone ( - user_id VARCHAR NOT NULL, - zone VARCHAR NOT NULL, - PRIMARY KEY(user_id, zone), - FOREIGN KEY(user_id) REFERENCES user(id) -) + `user_id` VARCHAR NOT NULL, + `zone_id` VARCHAR NOT NULL, + PRIMARY KEY(user_id, zone_id), + FOREIGN KEY(user_id) REFERENCES user(id), + FOREIGN KEY(zone_id) REFERENCES zone(id) +); + +CREATE TABLE zone ( + `id` VARCHAR NOT NULL PRIMARY KEY, + `name` VARCHAR NOT NULL UNIQUE +); diff --git a/src/models/dns.rs b/src/models/dns.rs index 9e632c7..cd4ea90 100644 --- a/src/models/dns.rs +++ b/src/models/dns.rs @@ -16,7 +16,6 @@ use trust_dns_proto::iocompat::AsyncIoTokioAsStd; use super::trust_dns_types::{self, Name}; use crate::config::Config; -use crate::models::errors::make_500; #[derive(Deserialize, Serialize)] diff --git a/src/models/errors.rs b/src/models/errors.rs index c20cf43..71c463d 100644 --- a/src/models/errors.rs +++ b/src/models/errors.rs @@ -62,6 +62,7 @@ impl From for ErrorResponse { UserError::ExpiredToken => ErrorResponse::new(Status::Unauthorized, "The provided token has expired".into()), UserError::MalformedHeader => ErrorResponse::new(Status::BadRequest, "Malformed authorization header".into()), UserError::PermissionDenied => ErrorResponse::new(Status::Forbidden, "Bearer is not authorized to access the resource".into()), + UserError::ZoneNotFound => ErrorResponse::new(Status::NotFound, "DNS zone does not exists".into()), UserError::DbError(e) => make_500(e), UserError::PasswordError(e) => make_500(e) } diff --git a/src/models/users.rs b/src/models/users.rs index 21e4c7f..2e4d319 100644 --- a/src/models/users.rs +++ b/src/models/users.rs @@ -42,7 +42,6 @@ pub enum Role { #[table_name = "user"] pub struct User { pub id: String, - pub role: Role, } #[derive(Debug, Queryable, Identifiable, Insertable)] @@ -52,6 +51,22 @@ pub struct LocalUser { pub user_id: String, pub username: String, pub password: String, + pub role: Role, +} + +#[derive(Debug, Queryable, Identifiable, Insertable)] +#[table_name = "user_zone"] +#[primary_key(user_id, zone_id)] +pub struct UserZone { + pub user_id: String, + pub zone_id: String, +} + +#[derive(Debug, Queryable, Identifiable, Insertable)] +#[table_name = "zone"] +pub struct Zone { + pub id: String, + pub name: String, } #[derive(Debug, Deserialize)] @@ -95,6 +110,28 @@ pub struct UserInfo { pub username: String, } +impl UserInfo { + pub fn is_admin(&self) -> bool { + matches!(self.role, Role::Admin) + } + + pub fn get_zone(&self, conn: &diesel::SqliteConnection, zone_name: &str) -> Result { + use crate::schema::user_zone::dsl::*; + use crate::schema::zone::dsl::*; + + let (res_zone, _): (Zone, UserZone) = zone.inner_join(user_zone) + .filter(name.eq(zone_name)) + .filter(user_id.eq(&self.id)) + .get_result(conn) + .map_err(|e| match e { + DieselError::NotFound => UserError::ZoneNotFound, + other => UserError::DbError(other) + })?; + + Ok(res_zone) + } +} + #[rocket::async_trait] impl<'r> FromRequest<'r> for UserInfo { type Error = ErrorResponse; @@ -140,6 +177,7 @@ impl<'r> FromRequest<'r> for UserInfo { #[derive(Debug)] pub enum UserError { + ZoneNotFound, NotFound, UserExists, BadToken, @@ -175,19 +213,19 @@ impl LocalUser { let new_user = User { id: new_user_id.clone(), - // TODO: Use role from request - role: Role::ZoneAdmin, }; let new_localuser = LocalUser { user_id: new_user_id, username: user_request.username.clone(), password: make_password_with_algorithm(&user_request.password, Algorithm::Argon2), + // TODO: Use role from request + role: Role::ZoneAdmin, }; let res = UserInfo { id: new_user.id.clone(), - role: new_user.role.clone(), + role: new_localuser.role.clone(), username: new_localuser.username.clone(), }; @@ -225,7 +263,7 @@ impl LocalUser { Ok(UserInfo { id: client_user.id, - role: client_user.role, + role: client_localuser.role, username: client_localuser.username, }) } @@ -240,7 +278,7 @@ impl LocalUser { Ok(UserInfo { id: client_user.id, - role: client_user.role, + role: client_localuser.role, username: client_localuser.username, }) } diff --git a/src/routes/zones.rs b/src/routes/zones.rs index 93ce3d8..d5ee92d 100644 --- a/src/routes/zones.rs +++ b/src/routes/zones.rs @@ -3,10 +3,10 @@ use rocket::http::Status; use rocket_contrib::json::Json; use trust_dns_client::client::ClientHandle; -use trust_dns_client::op::{DnsResponse, ResponseCode}; +use trust_dns_client::op::ResponseCode; use trust_dns_client::rr::{DNSClass, RecordType}; -use crate::models::dns; +use crate::{DbConn, models::dns}; use crate::models::errors::{ErrorResponse, make_500}; use crate::models::users::UserInfo; @@ -14,12 +14,21 @@ use crate::models::users::UserInfo; #[get("/zones//records")] pub async fn get_zone_records( mut client: dns::DnsClient, + conn: DbConn, user_info: Result, zone: dns::AbsoluteName ) -> Result>, ErrorResponse> { - println!("{:#?}", user_info?); - let response: DnsResponse = { + let user_info = user_info?; + + if !user_info.is_admin() { + let zone_name = zone.clone().to_string(); + conn.run(move |c| { + dbg!(user_info.get_zone(c, &zone_name)) + }).await?; + } + + let response = { let query = client.query((*zone).clone(), DNSClass::IN, RecordType::AXFR); query.await.map_err(make_500)? }; diff --git a/src/schema.rs b/src/schema.rs index 2e8d5ac..775c738 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -1,19 +1,11 @@ table! { use diesel::sql_types::*; + use crate::models::users::*; localuser (user_id) { user_id -> Text, username -> Text, password -> Text, - } -} - -table! { - use diesel::sql_types::*; - use crate::models::users::*; - - user (id) { - id -> Text, role -> RoleMapping, } } @@ -21,17 +13,36 @@ table! { table! { use diesel::sql_types::*; - user_zone (user_id, zone) { + user (id) { + id -> Text, + } +} + +table! { + use diesel::sql_types::*; + + user_zone (user_id, zone_id) { user_id -> Text, - zone -> Text, + zone_id -> Text, + } +} + +table! { + use diesel::sql_types::*; + + zone (id) { + id -> Text, + name -> Text, } } joinable!(localuser -> user (user_id)); joinable!(user_zone -> user (user_id)); +joinable!(user_zone -> zone (zone_id)); allow_tables_to_appear_in_same_query!( localuser, user, user_zone, + zone, );