diff --git a/src/net/config.rs b/src/cli/config.rs similarity index 82% rename from src/net/config.rs rename to src/cli/config.rs index df6d94a5..358c095c 100644 --- a/src/net/config.rs +++ b/src/cli/config.rs @@ -1,5 +1,8 @@ +use once_cell::sync::OnceCell; use std::net::SocketAddr; +pub static CF: OnceCell = OnceCell::new(); + #[derive(Clone, Debug)] pub struct Config { pub bind: SocketAddr, @@ -10,7 +13,7 @@ pub struct Config { pub key: Option, } -pub fn parse(matches: &clap::ArgMatches) -> Config { +pub fn init(matches: &clap::ArgMatches) { // Parse the server binding address let bind = matches .value_of("bind") @@ -26,13 +29,13 @@ pub fn parse(matches: &clap::ArgMatches) -> Config { // Parse any TLS server security options let crt = matches.value_of("web-crt").map(|v| v.to_owned()); let key = matches.value_of("web-key").map(|v| v.to_owned()); - // Return a new config object - Config { + // Store the new config object + let _ = CF.set(Config { bind, path, user, pass, crt, key, - } + }); } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index fc7df59e..0c94aded 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,10 +1,13 @@ mod backup; +mod config; mod export; mod import; mod log; mod start; mod version; +pub use config::CF; + use crate::cnf::LOGO; use clap::{Arg, Command}; use once_cell::sync::Lazy; diff --git a/src/cli/start.rs b/src/cli/start.rs index 92b31998..8328b7ef 100644 --- a/src/cli/start.rs +++ b/src/cli/start.rs @@ -1,12 +1,19 @@ +use super::config; use crate::cnf::LOGO; +use crate::dbs; use crate::err::Error; use crate::net; -pub fn init(matches: &clap::ArgMatches) -> Result<(), Error> { +#[tokio::main] +pub async fn init(matches: &clap::ArgMatches) -> Result<(), Error> { // output SurrealDB logo println!("{}", LOGO); - // Start up the web server - net::init(matches)?; - // Don't error when done + // Setup the cli options + config::init(matches); + // Start the kvs server + dbs::init().await?; + // Start the web server + net::init().await?; + // All ok Ok(()) } diff --git a/src/cli/version.rs b/src/cli/version.rs index 3c365755..e7750fee 100644 --- a/src/cli/version.rs +++ b/src/cli/version.rs @@ -11,10 +11,7 @@ macro_rules! get_cfg { pub fn init(_: &clap::ArgMatches) -> Result<(), Error> { get_cfg!(target_os: "windows", "macos", "ios", "linux", "android", "freebsd", "openbsd", "netbsd"); - get_cfg!(target_arch: "x86", "x86_64", "mips", "powerpc", "powerpc64", "arm", "aarch64"); - println!("{} {} for {} on {}", NAME, VERSION, target_os(), target_arch()); - Ok(()) } diff --git a/src/dbs/mod.rs b/src/dbs/mod.rs new file mode 100644 index 00000000..68bee962 --- /dev/null +++ b/src/dbs/mod.rs @@ -0,0 +1,17 @@ +use crate::cli::CF; +use crate::err::Error; +use once_cell::sync::OnceCell; +use surrealdb::Datastore; + +pub static DB: OnceCell = OnceCell::new(); + +pub async fn init() -> Result<(), Error> { + // Get local copy of options + let opt = CF.get().unwrap(); + // Parse and setup the desired kv datastore + let dbs = Datastore::new(&opt.path).await?; + // Store database instance + let _ = DB.set(dbs); + // All ok + Ok(()) +} diff --git a/src/iam/clear.rs b/src/iam/clear.rs new file mode 100644 index 00000000..70ebf305 --- /dev/null +++ b/src/iam/clear.rs @@ -0,0 +1,9 @@ +use crate::err::Error; +use std::sync::Arc; +use surrealdb::Auth; +use surrealdb::Session; + +pub async fn clear(session: &mut Session) -> Result<(), Error> { + session.au = Arc::new(Auth::No); + Ok(()) +} diff --git a/src/iam/mod.rs b/src/iam/mod.rs new file mode 100644 index 00000000..1a73ad62 --- /dev/null +++ b/src/iam/mod.rs @@ -0,0 +1,5 @@ +pub mod clear; +pub mod signin; +pub mod signup; +pub mod token; +pub mod verify; diff --git a/src/iam/signin.rs b/src/iam/signin.rs new file mode 100644 index 00000000..b161b662 --- /dev/null +++ b/src/iam/signin.rs @@ -0,0 +1,222 @@ +use crate::cnf::SERVER_NAME; +use crate::dbs::DB; +use crate::err::Error; +use crate::iam::token::{Claims, HEADER}; +use argon2::password_hash::{PasswordHash, PasswordVerifier}; +use argon2::Argon2; +use chrono::{Duration, Utc}; +use jsonwebtoken::{encode, EncodingKey}; +use surrealdb::sql::Object; +use surrealdb::Session; + +pub async fn signin(vars: Object) -> Result { + // Parse the speficied variables + let ns = vars.get("NS").or_else(|| vars.get("ns")); + let db = vars.get("DB").or_else(|| vars.get("db")); + let sc = vars.get("SC").or_else(|| vars.get("sc")); + // Check if the paramaters exist + match (ns, db, sc) { + (Some(ns), Some(db), Some(sc)) => { + // Process the provided values + let ns = ns.to_strand().as_string(); + 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?; + // Return the result to the client + Ok(res) + } + (Some(ns), Some(db), 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 ns = ns.to_strand().as_string(); + let db = db.to_strand().as_string(); + 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?; + // Return the result to the client + Ok(res) + } + // There is no username or password + _ => Err(Error::InvalidAuth), + } + } + (Some(ns), 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 ns = ns.to_strand().as_string(); + 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?; + // Return the result to the client + Ok(res) + } + // There is no username or password + _ => Err(Error::InvalidAuth), + } + } + _ => Err(Error::InvalidAuth), + } +} + +pub async fn sc(ns: String, db: String, sc: String, vars: Object) -> Result { + // Get a database reference + let kvs = DB.get().unwrap(); + // Create a new readonly transaction + let mut tx = kvs.transaction(false, false).await?; + // Check if the supplied NS Login exists + match tx.get_sc(&ns, &db, &sc).await { + Ok(sv) => { + match sv.signin { + // This scope allows signin + Some(val) => { + // Setup the query params + let vars = Some(vars.0); + // Setup the query session + let sess = Session::for_db(&ns, &db); + // Compute the value with the params + match kvs.compute(val, &sess, vars).await { + // The signin value succeeded + Ok(val) => match val.rid() { + // There is a record returned + Some(rid) => { + // Create the authentication key + let key = EncodingKey::from_secret(sv.code.as_ref()); + // Create the authentication claim + let val = Claims { + iss: SERVER_NAME.to_owned(), + iat: Utc::now().timestamp(), + nbf: Utc::now().timestamp(), + exp: match sv.session { + Some(v) => Utc::now() + Duration::from_std(v.0).unwrap(), + _ => Utc::now() + Duration::hours(1), + } + .timestamp(), + ns: Some(ns), + db: Some(db), + sc: Some(sc), + id: Some(rid.to_raw()), + ..Claims::default() + }; + // Create the authentication token + match encode(&*HEADER, &val, &key) { + // The auth token was created successfully + Ok(tk) => Ok(tk), + // There was an error creating the token + _ => Err(Error::InvalidAuth), + } + } + // No record was returned + _ => Err(Error::InvalidAuth), + }, + // The signin query failed + _ => Err(Error::InvalidAuth), + } + } + // This scope does not allow signin + _ => Err(Error::InvalidAuth), + } + } + // The scope does not exists + _ => Err(Error::InvalidAuth), + } +} + +pub async fn db(ns: String, db: String, user: String, pass: String) -> Result { + // Get a database reference + let kvs = DB.get().unwrap(); + // Create a new readonly transaction + let mut tx = kvs.transaction(false, false).await?; + // Check if the supplied DB Login exists + match tx.get_dl(&ns, &db, &user).await { + Ok(dl) => { + // Compute the hash and verify the password + let hash = PasswordHash::new(&dl.hash).unwrap(); + // Attempt to verify the password using Argon2 + match Argon2::default().verify_password(pass.as_ref(), &hash) { + Ok(_) => { + // Create the authentication key + let key = EncodingKey::from_secret(dl.code.as_ref()); + // Create the authentication claim + let val = Claims { + iss: SERVER_NAME.to_owned(), + iat: Utc::now().timestamp(), + nbf: Utc::now().timestamp(), + exp: (Utc::now() + Duration::hours(1)).timestamp(), + ns: Some(ns), + db: Some(db), + id: Some(user), + ..Claims::default() + }; + // Create the authentication token + match encode(&*HEADER, &val, &key) { + // The auth token was created successfully + Ok(tk) => Ok(tk), + // There was an error creating the token + _ => Err(Error::InvalidAuth), + } + } + // The password did not verify + _ => Err(Error::InvalidAuth), + } + } + // The specified user login does not exist + _ => Err(Error::InvalidAuth), + } +} + +pub async fn ns(ns: String, user: String, pass: String) -> Result { + // Get a database reference + let kvs = DB.get().unwrap(); + // Create a new readonly transaction + let mut tx = kvs.transaction(false, false).await?; + // Check if the supplied NS Login exists + match tx.get_nl(&ns, &user).await { + Ok(nl) => { + // Compute the hash and verify the password + let hash = PasswordHash::new(&nl.hash).unwrap(); + // Attempt to verify the password using Argon2 + match Argon2::default().verify_password(pass.as_ref(), &hash) { + Ok(_) => { + // Create the authentication key + let key = EncodingKey::from_secret(nl.code.as_ref()); + // Create the authentication claim + let val = Claims { + iss: SERVER_NAME.to_owned(), + iat: Utc::now().timestamp(), + nbf: Utc::now().timestamp(), + exp: (Utc::now() + Duration::hours(1)).timestamp(), + ns: Some(ns), + id: Some(user), + ..Claims::default() + }; + // Create the authentication token + match encode(&*HEADER, &val, &key) { + // The auth token was created successfully + Ok(tk) => Ok(tk), + // There was an error creating the token + _ => Err(Error::InvalidAuth), + } + } + // The password did not verify + _ => Err(Error::InvalidAuth), + } + } + // The specified user login does not exist + _ => Err(Error::InvalidAuth), + } +} diff --git a/src/iam/signup.rs b/src/iam/signup.rs new file mode 100644 index 00000000..7f19d2e7 --- /dev/null +++ b/src/iam/signup.rs @@ -0,0 +1,92 @@ +use crate::cnf::SERVER_NAME; +use crate::dbs::DB; +use crate::err::Error; +use crate::iam::token::{Claims, HEADER}; +use chrono::{Duration, Utc}; +use jsonwebtoken::{encode, EncodingKey}; +use surrealdb::sql::Object; +use surrealdb::Session; + +pub async fn signup(vars: Object) -> Result { + // Parse the speficied variables + let ns = vars.get("NS").or_else(|| vars.get("ns")); + let db = vars.get("DB").or_else(|| vars.get("db")); + let sc = vars.get("SC").or_else(|| vars.get("sc")); + // Check if the paramaters exist + match (ns, db, sc) { + (Some(ns), Some(db), Some(sc)) => { + // Process the provided values + let ns = ns.to_strand().as_string(); + 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?; + // Return the result to the client + Ok(res) + } + _ => Err(Error::InvalidAuth), + } +} + +pub async fn sc(ns: String, db: String, sc: String, vars: Object) -> Result { + // Get a database reference + let kvs = DB.get().unwrap(); + // Create a new readonly transaction + let mut tx = kvs.transaction(false, false).await?; + // Check if the supplied NS Login exists + match tx.get_sc(&ns, &db, &sc).await { + Ok(sv) => { + match sv.signup { + // This scope allows signin + Some(val) => { + // Setup the query params + let vars = Some(vars.0); + // Setup the query session + let sess = Session::for_db(&ns, &db); + // Compute the value with the params + match kvs.compute(val, &sess, vars).await { + // The signin value succeeded + Ok(val) => match val.rid() { + // There is a record returned + Some(rid) => { + // Create the authentication key + let key = EncodingKey::from_secret(sv.code.as_ref()); + // Create the authentication claim + let val = Claims { + iss: SERVER_NAME.to_owned(), + iat: Utc::now().timestamp(), + nbf: Utc::now().timestamp(), + exp: match sv.session { + Some(v) => Utc::now() + Duration::from_std(v.0).unwrap(), + _ => Utc::now() + Duration::hours(1), + } + .timestamp(), + ns: Some(ns), + db: Some(db), + sc: Some(sc), + id: Some(rid.to_raw()), + ..Claims::default() + }; + // Create the authentication token + match encode(&*HEADER, &val, &key) { + // The auth token was created successfully + Ok(tk) => Ok(tk), + // There was an error creating the token + _ => Err(Error::InvalidAuth), + } + } + // No record was returned + _ => Err(Error::InvalidAuth), + }, + // The signin query failed + _ => Err(Error::InvalidAuth), + } + } + // This scope does not allow signin + _ => Err(Error::InvalidAuth), + } + } + // The scope does not exists + _ => Err(Error::InvalidAuth), + } +} diff --git a/src/net/jwt.rs b/src/iam/token.rs similarity index 100% rename from src/net/jwt.rs rename to src/iam/token.rs diff --git a/src/iam/verify.rs b/src/iam/verify.rs new file mode 100644 index 00000000..2821bfe4 --- /dev/null +++ b/src/iam/verify.rs @@ -0,0 +1,278 @@ +use crate::cli::CF; +use crate::dbs::DB; +use crate::err::Error; +use crate::iam::token::Claims; +use argon2::password_hash::{PasswordHash, PasswordVerifier}; +use argon2::Argon2; +use jsonwebtoken::{decode, DecodingKey, Validation}; +use once_cell::sync::Lazy; +use std::sync::Arc; +use surrealdb::sql::Algorithm; +use surrealdb::sql::Value; +use surrealdb::Auth; +use surrealdb::Session; + +fn config(algo: Algorithm, code: String) -> Result<(DecodingKey, Validation), Error> { + match algo { + Algorithm::Hs256 => Ok(( + DecodingKey::from_secret(code.as_ref()), + Validation::new(jsonwebtoken::Algorithm::HS256), + )), + Algorithm::Hs384 => Ok(( + DecodingKey::from_secret(code.as_ref()), + Validation::new(jsonwebtoken::Algorithm::HS384), + )), + Algorithm::Hs512 => Ok(( + DecodingKey::from_secret(code.as_ref()), + Validation::new(jsonwebtoken::Algorithm::HS512), + )), + Algorithm::EdDSA => Ok(( + DecodingKey::from_ed_pem(code.as_ref())?, + Validation::new(jsonwebtoken::Algorithm::EdDSA), + )), + Algorithm::Es256 => Ok(( + DecodingKey::from_ec_pem(code.as_ref())?, + Validation::new(jsonwebtoken::Algorithm::ES256), + )), + Algorithm::Es384 => Ok(( + DecodingKey::from_ec_pem(code.as_ref())?, + Validation::new(jsonwebtoken::Algorithm::ES384), + )), + Algorithm::Es512 => Ok(( + DecodingKey::from_ec_pem(code.as_ref())?, + Validation::new(jsonwebtoken::Algorithm::ES384), + )), + Algorithm::Ps256 => Ok(( + DecodingKey::from_rsa_pem(code.as_ref())?, + Validation::new(jsonwebtoken::Algorithm::PS256), + )), + Algorithm::Ps384 => Ok(( + DecodingKey::from_rsa_pem(code.as_ref())?, + Validation::new(jsonwebtoken::Algorithm::PS384), + )), + Algorithm::Ps512 => Ok(( + DecodingKey::from_rsa_pem(code.as_ref())?, + Validation::new(jsonwebtoken::Algorithm::PS512), + )), + Algorithm::Rs256 => Ok(( + DecodingKey::from_rsa_pem(code.as_ref())?, + Validation::new(jsonwebtoken::Algorithm::RS256), + )), + Algorithm::Rs384 => Ok(( + DecodingKey::from_rsa_pem(code.as_ref())?, + Validation::new(jsonwebtoken::Algorithm::RS384), + )), + Algorithm::Rs512 => Ok(( + DecodingKey::from_rsa_pem(code.as_ref())?, + Validation::new(jsonwebtoken::Algorithm::RS512), + )), + } +} + +static KEY: Lazy = Lazy::new(|| DecodingKey::from_secret(&[])); + +static DUD: Lazy = Lazy::new(|| { + let mut validation = Validation::new(jsonwebtoken::Algorithm::HS256); + validation.insecure_disable_signature_validation(); + validation.validate_nbf = true; + validation.validate_exp = true; + validation +}); + +pub async fn basic(session: &mut Session, auth: String) -> Result<(), Error> { + // Retrieve just the auth data + if let Some((_, auth)) = auth.split_once(' ') { + // Get a database reference + let kvs = DB.get().unwrap(); + // Get the config options + let opts = CF.get().unwrap(); + // Decode the encoded auth data + let auth = base64::decode(auth)?; + // Convert the auth data to String + let auth = String::from_utf8(auth)?; + // Split the auth data into user and pass + if let Some((user, pass)) = auth.split_once(':') { + // Check that the details are not empty + if user.is_empty() || pass.is_empty() { + return Err(Error::InvalidAuth); + } + // Check if this is root authentication + if user == opts.user && pass == opts.pass { + session.au = Arc::new(Auth::Kv); + return Ok(()); + } + // Check if this is NS authentication + if let Some(ns) = &session.ns { + // Create a new readonly transaction + let mut tx = kvs.transaction(false, false).await?; + // Check if the supplied NS Login exists + if let Ok(nl) = tx.get_nl(ns, user).await { + // Compute the hash and verify the password + let hash = PasswordHash::new(&nl.hash).unwrap(); + if Argon2::default().verify_password(pass.as_ref(), &hash).is_ok() { + session.au = Arc::new(Auth::Ns(ns.to_owned())); + return Ok(()); + } + }; + // Check if this is DB authentication + if let Some(db) = &session.db { + // Check if the supplied DB Login exists + if let Ok(dl) = tx.get_dl(ns, db, user).await { + // Compute the hash and verify the password + let hash = PasswordHash::new(&dl.hash).unwrap(); + if Argon2::default().verify_password(pass.as_ref(), &hash).is_ok() { + session.au = Arc::new(Auth::Db(ns.to_owned(), db.to_owned())); + return Ok(()); + } + }; + } + } + } + } + // There was an auth error + Err(Error::InvalidAuth) +} + +pub async fn token(session: &mut Session, auth: String) -> Result<(), Error> { + // Retrieve just the auth data + if let Some((_, auth)) = auth.split_once(' ') { + // Get a database reference + let kvs = DB.get().unwrap(); + // Decode the token without verifying + let token = decode::(auth, &KEY, &DUD)?; + // Check the token authentication claims + match token.claims { + // Check if this is scope token authentication + Claims { + ns: Some(ns), + db: Some(db), + sc: Some(sc), + tk: Some(tk), + id: Some(id), + .. + } => { + // Create a new readonly transaction + let mut tx = kvs.transaction(false, false).await?; + // Parse the record id + let id = surrealdb::sql::thing(&id)?; + // Get the scope token + let de = tx.get_st(&ns, &db, &sc, &tk).await?; + let cf = config(de.kind, de.code)?; + // Verify the token + decode::(auth, &cf.0, &cf.1)?; + // Set the session + session.ns = Some(ns.to_owned()); + session.db = Some(db.to_owned()); + session.sc = Some(sc.to_owned()); + session.sd = Some(Value::from(id)); + session.au = Arc::new(Auth::Sc(ns, db, sc)); + return Ok(()); + } + // Check if this is scope authentication + Claims { + ns: Some(ns), + db: Some(db), + sc: Some(sc), + id: Some(id), + .. + } => { + // Create a new readonly transaction + let mut tx = kvs.transaction(false, false).await?; + // Parse the record id + let id = surrealdb::sql::thing(&id)?; + // Get the scope + let de = tx.get_sc(&ns, &db, &sc).await?; + let cf = config(Algorithm::Hs512, de.code)?; + // Verify the token + decode::(auth, &cf.0, &cf.1)?; + // Set the session + session.ns = Some(ns.to_owned()); + session.db = Some(db.to_owned()); + session.sc = Some(sc.to_owned()); + session.sd = Some(Value::from(id)); + session.au = Arc::new(Auth::Sc(ns, db, sc)); + return Ok(()); + } + // Check if this is database token authentication + Claims { + ns: Some(ns), + db: Some(db), + tk: Some(tk), + .. + } => { + // Create a new readonly transaction + let mut tx = kvs.transaction(false, false).await?; + // Get the database token + let de = tx.get_dt(&ns, &db, &tk).await?; + let cf = config(de.kind, de.code)?; + // Verify the token + decode::(auth, &cf.0, &cf.1)?; + // Set the session + session.ns = Some(ns.to_owned()); + session.db = Some(db.to_owned()); + session.au = Arc::new(Auth::Db(ns, db)); + return Ok(()); + } + // Check if this is database authentication + Claims { + ns: Some(ns), + db: Some(db), + id: Some(id), + .. + } => { + // Create a new readonly transaction + let mut tx = kvs.transaction(false, false).await?; + // Get the database login + let de = tx.get_dl(&ns, &db, &id).await?; + let cf = config(Algorithm::Hs512, de.code)?; + // Verify the token + decode::(auth, &cf.0, &cf.1)?; + // Set the session + session.ns = Some(ns.to_owned()); + session.db = Some(db.to_owned()); + session.au = Arc::new(Auth::Db(ns, db)); + return Ok(()); + } + // Check if this is namespace token authentication + Claims { + ns: Some(ns), + tk: Some(tk), + .. + } => { + // Create a new readonly transaction + let mut tx = kvs.transaction(false, false).await?; + // Get the namespace token + let de = tx.get_nt(&ns, &tk).await?; + let cf = config(de.kind, de.code)?; + // Verify the token + decode::(auth, &cf.0, &cf.1)?; + // Set the session + session.ns = Some(ns.to_owned()); + session.au = Arc::new(Auth::Ns(ns)); + return Ok(()); + } + // Check if this is namespace authentication + Claims { + ns: Some(ns), + id: Some(id), + .. + } => { + // Create a new readonly transaction + let mut tx = kvs.transaction(false, false).await?; + // Get the namespace login + let de = tx.get_nl(&ns, &id).await?; + let cf = config(Algorithm::Hs512, de.code)?; + // Verify the token + decode::(auth, &cf.0, &cf.1)?; + // Set the session + session.ns = Some(ns.to_owned()); + session.au = Arc::new(Auth::Ns(ns)); + return Ok(()); + } + // There was an auth error + _ => return Err(Error::InvalidAuth), + }; + } + // There was an auth error + Err(Error::InvalidAuth) +} diff --git a/src/main.rs b/src/main.rs index 9aff77f5..0af6004a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,9 @@ mod mac; mod cli; mod cnf; +mod dbs; mod err; +mod iam; mod net; fn main() { diff --git a/src/net/export.rs b/src/net/export.rs index 5995a3bf..cf0ee26b 100644 --- a/src/net/export.rs +++ b/src/net/export.rs @@ -1,6 +1,6 @@ +use crate::dbs::DB; use crate::err::Error; use crate::net::session; -use crate::net::DB; use bytes::Bytes; use hyper::body::Body; use surrealdb::Session; diff --git a/src/net/import.rs b/src/net/import.rs index 6bfe122b..6d5602de 100644 --- a/src/net/import.rs +++ b/src/net/import.rs @@ -1,7 +1,7 @@ +use crate::dbs::DB; use crate::err::Error; use crate::net::output; use crate::net::session; -use crate::net::DB; use bytes::Bytes; use surrealdb::Session; use warp::http; diff --git a/src/net/key.rs b/src/net/key.rs index 6a3e6bc9..7f10a6dc 100644 --- a/src/net/key.rs +++ b/src/net/key.rs @@ -1,8 +1,8 @@ +use crate::dbs::DB; use crate::err::Error; use crate::net::head; use crate::net::output; use crate::net::session; -use crate::net::DB; use bytes::Bytes; use serde::Deserialize; use std::str; diff --git a/src/net/mod.rs b/src/net/mod.rs index 4a64d4a6..67524b2e 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -1,13 +1,12 @@ -mod config; mod export; mod fail; mod head; mod import; mod index; -mod jwt; mod key; mod log; mod output; +mod rpc; mod session; mod signin; mod signup; @@ -16,26 +15,11 @@ mod status; mod sync; mod version; +use crate::cli::CF; use crate::err::Error; -use config::Config; -use once_cell::sync::OnceCell; -use surrealdb::Datastore; use warp::Filter; -static DB: OnceCell = OnceCell::new(); - -static CF: OnceCell = OnceCell::new(); - -#[tokio::main] -pub async fn init(matches: &clap::ArgMatches) -> Result<(), Error> { - // Parse the server config options - let cfg = config::parse(matches); - // Parse and setup the desired kv datastore - let dbs = Datastore::new(&cfg.path).await?; - // Store database instance - let _ = DB.set(dbs); - // Store config options - let _ = CF.set(cfg); +pub async fn init() -> Result<(), Error> { // Setup web routes let net = index::config() // Version endpoint @@ -52,6 +36,8 @@ pub async fn init(matches: &clap::ArgMatches) -> Result<(), Error> { .or(import::config()) // Backup endpoint .or(sync::config()) + // RPC query endpoint + .or(rpc::config()) // SQL query endpoint .or(sql::config()) // API query endpoint diff --git a/src/net/session.rs b/src/net/session.rs index 24c2eb6c..67719ce6 100644 --- a/src/net/session.rs +++ b/src/net/session.rs @@ -1,89 +1,12 @@ use crate::err::Error; -use crate::net::jwt::Claims; -use crate::net::CF; -use crate::net::DB; -use argon2::password_hash::{PasswordHash, PasswordVerifier}; -use argon2::Argon2; -use jsonwebtoken::{decode, DecodingKey, Validation}; -use once_cell::sync::Lazy; +use crate::iam::verify::{basic, token}; use std::net::SocketAddr; -use std::sync::Arc; -use surrealdb::sql::Algorithm; -use surrealdb::sql::Value; -use surrealdb::Auth; use surrealdb::Session; use warp::Filter; const BASIC: &str = "Basic "; const TOKEN: &str = "Bearer "; -fn config(algo: Algorithm, code: String) -> Result<(DecodingKey, Validation), Error> { - match algo { - Algorithm::Hs256 => Ok(( - DecodingKey::from_secret(code.as_ref()), - Validation::new(jsonwebtoken::Algorithm::HS256), - )), - Algorithm::Hs384 => Ok(( - DecodingKey::from_secret(code.as_ref()), - Validation::new(jsonwebtoken::Algorithm::HS384), - )), - Algorithm::Hs512 => Ok(( - DecodingKey::from_secret(code.as_ref()), - Validation::new(jsonwebtoken::Algorithm::HS512), - )), - Algorithm::EdDSA => Ok(( - DecodingKey::from_ed_pem(code.as_ref())?, - Validation::new(jsonwebtoken::Algorithm::EdDSA), - )), - Algorithm::Es256 => Ok(( - DecodingKey::from_ec_pem(code.as_ref())?, - Validation::new(jsonwebtoken::Algorithm::ES256), - )), - Algorithm::Es384 => Ok(( - DecodingKey::from_ec_pem(code.as_ref())?, - Validation::new(jsonwebtoken::Algorithm::ES384), - )), - Algorithm::Es512 => Ok(( - DecodingKey::from_ec_pem(code.as_ref())?, - Validation::new(jsonwebtoken::Algorithm::ES384), - )), - Algorithm::Ps256 => Ok(( - DecodingKey::from_rsa_pem(code.as_ref())?, - Validation::new(jsonwebtoken::Algorithm::PS256), - )), - Algorithm::Ps384 => Ok(( - DecodingKey::from_rsa_pem(code.as_ref())?, - Validation::new(jsonwebtoken::Algorithm::PS384), - )), - Algorithm::Ps512 => Ok(( - DecodingKey::from_rsa_pem(code.as_ref())?, - Validation::new(jsonwebtoken::Algorithm::PS512), - )), - Algorithm::Rs256 => Ok(( - DecodingKey::from_rsa_pem(code.as_ref())?, - Validation::new(jsonwebtoken::Algorithm::RS256), - )), - Algorithm::Rs384 => Ok(( - DecodingKey::from_rsa_pem(code.as_ref())?, - Validation::new(jsonwebtoken::Algorithm::RS384), - )), - Algorithm::Rs512 => Ok(( - DecodingKey::from_rsa_pem(code.as_ref())?, - Validation::new(jsonwebtoken::Algorithm::RS512), - )), - } -} - -static KEY: Lazy = Lazy::new(|| DecodingKey::from_secret(&[])); - -static DUD: Lazy = Lazy::new(|| { - let mut validation = Validation::new(jsonwebtoken::Algorithm::HS256); - validation.insecure_disable_signature_validation(); - validation.validate_nbf = true; - validation.validate_exp = true; - validation -}); - pub fn build() -> impl Filter + Clone { // Enable on any path let conf = warp::any(); @@ -115,216 +38,18 @@ async fn process( ) -> Result { // Create session #[rustfmt::skip] - let session = Session { ip, or, id, ns, db, ..Default::default() }; + let mut session = Session { ip, or, id, ns, db, ..Default::default() }; // Parse the authentication header - let session = match au { + match au { // Basic authentication data was supplied - Some(auth) if auth.starts_with(BASIC) => basic(auth, session).await, + Some(auth) if auth.starts_with(BASIC) => basic(&mut session, auth).await, // Token authentication data was supplied - Some(auth) if auth.starts_with(TOKEN) => token(auth, session).await, + Some(auth) if auth.starts_with(TOKEN) => token(&mut session, auth).await, // Wrong authentication data was supplied Some(_) => Err(Error::InvalidAuth), // No authentication data was supplied - None => Ok(session), + None => Ok(()), }?; // Pass the authenticated session through Ok(session) } - -async fn basic(auth: String, mut session: Session) -> Result { - // Retrieve just the auth data - if let Some((_, auth)) = auth.split_once(' ') { - // Get a database reference - let kvs = DB.get().unwrap(); - // Get the config options - let opts = CF.get().unwrap(); - // Decode the encoded auth data - let auth = base64::decode(auth)?; - // Convert the auth data to String - let auth = String::from_utf8(auth)?; - // Split the auth data into user and pass - if let Some((user, pass)) = auth.split_once(':') { - // Check that the details are not empty - if user.is_empty() || pass.is_empty() { - return Err(Error::InvalidAuth); - } - // Check if this is root authentication - if user == opts.user && pass == opts.pass { - session.au = Arc::new(Auth::Kv); - return Ok(session); - } - // Check if this is NS authentication - if let Some(ns) = &session.ns { - // Create a new readonly transaction - let mut tx = kvs.transaction(false, false).await?; - // Check if the supplied NS Login exists - if let Ok(nl) = tx.get_nl(ns, user).await { - // Compute the hash and verify the password - let hash = PasswordHash::new(&nl.hash).unwrap(); - if Argon2::default().verify_password(pass.as_ref(), &hash).is_ok() { - session.au = Arc::new(Auth::Ns(ns.to_owned())); - return Ok(session); - } - }; - // Check if this is DB authentication - if let Some(db) = &session.db { - // Check if the supplied DB Login exists - if let Ok(dl) = tx.get_dl(ns, db, user).await { - // Compute the hash and verify the password - let hash = PasswordHash::new(&dl.hash).unwrap(); - if Argon2::default().verify_password(pass.as_ref(), &hash).is_ok() { - session.au = Arc::new(Auth::Db(ns.to_owned(), db.to_owned())); - return Ok(session); - } - }; - } - } - } - } - // There was an auth error - Err(Error::InvalidAuth) -} - -async fn token(auth: String, mut session: Session) -> Result { - // Retrieve just the auth data - if let Some((_, auth)) = auth.split_once(' ') { - // Get a database reference - let kvs = DB.get().unwrap(); - // Decode the token without verifying - let token = decode::(auth, &KEY, &DUD)?; - // Check the token authentication claims - match token.claims { - // Check if this is scope token authentication - Claims { - ns: Some(ns), - db: Some(db), - sc: Some(sc), - tk: Some(tk), - id: Some(id), - .. - } => { - // Create a new readonly transaction - let mut tx = kvs.transaction(false, false).await?; - // Parse the record id - let id = surrealdb::sql::thing(&id)?; - // Get the scope token - let de = tx.get_st(&ns, &db, &sc, &tk).await?; - let cf = config(de.kind, de.code)?; - // Verify the token - decode::(auth, &cf.0, &cf.1)?; - // Set the session - session.ns = Some(ns.to_owned()); - session.db = Some(db.to_owned()); - session.sc = Some(sc.to_owned()); - session.sd = Some(Value::from(id)); - session.au = Arc::new(Auth::Sc(ns, db, sc)); - return Ok(session); - } - // Check if this is scope authentication - Claims { - ns: Some(ns), - db: Some(db), - sc: Some(sc), - id: Some(id), - .. - } => { - // Create a new readonly transaction - let mut tx = kvs.transaction(false, false).await?; - // Parse the record id - let id = surrealdb::sql::thing(&id)?; - // Get the scope - let de = tx.get_sc(&ns, &db, &sc).await?; - let cf = config(Algorithm::Hs512, de.code)?; - // Verify the token - decode::(auth, &cf.0, &cf.1)?; - // Set the session - session.ns = Some(ns.to_owned()); - session.db = Some(db.to_owned()); - session.sc = Some(sc.to_owned()); - session.sd = Some(Value::from(id)); - session.au = Arc::new(Auth::Sc(ns, db, sc)); - return Ok(session); - } - // Check if this is database token authentication - Claims { - ns: Some(ns), - db: Some(db), - tk: Some(tk), - .. - } => { - // Create a new readonly transaction - let mut tx = kvs.transaction(false, false).await?; - // Get the database token - let de = tx.get_dt(&ns, &db, &tk).await?; - let cf = config(de.kind, de.code)?; - // Verify the token - decode::(auth, &cf.0, &cf.1)?; - // Set the session - session.ns = Some(ns.to_owned()); - session.db = Some(db.to_owned()); - session.au = Arc::new(Auth::Db(ns, db)); - return Ok(session); - } - // Check if this is database authentication - Claims { - ns: Some(ns), - db: Some(db), - id: Some(id), - .. - } => { - // Create a new readonly transaction - let mut tx = kvs.transaction(false, false).await?; - // Get the database login - let de = tx.get_dl(&ns, &db, &id).await?; - let cf = config(Algorithm::Hs512, de.code)?; - // Verify the token - decode::(auth, &cf.0, &cf.1)?; - // Set the session - session.ns = Some(ns.to_owned()); - session.db = Some(db.to_owned()); - session.au = Arc::new(Auth::Db(ns, db)); - return Ok(session); - } - // Check if this is namespace token authentication - Claims { - ns: Some(ns), - tk: Some(tk), - .. - } => { - // Create a new readonly transaction - let mut tx = kvs.transaction(false, false).await?; - // Get the namespace token - let de = tx.get_nt(&ns, &tk).await?; - let cf = config(de.kind, de.code)?; - // Verify the token - decode::(auth, &cf.0, &cf.1)?; - // Set the session - session.ns = Some(ns.to_owned()); - session.au = Arc::new(Auth::Ns(ns)); - return Ok(session); - } - // Check if this is namespace authentication - Claims { - ns: Some(ns), - id: Some(id), - .. - } => { - // Create a new readonly transaction - let mut tx = kvs.transaction(false, false).await?; - // Get the namespace login - let de = tx.get_nl(&ns, &id).await?; - let cf = config(Algorithm::Hs512, de.code)?; - // Verify the token - decode::(auth, &cf.0, &cf.1)?; - // Set the session - session.ns = Some(ns.to_owned()); - session.au = Arc::new(Auth::Ns(ns)); - return Ok(session); - } - // There was an auth error - _ => return Err(Error::InvalidAuth), - }; - } - // There was an auth error - Err(Error::InvalidAuth) -} diff --git a/src/net/signin.rs b/src/net/signin.rs index a69521e1..89731d81 100644 --- a/src/net/signin.rs +++ b/src/net/signin.rs @@ -1,17 +1,8 @@ -use crate::cnf::SERVER_NAME; use crate::err::Error; use crate::net::head; -use crate::net::jwt::{Claims, HEADER}; -use crate::net::DB; -use argon2::password_hash::{PasswordHash, PasswordVerifier}; -use argon2::Argon2; use bytes::Bytes; -use chrono::{Duration, Utc}; -use jsonwebtoken::{encode, EncodingKey}; use std::str; -use surrealdb::sql::Object; use surrealdb::sql::Value; -use surrealdb::Session; use warp::http::Response; use warp::Filter; @@ -33,233 +24,18 @@ pub fn config() -> impl Filter 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)) => { - // Parse the speficied variables - let ns = vars.get("NS").or_else(|| vars.get("ns")); - let db = vars.get("DB").or_else(|| vars.get("db")); - let sc = vars.get("SC").or_else(|| vars.get("sc")); - // Match the authentication type - match (ns, db, sc) { - (Some(ns), Some(db), Some(sc)) => { - // Process the provided values - let ns = ns.to_strand().as_string(); - let db = db.to_strand().as_string(); - let sc = sc.to_strand().as_string(); - // Attempt to signin to specified scope - match signin_sc(ns, db, sc, vars).await { - // Namespace authentication was successful - Ok(v) => Ok(Response::builder().body(v)), - // There was an error with authentication - Err(e) => Err(warp::reject::custom(e)), - } - } - (Some(ns), Some(db), 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 ns = ns.to_strand().as_string(); - let db = db.to_strand().as_string(); - let user = user.to_strand().as_string(); - let pass = pass.to_strand().as_string(); - // Attempt to signin to database - match signin_db(ns, db, user, pass).await { - // Namespace authentication was successful - Ok(v) => Ok(Response::builder().body(v)), - // There was an error with authentication - Err(e) => Err(warp::reject::custom(e)), - } - } - // There is no username or password - _ => Err(warp::reject::custom(Error::InvalidAuth)), - } - } - (Some(ns), 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 ns = ns.to_strand().as_string(); - let user = user.to_strand().as_string(); - let pass = pass.to_strand().as_string(); - // Attempt to signin to namespace - match signin_ns(ns, user, pass).await { - // Namespace authentication was successful - Ok(v) => Ok(Response::builder().body(v)), - // There was an error with authentication - Err(e) => Err(warp::reject::custom(e)), - } - } - // There is no username or password - _ => Err(warp::reject::custom(Error::InvalidAuth)), - } - } - // No NS, DB, or SC keys were specified - _ => Err(warp::reject::custom(Error::InvalidAuth)), - } - } + Ok(Value::Object(vars)) => match crate::iam::signin::signin(vars).await { + // Authentication was successful + Ok(v) => Ok(Response::builder().body(v)), + // There was an error with authentication + Err(e) => Err(warp::reject::custom(e)), + }, // The provided value was not an object _ => Err(warp::reject::custom(Error::Request)), } } - -async fn signin_sc(ns: String, db: String, sc: String, vars: Object) -> Result { - // Get a database reference - let kvs = DB.get().unwrap(); - // Create a new readonly transaction - let mut tx = kvs.transaction(false, false).await?; - // Check if the supplied NS Login exists - match tx.get_sc(&ns, &db, &sc).await { - Ok(sv) => { - match sv.signin { - // This scope allows signin - Some(val) => { - // Setup the query params - let vars = Some(vars.0); - // Setup the query session - let sess = Session::for_db(&ns, &db); - // Compute the value with the params - match kvs.compute(val, &sess, vars).await { - // The signin value succeeded - Ok(val) => match val.rid() { - // There is a record returned - Some(rid) => { - // Create the authentication key - let key = EncodingKey::from_secret(sv.code.as_ref()); - // Create the authentication claim - let val = Claims { - iss: SERVER_NAME.to_owned(), - iat: Utc::now().timestamp(), - nbf: Utc::now().timestamp(), - exp: match sv.session { - Some(v) => Utc::now() + Duration::from_std(v.0).unwrap(), - _ => Utc::now() + Duration::hours(1), - } - .timestamp(), - ns: Some(ns), - db: Some(db), - sc: Some(sc), - id: Some(rid.to_raw()), - ..Claims::default() - }; - // Create the authentication token - match encode(&*HEADER, &val, &key) { - // The auth token was created successfully - Ok(tk) => Ok(tk), - // There was an error creating the token - _ => Err(Error::InvalidAuth), - } - } - // No record was returned - _ => Err(Error::InvalidAuth), - }, - // The signin query failed - _ => Err(Error::InvalidAuth), - } - } - // This scope does not allow signin - _ => Err(Error::InvalidAuth), - } - } - // The scope does not exists - _ => Err(Error::InvalidAuth), - } -} - -async fn signin_db(ns: String, db: String, user: String, pass: String) -> Result { - // Get a database reference - let kvs = DB.get().unwrap(); - // Create a new readonly transaction - let mut tx = kvs.transaction(false, false).await?; - // Check if the supplied DB Login exists - match tx.get_dl(&ns, &db, &user).await { - Ok(dl) => { - // Compute the hash and verify the password - let hash = PasswordHash::new(&dl.hash).unwrap(); - // Attempt to verify the password using Argon2 - match Argon2::default().verify_password(pass.as_ref(), &hash) { - Ok(_) => { - // Create the authentication key - let key = EncodingKey::from_secret(dl.code.as_ref()); - // Create the authentication claim - let val = Claims { - iss: SERVER_NAME.to_owned(), - iat: Utc::now().timestamp(), - nbf: Utc::now().timestamp(), - exp: (Utc::now() + Duration::hours(1)).timestamp(), - ns: Some(ns), - db: Some(db), - id: Some(user), - ..Claims::default() - }; - // Create the authentication token - match encode(&*HEADER, &val, &key) { - // The auth token was created successfully - Ok(tk) => Ok(tk), - // There was an error creating the token - _ => Err(Error::InvalidAuth), - } - } - // The password did not verify - _ => Err(Error::InvalidAuth), - } - } - // The specified user login does not exist - _ => Err(Error::InvalidAuth), - } -} - -async fn signin_ns(ns: String, user: String, pass: String) -> Result { - // Get a database reference - let kvs = DB.get().unwrap(); - // Create a new readonly transaction - let mut tx = kvs.transaction(false, false).await?; - // Check if the supplied NS Login exists - match tx.get_nl(&ns, &user).await { - Ok(nl) => { - // Compute the hash and verify the password - let hash = PasswordHash::new(&nl.hash).unwrap(); - // Attempt to verify the password using Argon2 - match Argon2::default().verify_password(pass.as_ref(), &hash) { - Ok(_) => { - // Create the authentication key - let key = EncodingKey::from_secret(nl.code.as_ref()); - // Create the authentication claim - let val = Claims { - iss: SERVER_NAME.to_owned(), - iat: Utc::now().timestamp(), - nbf: Utc::now().timestamp(), - exp: (Utc::now() + Duration::hours(1)).timestamp(), - ns: Some(ns), - id: Some(user), - ..Claims::default() - }; - // Create the authentication token - match encode(&*HEADER, &val, &key) { - // The auth token was created successfully - Ok(tk) => Ok(tk), - // There was an error creating the token - _ => Err(Error::InvalidAuth), - } - } - // The password did not verify - _ => Err(Error::InvalidAuth), - } - } - // The specified user login does not exist - _ => Err(Error::InvalidAuth), - } -} diff --git a/src/net/signup.rs b/src/net/signup.rs index 59056963..b0498f00 100644 --- a/src/net/signup.rs +++ b/src/net/signup.rs @@ -1,15 +1,8 @@ -use crate::cnf::SERVER_NAME; use crate::err::Error; use crate::net::head; -use crate::net::jwt::{Claims, HEADER}; -use crate::net::DB; use bytes::Bytes; -use chrono::{Duration, Utc}; -use jsonwebtoken::{encode, EncodingKey}; use std::str; -use surrealdb::sql::Object; use surrealdb::sql::Value; -use surrealdb::Session; use warp::http::Response; use warp::Filter; @@ -31,99 +24,18 @@ pub fn config() -> impl Filter 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)) => { - // Parse the speficied variables - let ns = vars.get("NS").or_else(|| vars.get("ns")); - let db = vars.get("DB").or_else(|| vars.get("db")); - let sc = vars.get("SC").or_else(|| vars.get("sc")); - // Match the authentication type - match (ns, db, sc) { - (Some(ns), Some(db), Some(sc)) => { - // Process the provided values - let ns = ns.to_strand().as_string(); - let db = db.to_strand().as_string(); - let sc = sc.to_strand().as_string(); - // Attempt to signin to specified scope - match signup_sc(ns, db, sc, vars).await { - // Namespace authentication was successful - Ok(v) => Ok(Response::builder().body(v)), - // There was an error with authentication - Err(e) => Err(warp::reject::custom(e)), - } - } - // No NS, DB, or SC keys were specified - _ => Err(warp::reject::custom(Error::InvalidAuth)), - } - } + Ok(Value::Object(vars)) => match crate::iam::signup::signup(vars).await { + // Authentication was successful + Ok(v) => Ok(Response::builder().body(v)), + // There was an error with authentication + Err(e) => Err(warp::reject::custom(e)), + }, // The provided value was not an object _ => Err(warp::reject::custom(Error::Request)), } } - -async fn signup_sc(ns: String, db: String, sc: String, vars: Object) -> Result { - // Get a database reference - let kvs = DB.get().unwrap(); - // Create a new readonly transaction - let mut tx = kvs.transaction(false, false).await?; - // Check if the supplied NS Login exists - match tx.get_sc(&ns, &db, &sc).await { - Ok(sv) => { - match sv.signup { - // This scope allows signin - Some(val) => { - // Setup the query params - let vars = Some(vars.0); - // Setup the query session - let sess = Session::for_db(&ns, &db); - // Compute the value with the params - match kvs.compute(val, &sess, vars).await { - // The signin value succeeded - Ok(val) => match val.rid() { - // There is a record returned - Some(rid) => { - // Create the authentication key - let key = EncodingKey::from_secret(sv.code.as_ref()); - // Create the authentication claim - let val = Claims { - iss: SERVER_NAME.to_owned(), - iat: Utc::now().timestamp(), - nbf: Utc::now().timestamp(), - exp: match sv.session { - Some(v) => Utc::now() + Duration::from_std(v.0).unwrap(), - _ => Utc::now() + Duration::hours(1), - } - .timestamp(), - ns: Some(ns), - db: Some(db), - sc: Some(sc), - id: Some(rid.to_raw()), - ..Claims::default() - }; - // Create the authentication token - match encode(&*HEADER, &val, &key) { - // The auth token was created successfully - Ok(tk) => Ok(tk), - // There was an error creating the token - _ => Err(Error::InvalidAuth), - } - } - // No record was returned - _ => Err(Error::InvalidAuth), - }, - // The signin query failed - _ => Err(Error::InvalidAuth), - } - } - // This scope does not allow signin - _ => Err(Error::InvalidAuth), - } - } - // The scope does not exists - _ => Err(Error::InvalidAuth), - } -} diff --git a/src/net/sql.rs b/src/net/sql.rs index 38e1a249..14d6269c 100644 --- a/src/net/sql.rs +++ b/src/net/sql.rs @@ -1,8 +1,8 @@ +use crate::dbs::DB; use crate::err::Error; use crate::net::head; use crate::net::output; use crate::net::session; -use crate::net::DB; use bytes::Bytes; use futures::{SinkExt, StreamExt}; use surrealdb::Session;