Move authentication code to separate module
This commit is contained in:
parent
187d9c08dc
commit
7bb4aa74f3
20 changed files with 677 additions and 643 deletions
|
@ -1,5 +1,8 @@
|
|||
use once_cell::sync::OnceCell;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
pub static CF: OnceCell<Config> = OnceCell::new();
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Config {
|
||||
pub bind: SocketAddr,
|
||||
|
@ -10,7 +13,7 @@ pub struct Config {
|
|||
pub key: Option<String>,
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
});
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
17
src/dbs/mod.rs
Normal file
17
src/dbs/mod.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
use crate::cli::CF;
|
||||
use crate::err::Error;
|
||||
use once_cell::sync::OnceCell;
|
||||
use surrealdb::Datastore;
|
||||
|
||||
pub static DB: OnceCell<Datastore> = 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(())
|
||||
}
|
9
src/iam/clear.rs
Normal file
9
src/iam/clear.rs
Normal file
|
@ -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(())
|
||||
}
|
5
src/iam/mod.rs
Normal file
5
src/iam/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
pub mod clear;
|
||||
pub mod signin;
|
||||
pub mod signup;
|
||||
pub mod token;
|
||||
pub mod verify;
|
222
src/iam/signin.rs
Normal file
222
src/iam/signin.rs
Normal file
|
@ -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<String, Error> {
|
||||
// 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<String, Error> {
|
||||
// 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<String, Error> {
|
||||
// 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<String, Error> {
|
||||
// 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),
|
||||
}
|
||||
}
|
92
src/iam/signup.rs
Normal file
92
src/iam/signup.rs
Normal file
|
@ -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<String, Error> {
|
||||
// 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<String, Error> {
|
||||
// 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),
|
||||
}
|
||||
}
|
278
src/iam/verify.rs
Normal file
278
src/iam/verify.rs
Normal file
|
@ -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<DecodingKey> = Lazy::new(|| DecodingKey::from_secret(&[]));
|
||||
|
||||
static DUD: Lazy<Validation> = 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::<Claims>(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::<Claims>(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::<Claims>(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::<Claims>(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::<Claims>(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::<Claims>(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::<Claims>(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)
|
||||
}
|
|
@ -17,7 +17,9 @@ mod mac;
|
|||
|
||||
mod cli;
|
||||
mod cnf;
|
||||
mod dbs;
|
||||
mod err;
|
||||
mod iam;
|
||||
mod net;
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<Datastore> = OnceCell::new();
|
||||
|
||||
static CF: OnceCell<Config> = 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
|
||||
|
|
|
@ -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<DecodingKey> = Lazy::new(|| DecodingKey::from_secret(&[]));
|
||||
|
||||
static DUD: Lazy<Validation> = 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<Extract = (Session,), Error = warp::Rejection> + Clone {
|
||||
// Enable on any path
|
||||
let conf = warp::any();
|
||||
|
@ -115,216 +38,18 @@ async fn process(
|
|||
) -> Result<Session, warp::Rejection> {
|
||||
// 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<Session, 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(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<Session, 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::<Claims>(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::<Claims>(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::<Claims>(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::<Claims>(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::<Claims>(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::<Claims>(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::<Claims>(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)
|
||||
}
|
||||
|
|
|
@ -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<Extract = impl warp::Reply, Error = warp::Rejecti
|
|||
}
|
||||
|
||||
async fn handler(body: Bytes) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
//
|
||||
// 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<String, Error> {
|
||||
// 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<String, Error> {
|
||||
// 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<String, Error> {
|
||||
// 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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Extract = impl warp::Reply, Error = warp::Rejecti
|
|||
}
|
||||
|
||||
async fn handler(body: Bytes) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
//
|
||||
// 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<String, Error> {
|
||||
// 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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue