add simple authentication by secret
Some checks are pending
continuous-integration/drone/push Build is running

This commit is contained in:
Johannes Heuel
2022-09-20 21:49:52 +02:00
parent 68fe42f7c1
commit 40ff666318
4 changed files with 97 additions and 49 deletions

1
Cargo.lock generated
View File

@@ -1903,6 +1903,7 @@ dependencies = [
"actix-web", "actix-web",
"clap", "clap",
"env_logger", "env_logger",
"futures",
"log", "log",
"serde_json", "serde_json",
"zoidberg_lib", "zoidberg_lib",

View File

@@ -12,7 +12,7 @@ use zoidberg_lib::types::{FetchResponse, Job, RegisterResponse, Status, Update};
const VERSION: &str = env!("CARGO_PKG_VERSION"); const VERSION: &str = env!("CARGO_PKG_VERSION");
fn build_client(secret: &str) -> Client { fn build_client(secret: &str) -> Client {
let cookie = format!("secret={}", secret); let cookie = format!("{}", secret);
let mut headers = header::HeaderMap::new(); let mut headers = header::HeaderMap::new();
headers.insert( headers.insert(
@@ -31,11 +31,12 @@ fn build_client(secret: &str) -> Client {
#[derive(Debug)] #[derive(Debug)]
struct Worker { struct Worker {
id: i32, id: i32,
secret: String,
} }
impl Worker { impl Worker {
async fn new(server: &str) -> Result<Worker, Box<dyn Error>> { async fn new(server: &str, secret: &str) -> Result<Worker, Box<dyn Error>> {
let res = build_client("some_secret") let res = build_client(secret)
.get(format!("{}/register", server)) .get(format!("{}/register", server))
.send() .send()
.await?; .await?;
@@ -43,7 +44,10 @@ impl Worker {
let body = res.text().await?; let body = res.text().await?;
let r: RegisterResponse = serde_json::from_str(&body)?; let r: RegisterResponse = serde_json::from_str(&body)?;
log::info!("registered worker with id: {}", &r.id); log::info!("registered worker with id: {}", &r.id);
Ok(Worker { id: r.id }) Ok(Worker {
id: r.id,
secret: secret.to_string(),
})
} }
async fn update(self: &Self, jobs: &[Job]) -> Result<(), Box<dyn Error>> { async fn update(self: &Self, jobs: &[Job]) -> Result<(), Box<dyn Error>> {
@@ -56,7 +60,7 @@ impl Worker {
}) })
.collect(); .collect();
let body = build_client("some_secret") let body = build_client(&self.secret)
.post("http://localhost:8080/update") .post("http://localhost:8080/update")
.json(&updates) .json(&updates)
.send() .send()
@@ -69,7 +73,7 @@ impl Worker {
} }
async fn fetch(self: &Self) -> Result<FetchResponse, Box<dyn Error>> { async fn fetch(self: &Self) -> Result<FetchResponse, Box<dyn Error>> {
let res = build_client("some_secret") let res = build_client(&self.secret)
.get("http://localhost:8080/fetch") .get("http://localhost:8080/fetch")
.send() .send()
.await?; .await?;
@@ -125,8 +129,12 @@ async fn main() -> Result<(), Box<dyn Error>> {
) )
.get_matches(); .get_matches();
let server = matches.value_of("server").unwrap(); let server = matches.value_of("server").unwrap();
let secret = std::env::var("ZOIDBERG_SECRET")
.expect("Please set the $ZOIDBERG_SECRET environment variable");
let client = Worker::new(server).await.expect("Could not create client"); let client = Worker::new(server, &secret)
.await
.expect("Could not create client");
let pause = time::Duration::from_secs(1); let pause = time::Duration::from_secs(1);
let long_pause = time::Duration::from_secs(20); let long_pause = time::Duration::from_secs(20);

View File

@@ -15,3 +15,4 @@ serde_json = "1.0"
clap = "3.2" clap = "3.2"
env_logger = "0.9" env_logger = "0.9"
log = "0.4" log = "0.4"
futures = "0.3.24"

View File

@@ -1,6 +1,11 @@
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder, Result}; use actix_web::error::ErrorBadRequest;
use actix_web::{
dev, get, middleware::Logger, post, web, App, Error, FromRequest, HttpRequest, HttpResponse,
HttpServer, Responder, Result,
};
use clap; use clap;
use env_logger::Env; use env_logger::Env;
use futures::future::{err, ok, Ready};
use log; use log;
use std::sync::Mutex; use std::sync::Mutex;
use zoidberg_lib::types::{FetchResponse, Job, RegisterResponse, StatusRequest, Update, Worker}; use zoidberg_lib::types::{FetchResponse, Job, RegisterResponse, StatusRequest, Update, Worker};
@@ -17,6 +22,41 @@ struct State {
jobs: Mutex<Vec<Job>>, jobs: Mutex<Vec<Job>>,
} }
impl State {
fn new() -> Self {
Self {
counter_workers: Mutex::new(0),
counter_jobs: Mutex::new(0),
workers: Mutex::new(Vec::new()),
new_jobs: Mutex::new(Vec::new()),
jobs: Mutex::new(Vec::new()),
}
}
}
struct Authorization {}
impl FromRequest for Authorization {
type Error = Error;
type Future = Ready<Result<Self, Self::Error>>;
fn from_request(req: &HttpRequest, _payload: &mut dev::Payload) -> Self::Future {
if let Some(head) = req.headers().get("cookie") {
if let Ok(cookie) = head.to_str() {
if let Some(secret) = req.app_data::<String>() {
println!("{} == {}", secret, cookie);
if secret == cookie {
return ok(Authorization {});
} else {
return err(ErrorBadRequest("no auth"));
}
}
}
}
err(ErrorBadRequest("no auth"))
}
}
#[get("/")] #[get("/")]
async fn index(data: web::Data<State>) -> impl Responder { async fn index(data: web::Data<State>) -> impl Responder {
let workers = data.workers.lock().unwrap(); let workers = data.workers.lock().unwrap();
@@ -26,7 +66,7 @@ async fn index(data: web::Data<State>) -> impl Responder {
} }
#[get("/register")] #[get("/register")]
async fn register(data: web::Data<State>) -> Result<impl Responder> { async fn register(data: web::Data<State>, _: Authorization) -> Result<impl Responder> {
let mut counter_workers = data.counter_workers.lock().unwrap(); let mut counter_workers = data.counter_workers.lock().unwrap();
*counter_workers += 1; *counter_workers += 1;
@@ -42,7 +82,7 @@ async fn register(data: web::Data<State>) -> Result<impl Responder> {
} }
#[get("/fetch")] #[get("/fetch")]
async fn fetch(data: web::Data<State>) -> Result<impl Responder> { async fn fetch(data: web::Data<State>, _: Authorization) -> Result<impl Responder> {
let mut new_jobs = data.new_jobs.lock().unwrap(); let mut new_jobs = data.new_jobs.lock().unwrap();
if let Some(j) = new_jobs.pop() { if let Some(j) = new_jobs.pop() {
return Ok(web::Json(FetchResponse::Jobs(vec![j]))); return Ok(web::Json(FetchResponse::Jobs(vec![j])));
@@ -54,6 +94,7 @@ async fn fetch(data: web::Data<State>) -> Result<impl Responder> {
async fn status( async fn status(
s: web::Json<Vec<StatusRequest>>, s: web::Json<Vec<StatusRequest>>,
data: web::Data<State>, data: web::Data<State>,
_: Authorization,
) -> Result<impl Responder> { ) -> Result<impl Responder> {
let jobs = data.jobs.lock().unwrap(); let jobs = data.jobs.lock().unwrap();
let status_updates: Vec<Job> = jobs let status_updates: Vec<Job> = jobs
@@ -66,7 +107,11 @@ async fn status(
} }
#[post("/update")] #[post("/update")]
async fn update(updates: web::Json<Vec<Update>>, data: web::Data<State>) -> Result<String> { async fn update(
updates: web::Json<Vec<Update>>,
data: web::Data<State>,
_: Authorization,
) -> Result<String> {
let mut jobs = data.jobs.lock().unwrap(); let mut jobs = data.jobs.lock().unwrap();
let mut n = 0; let mut n = 0;
for update in updates.iter() { for update in updates.iter() {
@@ -87,7 +132,11 @@ async fn update(updates: web::Json<Vec<Update>>, data: web::Data<State>) -> Resu
} }
#[post("/submit")] #[post("/submit")]
async fn submit(data: web::Data<State>, js: web::Json<Vec<Job>>) -> Result<impl Responder> { async fn submit(
data: web::Data<State>,
js: web::Json<Vec<Job>>,
_: Authorization,
) -> Result<impl Responder> {
let mut new_jobs = data.new_jobs.lock().unwrap(); let mut new_jobs = data.new_jobs.lock().unwrap();
let mut jobs = data.jobs.lock().unwrap(); let mut jobs = data.jobs.lock().unwrap();
let mut counter_jobs = data.counter_jobs.lock().unwrap(); let mut counter_jobs = data.counter_jobs.lock().unwrap();
@@ -113,21 +162,20 @@ async fn submit(data: web::Data<State>, js: web::Json<Vec<Job>>) -> Result<impl
async fn main() -> std::io::Result<()> { async fn main() -> std::io::Result<()> {
env_logger::Builder::from_env(Env::default().default_filter_or("zoidberg_server=info")).init(); env_logger::Builder::from_env(Env::default().default_filter_or("zoidberg_server=info")).init();
let secret = std::env::var("ZOIDBERG_SECRET")
.expect("Please set the $ZOIDBERG_SECRET environment variable");
let _matches = clap::App::new("Zoidberg server") let _matches = clap::App::new("Zoidberg server")
.version(VERSION) .version(VERSION)
.author("Johannes Heuel") .author("Johannes Heuel")
.get_matches(); .get_matches();
let state = web::Data::new(State { let state = web::Data::new(State::new());
counter_workers: Mutex::new(0),
counter_jobs: Mutex::new(0),
workers: Mutex::new(Vec::new()),
new_jobs: Mutex::new(Vec::new()),
jobs: Mutex::new(Vec::new()),
});
HttpServer::new(move || { HttpServer::new(move || {
App::new() App::new()
.wrap(Logger::default())
.app_data(secret.clone())
.app_data(state.clone()) .app_data(state.clone())
.service(index) .service(index)
.service(register) .service(register)
@@ -151,13 +199,7 @@ mod tests {
async fn test_index() { async fn test_index() {
let app = test::init_service( let app = test::init_service(
App::new() App::new()
.app_data(web::Data::new(State { .app_data(web::Data::new(State::new()))
counter_workers: Mutex::new(0),
counter_jobs: Mutex::new(0),
workers: Mutex::new(Vec::new()),
new_jobs: Mutex::new(Vec::new()),
jobs: Mutex::new(Vec::new()),
}))
.service(index), .service(index),
) )
.await; .await;
@@ -170,17 +212,15 @@ mod tests {
async fn test_register() { async fn test_register() {
let app = test::init_service( let app = test::init_service(
App::new() App::new()
.app_data(web::Data::new(State { .app_data(String::from("secret"))
counter_workers: Mutex::new(0), .app_data(web::Data::new(State::new()))
counter_jobs: Mutex::new(0),
workers: Mutex::new(Vec::new()),
new_jobs: Mutex::new(Vec::new()),
jobs: Mutex::new(Vec::new()),
}))
.service(register), .service(register),
) )
.await; .await;
let req = test::TestRequest::get().uri("/register").to_request(); let req = test::TestRequest::get()
.append_header(("cookie", "secret"))
.uri("/register")
.to_request();
let resp: RegisterResponse = test::call_and_read_body_json(&app, req).await; let resp: RegisterResponse = test::call_and_read_body_json(&app, req).await;
assert_eq!(resp.id, 1); assert_eq!(resp.id, 1);
} }
@@ -190,6 +230,7 @@ mod tests {
let cmd = String::from("hi"); let cmd = String::from("hi");
let app = test::init_service( let app = test::init_service(
App::new() App::new()
.app_data(String::from("secret"))
.app_data(web::Data::new(State { .app_data(web::Data::new(State {
counter_workers: Mutex::new(0), counter_workers: Mutex::new(0),
counter_jobs: Mutex::new(0), counter_jobs: Mutex::new(0),
@@ -204,7 +245,10 @@ mod tests {
.service(fetch), .service(fetch),
) )
.await; .await;
let req = test::TestRequest::get().uri("/fetch").to_request(); let req = test::TestRequest::get()
.append_header(("cookie", "secret"))
.uri("/fetch")
.to_request();
let resp: FetchResponse = test::call_and_read_body_json(&app, req).await; let resp: FetchResponse = test::call_and_read_body_json(&app, req).await;
match resp { match resp {
FetchResponse::Nop => { FetchResponse::Nop => {
@@ -225,6 +269,7 @@ mod tests {
let cmd = String::from("hi"); let cmd = String::from("hi");
let app = test::init_service( let app = test::init_service(
App::new() App::new()
.app_data(String::from("secret"))
.app_data(web::Data::new(State { .app_data(web::Data::new(State {
counter_workers: Mutex::new(0), counter_workers: Mutex::new(0),
counter_jobs: Mutex::new(0), counter_jobs: Mutex::new(0),
@@ -240,6 +285,7 @@ mod tests {
) )
.await; .await;
let req = test::TestRequest::post() let req = test::TestRequest::post()
.append_header(("cookie", "secret"))
.set_json(vec![StatusRequest { id: 1 }]) .set_json(vec![StatusRequest { id: 1 }])
.uri("/status") .uri("/status")
.to_request(); .to_request();
@@ -251,17 +297,13 @@ mod tests {
async fn test_update() { async fn test_update() {
let app = test::init_service( let app = test::init_service(
App::new() App::new()
.app_data(web::Data::new(State { .app_data(String::from("secret"))
counter_workers: Mutex::new(0), .app_data(web::Data::new(State::new()))
counter_jobs: Mutex::new(0),
workers: Mutex::new(Vec::new()),
new_jobs: Mutex::new(Vec::new()),
jobs: Mutex::new(Vec::new()),
}))
.service(update), .service(update),
) )
.await; .await;
let req = test::TestRequest::post() let req = test::TestRequest::post()
.append_header(("cookie", "secret"))
.set_json(vec![Update { .set_json(vec![Update {
worker: 0, worker: 0,
job: 0, job: 0,
@@ -277,17 +319,13 @@ mod tests {
async fn test_submit() { async fn test_submit() {
let app = test::init_service( let app = test::init_service(
App::new() App::new()
.app_data(web::Data::new(State { .app_data(String::from("secret"))
counter_workers: Mutex::new(0), .app_data(web::Data::new(State::new()))
counter_jobs: Mutex::new(0),
workers: Mutex::new(Vec::new()),
new_jobs: Mutex::new(Vec::new()),
jobs: Mutex::new(Vec::new()),
}))
.service(submit), .service(submit),
) )
.await; .await;
let req = test::TestRequest::post() let req = test::TestRequest::post()
.append_header(("cookie", "secret"))
.set_json(vec![Job { .set_json(vec![Job {
id: 0, id: 0,
cmd: String::from("hi"), cmd: String::from("hi"),