add some kind of authorization #5
					 7 changed files with 97 additions and 32 deletions
				
			
		add some kind of authorizations
				commit
				
					
					
						fa14636835
					
				
			
		| 
						 | 
					@ -1,4 +1,5 @@
 | 
				
			||||||
-- This file should undo anything in `up.sql`
 | 
					-- This file should undo anything in `up.sql`
 | 
				
			||||||
DROP TABLE localuser;
 | 
					DROP TABLE localuser;
 | 
				
			||||||
DROP TABLE user;
 | 
					DROP TABLE user;
 | 
				
			||||||
 | 
					DROP TABLE zone;
 | 
				
			||||||
DROP TABLE user_zone;
 | 
					DROP TABLE user_zone;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,19 +1,25 @@
 | 
				
			||||||
-- Your SQL goes here
 | 
					-- Your SQL goes here
 | 
				
			||||||
CREATE TABLE localuser (
 | 
					CREATE TABLE localuser (
 | 
				
			||||||
  user_id VARCHAR NOT NULL PRIMARY KEY,
 | 
					  `user_id` VARCHAR NOT NULL PRIMARY KEY,
 | 
				
			||||||
  username VARCHAR NOT NULL UNIQUE,
 | 
					  `username` VARCHAR NOT NULL UNIQUE,
 | 
				
			||||||
  password VARCHAR NOT NULL,
 | 
					  `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)
 | 
					  FOREIGN KEY(user_id) REFERENCES user(id)
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE user (
 | 
					CREATE TABLE user (
 | 
				
			||||||
  id VARCHAR NOT NULL PRIMARY KEY,
 | 
					  `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
 | 
					 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREATE TABLE user_zone (
 | 
					CREATE TABLE user_zone (
 | 
				
			||||||
  user_id VARCHAR NOT NULL,
 | 
					  `user_id` VARCHAR NOT NULL,
 | 
				
			||||||
  zone VARCHAR NOT NULL,
 | 
					  `zone_id` VARCHAR NOT NULL,
 | 
				
			||||||
  PRIMARY KEY(user_id, zone),
 | 
					  PRIMARY KEY(user_id, zone_id),
 | 
				
			||||||
  FOREIGN KEY(user_id) REFERENCES user(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
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,7 +16,6 @@ use trust_dns_proto::iocompat::AsyncIoTokioAsStd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::trust_dns_types::{self, Name};
 | 
					use super::trust_dns_types::{self, Name};
 | 
				
			||||||
use crate::config::Config;
 | 
					use crate::config::Config;
 | 
				
			||||||
use crate::models::errors::make_500;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Deserialize, Serialize)]
 | 
					#[derive(Deserialize, Serialize)]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -62,6 +62,7 @@ impl From<UserError> for ErrorResponse {
 | 
				
			||||||
            UserError::ExpiredToken => ErrorResponse::new(Status::Unauthorized, "The provided token has expired".into()),
 | 
					            UserError::ExpiredToken => ErrorResponse::new(Status::Unauthorized, "The provided token has expired".into()),
 | 
				
			||||||
            UserError::MalformedHeader => ErrorResponse::new(Status::BadRequest, "Malformed authorization header".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::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::DbError(e) => make_500(e),
 | 
				
			||||||
            UserError::PasswordError(e) => make_500(e)
 | 
					            UserError::PasswordError(e) => make_500(e)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,7 +42,6 @@ pub enum Role {
 | 
				
			||||||
#[table_name = "user"]
 | 
					#[table_name = "user"]
 | 
				
			||||||
pub struct User {
 | 
					pub struct User {
 | 
				
			||||||
    pub id: String,
 | 
					    pub id: String,
 | 
				
			||||||
    pub role: Role,
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug, Queryable, Identifiable, Insertable)]
 | 
					#[derive(Debug, Queryable, Identifiable, Insertable)]
 | 
				
			||||||
| 
						 | 
					@ -52,6 +51,22 @@ pub struct LocalUser {
 | 
				
			||||||
    pub user_id: String,
 | 
					    pub user_id: String,
 | 
				
			||||||
    pub username: String,
 | 
					    pub username: String,
 | 
				
			||||||
    pub password: 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)]
 | 
					#[derive(Debug, Deserialize)]
 | 
				
			||||||
| 
						 | 
					@ -95,6 +110,28 @@ pub struct UserInfo {
 | 
				
			||||||
    pub username: String,
 | 
					    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<Zone, UserError> {
 | 
				
			||||||
 | 
					        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]
 | 
					#[rocket::async_trait]
 | 
				
			||||||
impl<'r> FromRequest<'r> for UserInfo {
 | 
					impl<'r> FromRequest<'r> for UserInfo {
 | 
				
			||||||
    type Error = ErrorResponse;
 | 
					    type Error = ErrorResponse;
 | 
				
			||||||
| 
						 | 
					@ -140,6 +177,7 @@ impl<'r> FromRequest<'r> for UserInfo {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
pub enum UserError {
 | 
					pub enum UserError {
 | 
				
			||||||
 | 
					    ZoneNotFound,
 | 
				
			||||||
    NotFound,
 | 
					    NotFound,
 | 
				
			||||||
    UserExists,
 | 
					    UserExists,
 | 
				
			||||||
    BadToken,
 | 
					    BadToken,
 | 
				
			||||||
| 
						 | 
					@ -175,19 +213,19 @@ impl LocalUser {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let new_user = User {
 | 
					        let new_user = User {
 | 
				
			||||||
            id: new_user_id.clone(),
 | 
					            id: new_user_id.clone(),
 | 
				
			||||||
            // TODO: Use role from request
 | 
					 | 
				
			||||||
            role: Role::ZoneAdmin,
 | 
					 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let new_localuser = LocalUser {
 | 
					        let new_localuser = LocalUser {
 | 
				
			||||||
            user_id: new_user_id,
 | 
					            user_id: new_user_id,
 | 
				
			||||||
            username: user_request.username.clone(),
 | 
					            username: user_request.username.clone(),
 | 
				
			||||||
            password: make_password_with_algorithm(&user_request.password, Algorithm::Argon2),
 | 
					            password: make_password_with_algorithm(&user_request.password, Algorithm::Argon2),
 | 
				
			||||||
 | 
					            // TODO: Use role from request
 | 
				
			||||||
 | 
					            role: Role::ZoneAdmin,
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let res = UserInfo {
 | 
					        let res = UserInfo {
 | 
				
			||||||
            id: new_user.id.clone(),
 | 
					            id: new_user.id.clone(),
 | 
				
			||||||
            role: new_user.role.clone(),
 | 
					            role: new_localuser.role.clone(),
 | 
				
			||||||
            username: new_localuser.username.clone(),
 | 
					            username: new_localuser.username.clone(),
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -225,7 +263,7 @@ impl LocalUser {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(UserInfo {
 | 
					        Ok(UserInfo {
 | 
				
			||||||
            id: client_user.id,
 | 
					            id: client_user.id,
 | 
				
			||||||
            role: client_user.role,
 | 
					            role: client_localuser.role,
 | 
				
			||||||
            username: client_localuser.username,
 | 
					            username: client_localuser.username,
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -240,7 +278,7 @@ impl LocalUser {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(UserInfo {
 | 
					        Ok(UserInfo {
 | 
				
			||||||
            id: client_user.id,
 | 
					            id: client_user.id,
 | 
				
			||||||
            role: client_user.role,
 | 
					            role: client_localuser.role,
 | 
				
			||||||
            username: client_localuser.username,
 | 
					            username: client_localuser.username,
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,10 +3,10 @@ use rocket::http::Status;
 | 
				
			||||||
use rocket_contrib::json::Json;
 | 
					use rocket_contrib::json::Json;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use trust_dns_client::client::ClientHandle;
 | 
					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 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::errors::{ErrorResponse, make_500};
 | 
				
			||||||
use crate::models::users::UserInfo;
 | 
					use crate::models::users::UserInfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,12 +14,21 @@ use crate::models::users::UserInfo;
 | 
				
			||||||
#[get("/zones/<zone>/records")]
 | 
					#[get("/zones/<zone>/records")]
 | 
				
			||||||
pub async fn get_zone_records(
 | 
					pub async fn get_zone_records(
 | 
				
			||||||
    mut client: dns::DnsClient,
 | 
					    mut client: dns::DnsClient,
 | 
				
			||||||
 | 
					    conn: DbConn,
 | 
				
			||||||
    user_info: Result<UserInfo, ErrorResponse>,
 | 
					    user_info: Result<UserInfo, ErrorResponse>,
 | 
				
			||||||
    zone: dns::AbsoluteName
 | 
					    zone: dns::AbsoluteName
 | 
				
			||||||
) -> Result<Json<Vec<dns::Record>>, ErrorResponse> {
 | 
					) -> Result<Json<Vec<dns::Record>>, 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);
 | 
					        let query = client.query((*zone).clone(), DNSClass::IN, RecordType::AXFR);
 | 
				
			||||||
        query.await.map_err(make_500)?
 | 
					        query.await.map_err(make_500)?
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,19 +1,11 @@
 | 
				
			||||||
table! {
 | 
					table! {
 | 
				
			||||||
    use diesel::sql_types::*;
 | 
					    use diesel::sql_types::*;
 | 
				
			||||||
 | 
					    use crate::models::users::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    localuser (user_id) {
 | 
					    localuser (user_id) {
 | 
				
			||||||
        user_id -> Text,
 | 
					        user_id -> Text,
 | 
				
			||||||
        username -> Text,
 | 
					        username -> Text,
 | 
				
			||||||
        password -> Text,
 | 
					        password -> Text,
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
table! {
 | 
					 | 
				
			||||||
    use diesel::sql_types::*;
 | 
					 | 
				
			||||||
    use crate::models::users::*;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    user (id) {
 | 
					 | 
				
			||||||
        id -> Text,
 | 
					 | 
				
			||||||
        role -> RoleMapping,
 | 
					        role -> RoleMapping,
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -21,17 +13,36 @@ table! {
 | 
				
			||||||
table! {
 | 
					table! {
 | 
				
			||||||
    use diesel::sql_types::*;
 | 
					    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,
 | 
					        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!(localuser -> user (user_id));
 | 
				
			||||||
joinable!(user_zone -> user (user_id));
 | 
					joinable!(user_zone -> user (user_id));
 | 
				
			||||||
 | 
					joinable!(user_zone -> zone (zone_id));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
allow_tables_to_appear_in_same_query!(
 | 
					allow_tables_to_appear_in_same_query!(
 | 
				
			||||||
    localuser,
 | 
					    localuser,
 | 
				
			||||||
    user,
 | 
					    user,
 | 
				
			||||||
    user_zone,
 | 
					    user_zone,
 | 
				
			||||||
 | 
					    zone,
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue