From 31bbed2019952b2a009b0e2ae0afa58b1fa329ef Mon Sep 17 00:00:00 2001 From: Bevan Hunt Date: Thu, 25 Mar 2021 13:19:27 -0700 Subject: [PATCH] add http basic auth --- CHANGELOG.md | 5 +++++ Cargo.lock | 3 ++- Cargo.toml | 3 ++- README.md | 7 ++++--- src/main.rs | 54 ++++++++++++++++++++++++++++++++++++++++------------ 5 files changed, 55 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60ab083..7517bd3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [8.1.x] - 2021-04-25 + +### Added +- Adds http basic auth + ## [8.0.x] - 2021-03-23 ### Added diff --git a/Cargo.lock b/Cargo.lock index 03ba699..8ad7a56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -461,10 +461,11 @@ dependencies = [ [[package]] name = "broker" -version = "8.0.0" +version = "8.1.0" dependencies = [ "anyhow", "async-std", + "base64 0.13.0", "driftwood", "futures", "go-flag", diff --git a/Cargo.toml b/Cargo.toml index 3947e54..c9b6e88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "broker" -version = "8.0.0" +version = "8.1.0" authors = ["Bevan Hunt "] edition = "2018" license = "MIT" @@ -31,3 +31,4 @@ http-types = "2" tide-rustls = "0.3" futures = "0.3" tide-acme = "0.1.0" +base64 = "0.13" diff --git a/README.md b/README.md index 51d5978..54fdc38 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,8 @@ Broker follows an insert-only/publish/subscribe paradigm rather than a REST CRUD * Add users with admin token permission * Multi-tenant * Supports SSL - full end-to-end encryption -* Provides user authentication with JWTs with stored Argon2 passwords +* Provides user authentication with JWTs or HTTP Basic +* Secure passwords with Argon2 encoding * Uses Global NTP servers and doesn't rely on your local server time * Insert event via JSON POST request * Sync latest events on SSE client connection @@ -90,7 +91,7 @@ will return ```html GET /sse ``` -- authenticated endpoint (Authorization: Bearer {jwt}) +- authenticated endpoint (Authorization: Bearer {jwt}) or (Authorization: Basic {username:password}) - connect your sse-client to this endpoint using [broker-client](https://www.npmjs.com/package/broker-client) - `note`: broker-client uses fetch as eventsource doesn't support headers @@ -99,7 +100,7 @@ GET /sse ```html POST /insert ``` -- authenticated endpoint (Authorization: Bearer {jwt}) +- authenticated endpoint (Authorization: Bearer {jwt}) or (Authorization: Basic {username:password}) - POST JSON to insert an event ```json {"event":{...}, "data":{...}, "tenant_name":{...}} diff --git a/src/main.rs b/src/main.rs index f4eefa7..bfc6dac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -219,7 +219,7 @@ fn env_var_config() -> EnvVarConfig { EnvVarConfig{port, origin, jwt_expiry, jwt_secret, secure, domain, certs, db, admin_token} } -fn jwt_verify(token: String) -> Result>> { +async fn jwt_verify(token: String) -> Result>> { let configure = env_var_config(); @@ -228,16 +228,45 @@ fn jwt_verify(token: String) -> Result>> { if auth_type == "Bearer" { let token = parts.next().unwrap(); let _ = match decode::(&token, &DecodingKey::from_secret(configure.jwt_secret.as_ref()), &Validation::default()) { - Ok(c) => { - return Ok(Some(c)); - }, - Err(_) => { - return Ok(None); - } + Ok(c) => { return Ok(Some(c)); }, + Err(_) => { return Ok(None); } + }; + } else if auth_type == "Basic" { + let basic_encoded = parts.next().unwrap(); + + let _ = match base64::decode(basic_encoded) { + Ok(c) => { + let _ = match std::str::from_utf8(&c) { + Ok(basic) => { + let mut basic_parts = basic.split(":"); + let user_name = basic_parts.next().unwrap(); + let password = basic_parts.next().unwrap(); + let user_value = get_user_by_username(user_name.to_string())?; + match user_value { + Some(user) => { + if argon2::verify_encoded(&user.password, password.as_ref())? { + let app = env_var_config(); + let iat = nippy::get_unix_ntp_time().await?; + let exp = iat + app.jwt_expiry; + let iss = "Dispatcher".to_string(); + let my_claims = Claims{sub: user.clone().username, exp, iat, iss}; + let my_token = TokenData{ + header: Header::default(), + claims: my_claims, + }; + return Ok(Some(my_token)); + } + }, + None => { return Ok(None); } + } + }, + Err(_) => { return Ok(None); } + }; + }, + Err(_) => { return Ok(None); } }; - } else { - Ok(None) } + Ok(None) } // insert an event @@ -301,7 +330,7 @@ async fn insert_event(mut req: Request<()>) -> tide::Result { match token_value { Some(token_header) => { let token = token_header.last().to_string(); - let jwt_value = jwt_verify(token)?; + let jwt_value = jwt_verify(token).await?; match jwt_value { Some(jwt) => { let r = req.body_string().await?; @@ -340,13 +369,14 @@ async fn main() -> tide::Result<()> { match token_value { Some(token_header) => { let token = token_header.last().to_string(); - let jwt_value = jwt_verify(token)?; + let jwt_value = jwt_verify(token).await?; match jwt_value { Some(jwt) => { let user_value = get_user_by_username(jwt.claims.sub)?; match user_value { Some(user) => { + let mut cache: HashMap = HashMap::new(); let mut interval = stream::interval(Duration::from_millis(100)); @@ -369,7 +399,7 @@ async fn main() -> tide::Result<()> { cache.insert(evt.event.clone(), evt.clone()); } }, - None => { return Ok(()); } + None => { println!("helo"); return Ok(()); } } } }