add some kind of authorization #5
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -62,6 +62,7 @@ impl From<UserError> 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)
|
||||
}
|
||||
|
|
|
@ -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<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]
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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/<zone>/records")]
|
||||
pub async fn get_zone_records(
|
||||
mut client: dns::DnsClient,
|
||||
conn: DbConn,
|
||||
user_info: Result<UserInfo, ErrorResponse>,
|
||||
zone: dns::AbsoluteName
|
||||
) -> 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);
|
||||
query.await.map_err(make_500)?
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue