Commit fa146368 authored by Gaël Berthaud-Müller's avatar Gaël Berthaud-Müller
Browse files

add some kind of authorizations

parent 4105fbeb
-- 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;
-- 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; ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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; ...@@ -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; ...@@ -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)?
}; };
......
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,
role -> RoleMapping,
} }
} }
table! { table! {
use diesel::sql_types::*; use diesel::sql_types::*;
use crate::models::users::*;
user (id) { user (id) {
id -> Text, id -> Text,
role -> RoleMapping,
} }
} }
table! { table! {
use diesel::sql_types::*; use diesel::sql_types::*;
user_zone (user_id, zone) { 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,
); );
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment