From e5a78bed068fb22f39855b6392c91c1f1e279437 Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Tue, 23 Aug 2022 23:44:13 +0100 Subject: [PATCH] Allow root authentication over WebSocket connection --- src/iam/signin.rs | 85 +++++++++++++++++++++++++++++++++++++++-------- src/iam/signup.rs | 22 ++++++++---- src/net/rpc.rs | 18 ++++++---- src/net/signin.rs | 7 ++-- src/net/signup.rs | 7 ++-- 5 files changed, 110 insertions(+), 29 deletions(-) diff --git a/src/iam/signin.rs b/src/iam/signin.rs index c8b4295e..b8ed0c9c 100644 --- a/src/iam/signin.rs +++ b/src/iam/signin.rs @@ -7,10 +7,12 @@ use argon2::password_hash::{PasswordHash, PasswordVerifier}; use argon2::Argon2; use chrono::{Duration, Utc}; use jsonwebtoken::{encode, EncodingKey}; +use std::sync::Arc; use surrealdb::sql::Object; +use surrealdb::Auth; use surrealdb::Session; -pub async fn signin(vars: Object) -> Result { +pub async fn signin(session: &mut Session, vars: Object) -> Result { // Parse the specified variables let ns = vars.get("NS").or_else(|| vars.get("ns")); let db = vars.get("DB").or_else(|| vars.get("db")); @@ -23,7 +25,7 @@ pub async fn signin(vars: Object) -> Result { let db = db.to_strand().as_string(); let sc = sc.to_strand().as_string(); // Attempt to signin to specified scope - let res = super::signin::sc(ns, db, sc, vars).await?; + let res = super::signin::sc(session, ns, db, sc, vars).await?; // Return the result to the client Ok(res) } @@ -41,7 +43,7 @@ pub async fn signin(vars: Object) -> Result { let user = user.to_strand().as_string(); let pass = pass.to_strand().as_string(); // Attempt to signin to database - let res = super::signin::db(ns, db, user, pass).await?; + let res = super::signin::db(session, ns, db, user, pass).await?; // Return the result to the client Ok(res) } @@ -62,7 +64,27 @@ pub async fn signin(vars: Object) -> Result { let user = user.to_strand().as_string(); let pass = pass.to_strand().as_string(); // Attempt to signin to namespace - let res = super::signin::ns(ns, user, pass).await?; + let res = super::signin::ns(session, ns, user, pass).await?; + // Return the result to the client + Ok(res) + } + // There is no username or password + _ => Err(Error::InvalidAuth), + } + } + (None, None, None) => { + // Get the provided user and pass + let user = vars.get("user"); + let pass = vars.get("pass"); + // Validate the user and pass + match (user, pass) { + // There is a username and password + (Some(user), Some(pass)) => { + // Process the provided values + let user = user.to_strand().as_string(); + let pass = pass.to_strand().as_string(); + // Attempt to signin to namespace + let res = super::signin::su(session, user, pass).await?; // Return the result to the client Ok(res) } @@ -74,7 +96,13 @@ pub async fn signin(vars: Object) -> Result { } } -pub async fn sc(ns: String, db: String, sc: String, vars: Object) -> Result { +pub async fn sc( + session: &mut Session, + ns: String, + db: String, + sc: String, + vars: Object, +) -> Result { // Get a database reference let kvs = DB.get().unwrap(); // Get local copy of options @@ -109,12 +137,14 @@ pub async fn sc(ns: String, db: String, sc: String, vars: Object) -> Result Utc::now() + Duration::hours(1), } .timestamp(), - ns: Some(ns), - db: Some(db), - sc: Some(sc), + ns: Some(ns.to_owned()), + db: Some(db.to_owned()), + sc: Some(sc.to_owned()), id: Some(rid.to_raw()), ..Claims::default() }; + // Set the authentication on the sesssion + session.au = Arc::new(Auth::Sc(ns, db, sc)); // Create the authentication token match encode(&*HEADER, &val, &key) { // The auth token was created successfully @@ -139,7 +169,13 @@ pub async fn sc(ns: String, db: String, sc: String, vars: Object) -> Result Result { +pub async fn db( + session: &mut Session, + ns: String, + db: String, + user: String, + pass: String, +) -> Result { // Get a database reference let kvs = DB.get().unwrap(); // Create a new readonly transaction @@ -160,11 +196,13 @@ pub async fn db(ns: String, db: String, user: String, pass: String) -> Result Result Result { +pub async fn ns( + session: &mut Session, + ns: String, + user: String, + pass: String, +) -> Result { // Get a database reference let kvs = DB.get().unwrap(); // Create a new readonly transaction @@ -203,10 +246,12 @@ pub async fn ns(ns: String, user: String, pass: String) -> Result iat: Utc::now().timestamp(), nbf: Utc::now().timestamp(), exp: (Utc::now() + Duration::hours(1)).timestamp(), - ns: Some(ns), + ns: Some(ns.to_owned()), id: Some(user), ..Claims::default() }; + // Set the authentication on the sesssion + session.au = Arc::new(Auth::Ns(ns)); // Create the authentication token match encode(&*HEADER, &val, &key) { // The auth token was created successfully @@ -223,3 +268,17 @@ pub async fn ns(ns: String, user: String, pass: String) -> Result _ => Err(Error::InvalidAuth), } } + +pub async fn su(session: &mut Session, user: String, pass: String) -> Result { + // Get the config options + let opts = CF.get().unwrap(); + // Attempt to verify the root user + if let Some(root) = &opts.pass { + if user == opts.user && &pass == root { + session.au = Arc::new(Auth::Kv); + return Ok(String::from("")); + } + } + // The specified user login does not exist + Err(Error::InvalidAuth) +} diff --git a/src/iam/signup.rs b/src/iam/signup.rs index 722e3363..c32ddf8d 100644 --- a/src/iam/signup.rs +++ b/src/iam/signup.rs @@ -5,10 +5,12 @@ use crate::err::Error; use crate::iam::token::{Claims, HEADER}; use chrono::{Duration, Utc}; use jsonwebtoken::{encode, EncodingKey}; +use std::sync::Arc; use surrealdb::sql::Object; +use surrealdb::Auth; use surrealdb::Session; -pub async fn signup(vars: Object) -> Result { +pub async fn signup(session: &mut Session, vars: Object) -> Result { // Parse the specified variables let ns = vars.get("NS").or_else(|| vars.get("ns")); let db = vars.get("DB").or_else(|| vars.get("db")); @@ -21,7 +23,7 @@ pub async fn signup(vars: Object) -> Result { let db = db.to_strand().as_string(); let sc = sc.to_strand().as_string(); // Attempt to signin to specified scope - let res = super::signup::sc(ns, db, sc, vars).await?; + let res = super::signup::sc(session, ns, db, sc, vars).await?; // Return the result to the client Ok(res) } @@ -29,7 +31,13 @@ pub async fn signup(vars: Object) -> Result { } } -pub async fn sc(ns: String, db: String, sc: String, vars: Object) -> Result { +pub async fn sc( + session: &mut Session, + ns: String, + db: String, + sc: String, + vars: Object, +) -> Result { // Get a database reference let kvs = DB.get().unwrap(); // Get local copy of options @@ -64,12 +72,14 @@ pub async fn sc(ns: String, db: String, sc: String, vars: Object) -> Result Utc::now() + Duration::hours(1), } .timestamp(), - ns: Some(ns), - db: Some(db), - sc: Some(sc), + ns: Some(ns.to_owned()), + db: Some(db.to_owned()), + sc: Some(sc.to_owned()), id: Some(rid.to_raw()), ..Claims::default() }; + // Set the authentication on the sesssion + session.au = Arc::new(Auth::Sc(ns, db, sc)); // Create the authentication token match encode(&*HEADER, &val, &key) { // The auth token was created successfully diff --git a/src/net/rpc.rs b/src/net/rpc.rs index a994eb7e..b8ec7866 100644 --- a/src/net/rpc.rs +++ b/src/net/rpc.rs @@ -116,11 +116,11 @@ impl Rpc { _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, }, "signup" => match params.take_one() { - Value::Object(v) => rpc.read().await.signup(v).await, + Value::Object(v) => rpc.write().await.signup(v).await, _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, }, "signin" => match params.take_one() { - Value::Object(v) => rpc.read().await.signin(v).await, + Value::Object(v) => rpc.write().await.signin(v).await, _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, }, "invalidate" => match params.len() { @@ -208,12 +208,18 @@ impl Rpc { Ok(Value::None) } - async fn signup(&self, vars: Object) -> Result { - crate::iam::signup::signup(vars).await.map(Into::into).map_err(Into::into) + async fn signup(&mut self, vars: Object) -> Result { + crate::iam::signup::signup(&mut self.session, vars) + .await + .map(Into::into) + .map_err(Into::into) } - async fn signin(&self, vars: Object) -> Result { - crate::iam::signin::signin(vars).await.map(Into::into).map_err(Into::into) + async fn signin(&mut self, vars: Object) -> Result { + crate::iam::signin::signin(&mut self.session, vars) + .await + .map(Into::into) + .map_err(Into::into) } async fn invalidate(&mut self) -> Result { diff --git a/src/net/signin.rs b/src/net/signin.rs index 625ea6f9..5f27ff06 100644 --- a/src/net/signin.rs +++ b/src/net/signin.rs @@ -1,7 +1,9 @@ use crate::err::Error; +use crate::net::session; use bytes::Bytes; use std::str; use surrealdb::sql::Value; +use surrealdb::Session; use warp::http::Response; use warp::Filter; @@ -15,6 +17,7 @@ pub fn config() -> impl Filter impl Filter Result { +async fn handler(mut session: Session, body: Bytes) -> Result { // Convert the HTTP body into text let data = str::from_utf8(&body).unwrap(); // Parse the provided data as JSON match surrealdb::sql::json(data) { // The provided value was an object - Ok(Value::Object(vars)) => match crate::iam::signin::signin(vars).await { + Ok(Value::Object(vars)) => match crate::iam::signin::signin(&mut session, vars).await { // Authentication was successful Ok(v) => Ok(Response::builder().body(v)), // There was an error with authentication diff --git a/src/net/signup.rs b/src/net/signup.rs index 8689c056..9a1b4441 100644 --- a/src/net/signup.rs +++ b/src/net/signup.rs @@ -1,7 +1,9 @@ use crate::err::Error; +use crate::net::session; use bytes::Bytes; use std::str; use surrealdb::sql::Value; +use surrealdb::Session; use warp::http::Response; use warp::Filter; @@ -15,6 +17,7 @@ pub fn config() -> impl Filter impl Filter Result { +async fn handler(mut session: Session, body: Bytes) -> Result { // Convert the HTTP body into text let data = str::from_utf8(&body).unwrap(); // Parse the provided data as JSON match surrealdb::sql::json(data) { // The provided value was an object - Ok(Value::Object(vars)) => match crate::iam::signup::signup(vars).await { + Ok(Value::Object(vars)) => match crate::iam::signup::signup(&mut session, vars).await { // Authentication was successful Ok(v) => Ok(Response::builder().body(v)), // There was an error with authentication