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;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
|
pub static CF: OnceCell<Config> = OnceCell::new();
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub bind: SocketAddr,
|
pub bind: SocketAddr,
|
||||||
|
@ -10,7 +13,7 @@ pub struct Config {
|
||||||
pub key: Option<String>,
|
pub key: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(matches: &clap::ArgMatches) -> Config {
|
pub fn init(matches: &clap::ArgMatches) {
|
||||||
// Parse the server binding address
|
// Parse the server binding address
|
||||||
let bind = matches
|
let bind = matches
|
||||||
.value_of("bind")
|
.value_of("bind")
|
||||||
|
@ -26,13 +29,13 @@ pub fn parse(matches: &clap::ArgMatches) -> Config {
|
||||||
// Parse any TLS server security options
|
// Parse any TLS server security options
|
||||||
let crt = matches.value_of("web-crt").map(|v| v.to_owned());
|
let crt = matches.value_of("web-crt").map(|v| v.to_owned());
|
||||||
let key = matches.value_of("web-key").map(|v| v.to_owned());
|
let key = matches.value_of("web-key").map(|v| v.to_owned());
|
||||||
// Return a new config object
|
// Store the new config object
|
||||||
Config {
|
let _ = CF.set(Config {
|
||||||
bind,
|
bind,
|
||||||
path,
|
path,
|
||||||
user,
|
user,
|
||||||
pass,
|
pass,
|
||||||
crt,
|
crt,
|
||||||
key,
|
key,
|
||||||
}
|
});
|
||||||
}
|
}
|
|
@ -1,10 +1,13 @@
|
||||||
mod backup;
|
mod backup;
|
||||||
|
mod config;
|
||||||
mod export;
|
mod export;
|
||||||
mod import;
|
mod import;
|
||||||
mod log;
|
mod log;
|
||||||
mod start;
|
mod start;
|
||||||
mod version;
|
mod version;
|
||||||
|
|
||||||
|
pub use config::CF;
|
||||||
|
|
||||||
use crate::cnf::LOGO;
|
use crate::cnf::LOGO;
|
||||||
use clap::{Arg, Command};
|
use clap::{Arg, Command};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
|
@ -1,12 +1,19 @@
|
||||||
|
use super::config;
|
||||||
use crate::cnf::LOGO;
|
use crate::cnf::LOGO;
|
||||||
|
use crate::dbs;
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
use crate::net;
|
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
|
// output SurrealDB logo
|
||||||
println!("{}", LOGO);
|
println!("{}", LOGO);
|
||||||
// Start up the web server
|
// Setup the cli options
|
||||||
net::init(matches)?;
|
config::init(matches);
|
||||||
// Don't error when done
|
// Start the kvs server
|
||||||
|
dbs::init().await?;
|
||||||
|
// Start the web server
|
||||||
|
net::init().await?;
|
||||||
|
// All ok
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,10 +11,7 @@ macro_rules! get_cfg {
|
||||||
|
|
||||||
pub fn init(_: &clap::ArgMatches) -> Result<(), Error> {
|
pub fn init(_: &clap::ArgMatches) -> Result<(), Error> {
|
||||||
get_cfg!(target_os: "windows", "macos", "ios", "linux", "android", "freebsd", "openbsd", "netbsd");
|
get_cfg!(target_os: "windows", "macos", "ios", "linux", "android", "freebsd", "openbsd", "netbsd");
|
||||||
|
|
||||||
get_cfg!(target_arch: "x86", "x86_64", "mips", "powerpc", "powerpc64", "arm", "aarch64");
|
get_cfg!(target_arch: "x86", "x86_64", "mips", "powerpc", "powerpc64", "arm", "aarch64");
|
||||||
|
|
||||||
println!("{} {} for {} on {}", NAME, VERSION, target_os(), target_arch());
|
println!("{} {} for {} on {}", NAME, VERSION, target_os(), target_arch());
|
||||||
|
|
||||||
Ok(())
|
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 cli;
|
||||||
mod cnf;
|
mod cnf;
|
||||||
|
mod dbs;
|
||||||
mod err;
|
mod err;
|
||||||
|
mod iam;
|
||||||
mod net;
|
mod net;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
use crate::dbs::DB;
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
use crate::net::session;
|
use crate::net::session;
|
||||||
use crate::net::DB;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use hyper::body::Body;
|
use hyper::body::Body;
|
||||||
use surrealdb::Session;
|
use surrealdb::Session;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
use crate::dbs::DB;
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
use crate::net::output;
|
use crate::net::output;
|
||||||
use crate::net::session;
|
use crate::net::session;
|
||||||
use crate::net::DB;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use surrealdb::Session;
|
use surrealdb::Session;
|
||||||
use warp::http;
|
use warp::http;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
|
use crate::dbs::DB;
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
use crate::net::head;
|
use crate::net::head;
|
||||||
use crate::net::output;
|
use crate::net::output;
|
||||||
use crate::net::session;
|
use crate::net::session;
|
||||||
use crate::net::DB;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
mod config;
|
|
||||||
mod export;
|
mod export;
|
||||||
mod fail;
|
mod fail;
|
||||||
mod head;
|
mod head;
|
||||||
mod import;
|
mod import;
|
||||||
mod index;
|
mod index;
|
||||||
mod jwt;
|
|
||||||
mod key;
|
mod key;
|
||||||
mod log;
|
mod log;
|
||||||
mod output;
|
mod output;
|
||||||
|
mod rpc;
|
||||||
mod session;
|
mod session;
|
||||||
mod signin;
|
mod signin;
|
||||||
mod signup;
|
mod signup;
|
||||||
|
@ -16,26 +15,11 @@ mod status;
|
||||||
mod sync;
|
mod sync;
|
||||||
mod version;
|
mod version;
|
||||||
|
|
||||||
|
use crate::cli::CF;
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
use config::Config;
|
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use surrealdb::Datastore;
|
|
||||||
use warp::Filter;
|
use warp::Filter;
|
||||||
|
|
||||||
static DB: OnceCell<Datastore> = OnceCell::new();
|
pub async fn init() -> Result<(), Error> {
|
||||||
|
|
||||||
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);
|
|
||||||
// Setup web routes
|
// Setup web routes
|
||||||
let net = index::config()
|
let net = index::config()
|
||||||
// Version endpoint
|
// Version endpoint
|
||||||
|
@ -52,6 +36,8 @@ pub async fn init(matches: &clap::ArgMatches) -> Result<(), Error> {
|
||||||
.or(import::config())
|
.or(import::config())
|
||||||
// Backup endpoint
|
// Backup endpoint
|
||||||
.or(sync::config())
|
.or(sync::config())
|
||||||
|
// RPC query endpoint
|
||||||
|
.or(rpc::config())
|
||||||
// SQL query endpoint
|
// SQL query endpoint
|
||||||
.or(sql::config())
|
.or(sql::config())
|
||||||
// API query endpoint
|
// API query endpoint
|
||||||
|
|
|
@ -1,89 +1,12 @@
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
use crate::net::jwt::Claims;
|
use crate::iam::verify::{basic, token};
|
||||||
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 std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::Arc;
|
|
||||||
use surrealdb::sql::Algorithm;
|
|
||||||
use surrealdb::sql::Value;
|
|
||||||
use surrealdb::Auth;
|
|
||||||
use surrealdb::Session;
|
use surrealdb::Session;
|
||||||
use warp::Filter;
|
use warp::Filter;
|
||||||
|
|
||||||
const BASIC: &str = "Basic ";
|
const BASIC: &str = "Basic ";
|
||||||
const TOKEN: &str = "Bearer ";
|
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 {
|
pub fn build() -> impl Filter<Extract = (Session,), Error = warp::Rejection> + Clone {
|
||||||
// Enable on any path
|
// Enable on any path
|
||||||
let conf = warp::any();
|
let conf = warp::any();
|
||||||
|
@ -115,216 +38,18 @@ async fn process(
|
||||||
) -> Result<Session, warp::Rejection> {
|
) -> Result<Session, warp::Rejection> {
|
||||||
// Create session
|
// Create session
|
||||||
#[rustfmt::skip]
|
#[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
|
// Parse the authentication header
|
||||||
let session = match au {
|
match au {
|
||||||
// Basic authentication data was supplied
|
// 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
|
// 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
|
// Wrong authentication data was supplied
|
||||||
Some(_) => Err(Error::InvalidAuth),
|
Some(_) => Err(Error::InvalidAuth),
|
||||||
// No authentication data was supplied
|
// No authentication data was supplied
|
||||||
None => Ok(session),
|
None => Ok(()),
|
||||||
}?;
|
}?;
|
||||||
// Pass the authenticated session through
|
// Pass the authenticated session through
|
||||||
Ok(session)
|
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::err::Error;
|
||||||
use crate::net::head;
|
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 bytes::Bytes;
|
||||||
use chrono::{Duration, Utc};
|
|
||||||
use jsonwebtoken::{encode, EncodingKey};
|
|
||||||
use std::str;
|
use std::str;
|
||||||
use surrealdb::sql::Object;
|
|
||||||
use surrealdb::sql::Value;
|
use surrealdb::sql::Value;
|
||||||
use surrealdb::Session;
|
|
||||||
use warp::http::Response;
|
use warp::http::Response;
|
||||||
use warp::Filter;
|
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> {
|
async fn handler(body: Bytes) -> Result<impl warp::Reply, warp::Rejection> {
|
||||||
//
|
// Convert the HTTP body into text
|
||||||
let data = str::from_utf8(&body).unwrap();
|
let data = str::from_utf8(&body).unwrap();
|
||||||
//
|
// Parse the provided data as JSON
|
||||||
match surrealdb::sql::json(data) {
|
match surrealdb::sql::json(data) {
|
||||||
// The provided value was an object
|
// The provided value was an object
|
||||||
Ok(Value::Object(vars)) => {
|
Ok(Value::Object(vars)) => match crate::iam::signin::signin(vars).await {
|
||||||
// Parse the speficied variables
|
// Authentication was successful
|
||||||
let ns = vars.get("NS").or_else(|| vars.get("ns"));
|
Ok(v) => Ok(Response::builder().body(v)),
|
||||||
let db = vars.get("DB").or_else(|| vars.get("db"));
|
// There was an error with authentication
|
||||||
let sc = vars.get("SC").or_else(|| vars.get("sc"));
|
Err(e) => Err(warp::reject::custom(e)),
|
||||||
// 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)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The provided value was not an object
|
// The provided value was not an object
|
||||||
_ => Err(warp::reject::custom(Error::Request)),
|
_ => 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::err::Error;
|
||||||
use crate::net::head;
|
use crate::net::head;
|
||||||
use crate::net::jwt::{Claims, HEADER};
|
|
||||||
use crate::net::DB;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use chrono::{Duration, Utc};
|
|
||||||
use jsonwebtoken::{encode, EncodingKey};
|
|
||||||
use std::str;
|
use std::str;
|
||||||
use surrealdb::sql::Object;
|
|
||||||
use surrealdb::sql::Value;
|
use surrealdb::sql::Value;
|
||||||
use surrealdb::Session;
|
|
||||||
use warp::http::Response;
|
use warp::http::Response;
|
||||||
use warp::Filter;
|
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> {
|
async fn handler(body: Bytes) -> Result<impl warp::Reply, warp::Rejection> {
|
||||||
//
|
// Convert the HTTP body into text
|
||||||
let data = str::from_utf8(&body).unwrap();
|
let data = str::from_utf8(&body).unwrap();
|
||||||
//
|
// Parse the provided data as JSON
|
||||||
match surrealdb::sql::json(data) {
|
match surrealdb::sql::json(data) {
|
||||||
// The provided value was an object
|
// The provided value was an object
|
||||||
Ok(Value::Object(vars)) => {
|
Ok(Value::Object(vars)) => match crate::iam::signup::signup(vars).await {
|
||||||
// Parse the speficied variables
|
// Authentication was successful
|
||||||
let ns = vars.get("NS").or_else(|| vars.get("ns"));
|
Ok(v) => Ok(Response::builder().body(v)),
|
||||||
let db = vars.get("DB").or_else(|| vars.get("db"));
|
// There was an error with authentication
|
||||||
let sc = vars.get("SC").or_else(|| vars.get("sc"));
|
Err(e) => Err(warp::reject::custom(e)),
|
||||||
// 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)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The provided value was not an object
|
// The provided value was not an object
|
||||||
_ => Err(warp::reject::custom(Error::Request)),
|
_ => 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::err::Error;
|
||||||
use crate::net::head;
|
use crate::net::head;
|
||||||
use crate::net::output;
|
use crate::net::output;
|
||||||
use crate::net::session;
|
use crate::net::session;
|
||||||
use crate::net::DB;
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures::{SinkExt, StreamExt};
|
use futures::{SinkExt, StreamExt};
|
||||||
use surrealdb::Session;
|
use surrealdb::Session;
|
||||||
|
|
Loading…
Reference in a new issue