Add authentication #1
|
@ -795,6 +795,7 @@ dependencies = [
|
||||||
"rocket_contrib",
|
"rocket_contrib",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
"trust-dns-client",
|
"trust-dns-client",
|
||||||
"trust-dns-proto",
|
"trust-dns-proto",
|
||||||
|
|
|
@ -22,3 +22,4 @@ djangohashers = { version = "1.4.0", features = ["with_argon2"], default-feature
|
||||||
jsonwebtoken = "7.2.0"
|
jsonwebtoken = "7.2.0"
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
humantime = "2.1.0"
|
humantime = "2.1.0"
|
||||||
|
tokio = "1"
|
||||||
|
|
|
@ -3,3 +3,4 @@
|
||||||
|
|
||||||
[print_schema]
|
[print_schema]
|
||||||
file = "src/schema.rs"
|
file = "src/schema.rs"
|
||||||
|
import_types = ["diesel::sql_types::*", "crate::models::users::*"]
|
||||||
|
|
|
@ -8,5 +8,5 @@ CREATE TABLE localuser (
|
||||||
|
|
||||||
CREATE TABLE user (
|
CREATE TABLE user (
|
||||||
id VARCHAR NOT NULL PRIMARY KEY,
|
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
|
||||||
);
|
);
|
||||||
|
|
24
src/main.rs
24
src/main.rs
|
@ -4,8 +4,15 @@
|
||||||
#[macro_use] extern crate rocket_contrib;
|
#[macro_use] extern crate rocket_contrib;
|
||||||
#[macro_use] extern crate diesel;
|
#[macro_use] extern crate diesel;
|
||||||
|
|
||||||
use trust_dns_client::client::SyncClient;
|
use trust_dns_client::client::AsyncClient;
|
||||||
use trust_dns_client::tcp::TcpClientConnection;
|
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 models;
|
||||||
mod config;
|
mod config;
|
||||||
|
@ -19,19 +26,22 @@ use routes::zones::*;
|
||||||
#[database("db")]
|
#[database("db")]
|
||||||
pub struct DbConn(diesel::SqliteConnection);
|
pub struct DbConn(diesel::SqliteConnection);
|
||||||
|
|
||||||
type DnsClient = SyncClient<TcpClientConnection>;
|
type DnsClient = Arc<Mutex<AsyncClient>>;
|
||||||
|
|
||||||
|
|
||||||
#[launch]
|
#[launch]
|
||||||
fn rocket() -> rocket::Rocket {
|
async fn rocket() -> rocket::Rocket {
|
||||||
let app_config = config::load("config.toml".into());
|
let app_config = config::load("config.toml".into());
|
||||||
println!("{:#?}", app_config);
|
println!("{:#?}", app_config);
|
||||||
|
|
||||||
let conn = TcpClientConnection::new(app_config.dns.server).unwrap();
|
let (stream, handle) = TcpClientStream::<AsyncIoTokioAsStd<TokioTcpStream>>::new(app_config.dns.server);
|
||||||
let client = SyncClient::new(conn);
|
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()
|
rocket::ignite()
|
||||||
.manage(client)
|
.manage(Arc::new(Mutex::new(client)))
|
||||||
.manage(app_config)
|
.manage(app_config)
|
||||||
.attach(DbConn::fairing())
|
.attach(DbConn::fairing())
|
||||||
.mount("/api/v1", routes![get_zone_records, create_auth_token, create_user])
|
.mount("/api/v1", routes![get_zone_records, create_auth_token, create_user])
|
||||||
|
|
|
@ -27,10 +27,11 @@ const BEARER: &'static str = "Bearer ";
|
||||||
const AUTH_HEADER: &'static str = "Authentication";
|
const AUTH_HEADER: &'static str = "Authentication";
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, DbEnum, Deserialize)]
|
#[derive(Debug, DbEnum, Deserialize, Clone)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub enum Role {
|
pub enum Role {
|
||||||
Admin,
|
Admin,
|
||||||
|
#[db_rename = "zoneadmin"]
|
||||||
ZoneAdmin,
|
ZoneAdmin,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +41,7 @@ pub enum Role {
|
||||||
#[table_name = "user"]
|
#[table_name = "user"]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub role: String,
|
pub role: Role,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Queryable, Identifiable, Insertable)]
|
#[derive(Debug, Queryable, Identifiable, Insertable)]
|
||||||
|
@ -89,7 +90,7 @@ pub struct AuthTokenRequest {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UserInfo {
|
pub struct UserInfo {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub role: String,
|
pub role: Role,
|
||||||
pub username: String,
|
pub username: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,7 +177,7 @@ 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
|
// TODO: Use role from request
|
||||||
role: "zoneadmin".into(),
|
role: Role::ZoneAdmin,
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_localuser = LocalUser {
|
let new_localuser = LocalUser {
|
||||||
|
|
|
@ -3,28 +3,31 @@ use rocket::http::Status;
|
||||||
|
|
||||||
use rocket_contrib::json::Json;
|
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::op::{DnsResponse, ResponseCode};
|
||||||
use trust_dns_client::rr::{DNSClass, Name, RecordType};
|
use trust_dns_client::rr::{DNSClass, Name, RecordType};
|
||||||
|
|
||||||
use crate::models::dns;
|
use crate::models::dns;
|
||||||
use crate::models::errors::ErrorResponse;
|
use crate::models::errors::{ErrorResponse, make_500};
|
||||||
use crate::models::users::UserInfo;
|
use crate::models::users::UserInfo;
|
||||||
use crate::DnsClient;
|
use crate::DnsClient;
|
||||||
|
|
||||||
|
|
||||||
#[get("/zones/<zone>/records")]
|
#[get("/zones/<zone>/records")]
|
||||||
pub fn get_zone_records(
|
pub async fn get_zone_records(
|
||||||
client: State<DnsClient>,
|
client: State<'_, DnsClient>,
|
||||||
user_info: Result<UserInfo, ErrorResponse>,
|
user_info: Result<UserInfo, ErrorResponse>,
|
||||||
zone: String
|
zone: String
|
||||||
) -> Result<Json<Vec<dns::Record>>, ErrorResponse> {
|
) -> Result<Json<Vec<dns::Record>>, ErrorResponse> {
|
||||||
user_info?;
|
println!("{:#?}", user_info?);
|
||||||
|
|
||||||
// TODO: Implement FromParam for Name
|
// TODO: Implement FromParam for Name
|
||||||
let name = Name::from_utf8(&zone).unwrap();
|
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 {
|
if response.response_code() != ResponseCode::NoError {
|
||||||
return ErrorResponse::new(
|
return ErrorResponse::new(
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
table! {
|
table! {
|
||||||
|
use diesel::sql_types::*;
|
||||||
|
|
||||||
localuser (user_id) {
|
localuser (user_id) {
|
||||||
user_id -> Text,
|
user_id -> Text,
|
||||||
username -> Text,
|
username -> Text,
|
||||||
|
@ -7,9 +9,12 @@ table! {
|
||||||
}
|
}
|
||||||
|
|
||||||
table! {
|
table! {
|
||||||
|
use diesel::sql_types::*;
|
||||||
|
use crate::models::users::*;
|
||||||
|
|
||||||
user (id) {
|
user (id) {
|
||||||
id -> Text,
|
id -> Text,
|
||||||
role -> Text,
|
role -> RoleMapping,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue