Allow root authentication over WebSocket connection

This commit is contained in:
Tobie Morgan Hitchcock 2022-08-23 23:44:13 +01:00
parent 89915f9a62
commit e5a78bed06
5 changed files with 110 additions and 29 deletions

View file

@ -7,10 +7,12 @@ use argon2::password_hash::{PasswordHash, PasswordVerifier};
use argon2::Argon2; use argon2::Argon2;
use chrono::{Duration, Utc}; use chrono::{Duration, Utc};
use jsonwebtoken::{encode, EncodingKey}; use jsonwebtoken::{encode, EncodingKey};
use std::sync::Arc;
use surrealdb::sql::Object; use surrealdb::sql::Object;
use surrealdb::Auth;
use surrealdb::Session; use surrealdb::Session;
pub async fn signin(vars: Object) -> Result<String, Error> { pub async fn signin(session: &mut Session, vars: Object) -> Result<String, Error> {
// Parse the specified variables // Parse the specified variables
let ns = vars.get("NS").or_else(|| vars.get("ns")); let ns = vars.get("NS").or_else(|| vars.get("ns"));
let db = vars.get("DB").or_else(|| vars.get("db")); let db = vars.get("DB").or_else(|| vars.get("db"));
@ -23,7 +25,7 @@ pub async fn signin(vars: Object) -> Result<String, Error> {
let db = db.to_strand().as_string(); let db = db.to_strand().as_string();
let sc = sc.to_strand().as_string(); let sc = sc.to_strand().as_string();
// Attempt to signin to specified scope // Attempt to signin to specified scope
let res = super::signin::sc(ns, db, sc, vars).await?; let res = super::signin::sc(session, ns, db, sc, vars).await?;
// Return the result to the client // Return the result to the client
Ok(res) Ok(res)
} }
@ -41,7 +43,7 @@ pub async fn signin(vars: Object) -> Result<String, Error> {
let user = user.to_strand().as_string(); let user = user.to_strand().as_string();
let pass = pass.to_strand().as_string(); let pass = pass.to_strand().as_string();
// Attempt to signin to database // Attempt to signin to database
let res = super::signin::db(ns, db, user, pass).await?; let res = super::signin::db(session, ns, db, user, pass).await?;
// Return the result to the client // Return the result to the client
Ok(res) Ok(res)
} }
@ -62,7 +64,27 @@ pub async fn signin(vars: Object) -> Result<String, Error> {
let user = user.to_strand().as_string(); let user = user.to_strand().as_string();
let pass = pass.to_strand().as_string(); let pass = pass.to_strand().as_string();
// Attempt to signin to namespace // Attempt to signin to namespace
let res = super::signin::ns(ns, user, pass).await?; let res = super::signin::ns(session, ns, user, pass).await?;
// Return the result to the client
Ok(res)
}
// There is no username or password
_ => Err(Error::InvalidAuth),
}
}
(None, None, None) => {
// Get the provided user and pass
let user = vars.get("user");
let pass = vars.get("pass");
// Validate the user and pass
match (user, pass) {
// There is a username and password
(Some(user), Some(pass)) => {
// Process the provided values
let user = user.to_strand().as_string();
let pass = pass.to_strand().as_string();
// Attempt to signin to namespace
let res = super::signin::su(session, user, pass).await?;
// Return the result to the client // Return the result to the client
Ok(res) Ok(res)
} }
@ -74,7 +96,13 @@ pub async fn signin(vars: Object) -> Result<String, Error> {
} }
} }
pub async fn sc(ns: String, db: String, sc: String, vars: Object) -> Result<String, Error> { pub async fn sc(
session: &mut Session,
ns: String,
db: String,
sc: String,
vars: Object,
) -> Result<String, Error> {
// Get a database reference // Get a database reference
let kvs = DB.get().unwrap(); let kvs = DB.get().unwrap();
// Get local copy of options // Get local copy of options
@ -109,12 +137,14 @@ pub async fn sc(ns: String, db: String, sc: String, vars: Object) -> Result<Stri
_ => Utc::now() + Duration::hours(1), _ => Utc::now() + Duration::hours(1),
} }
.timestamp(), .timestamp(),
ns: Some(ns), ns: Some(ns.to_owned()),
db: Some(db), db: Some(db.to_owned()),
sc: Some(sc), sc: Some(sc.to_owned()),
id: Some(rid.to_raw()), id: Some(rid.to_raw()),
..Claims::default() ..Claims::default()
}; };
// Set the authentication on the sesssion
session.au = Arc::new(Auth::Sc(ns, db, sc));
// Create the authentication token // Create the authentication token
match encode(&*HEADER, &val, &key) { match encode(&*HEADER, &val, &key) {
// The auth token was created successfully // The auth token was created successfully
@ -139,7 +169,13 @@ pub async fn sc(ns: String, db: String, sc: String, vars: Object) -> Result<Stri
} }
} }
pub async fn db(ns: String, db: String, user: String, pass: String) -> Result<String, Error> { pub async fn db(
session: &mut Session,
ns: String,
db: String,
user: String,
pass: String,
) -> Result<String, Error> {
// Get a database reference // Get a database reference
let kvs = DB.get().unwrap(); let kvs = DB.get().unwrap();
// Create a new readonly transaction // Create a new readonly transaction
@ -160,11 +196,13 @@ pub async fn db(ns: String, db: String, user: String, pass: String) -> Result<St
iat: Utc::now().timestamp(), iat: Utc::now().timestamp(),
nbf: Utc::now().timestamp(), nbf: Utc::now().timestamp(),
exp: (Utc::now() + Duration::hours(1)).timestamp(), exp: (Utc::now() + Duration::hours(1)).timestamp(),
ns: Some(ns), ns: Some(ns.to_owned()),
db: Some(db), db: Some(db.to_owned()),
id: Some(user), id: Some(user),
..Claims::default() ..Claims::default()
}; };
// Set the authentication on the sesssion
session.au = Arc::new(Auth::Db(ns, db));
// Create the authentication token // Create the authentication token
match encode(&*HEADER, &val, &key) { match encode(&*HEADER, &val, &key) {
// The auth token was created successfully // The auth token was created successfully
@ -182,7 +220,12 @@ pub async fn db(ns: String, db: String, user: String, pass: String) -> Result<St
} }
} }
pub async fn ns(ns: String, user: String, pass: String) -> Result<String, Error> { pub async fn ns(
session: &mut Session,
ns: String,
user: String,
pass: String,
) -> Result<String, Error> {
// Get a database reference // Get a database reference
let kvs = DB.get().unwrap(); let kvs = DB.get().unwrap();
// Create a new readonly transaction // Create a new readonly transaction
@ -203,10 +246,12 @@ pub async fn ns(ns: String, user: String, pass: String) -> Result<String, Error>
iat: Utc::now().timestamp(), iat: Utc::now().timestamp(),
nbf: Utc::now().timestamp(), nbf: Utc::now().timestamp(),
exp: (Utc::now() + Duration::hours(1)).timestamp(), exp: (Utc::now() + Duration::hours(1)).timestamp(),
ns: Some(ns), ns: Some(ns.to_owned()),
id: Some(user), id: Some(user),
..Claims::default() ..Claims::default()
}; };
// Set the authentication on the sesssion
session.au = Arc::new(Auth::Ns(ns));
// Create the authentication token // Create the authentication token
match encode(&*HEADER, &val, &key) { match encode(&*HEADER, &val, &key) {
// The auth token was created successfully // The auth token was created successfully
@ -223,3 +268,17 @@ pub async fn ns(ns: String, user: String, pass: String) -> Result<String, Error>
_ => Err(Error::InvalidAuth), _ => Err(Error::InvalidAuth),
} }
} }
pub async fn su(session: &mut Session, user: String, pass: String) -> Result<String, Error> {
// Get the config options
let opts = CF.get().unwrap();
// Attempt to verify the root user
if let Some(root) = &opts.pass {
if user == opts.user && &pass == root {
session.au = Arc::new(Auth::Kv);
return Ok(String::from(""));
}
}
// The specified user login does not exist
Err(Error::InvalidAuth)
}

View file

@ -5,10 +5,12 @@ use crate::err::Error;
use crate::iam::token::{Claims, HEADER}; use crate::iam::token::{Claims, HEADER};
use chrono::{Duration, Utc}; use chrono::{Duration, Utc};
use jsonwebtoken::{encode, EncodingKey}; use jsonwebtoken::{encode, EncodingKey};
use std::sync::Arc;
use surrealdb::sql::Object; use surrealdb::sql::Object;
use surrealdb::Auth;
use surrealdb::Session; use surrealdb::Session;
pub async fn signup(vars: Object) -> Result<String, Error> { pub async fn signup(session: &mut Session, vars: Object) -> Result<String, Error> {
// Parse the specified variables // Parse the specified variables
let ns = vars.get("NS").or_else(|| vars.get("ns")); let ns = vars.get("NS").or_else(|| vars.get("ns"));
let db = vars.get("DB").or_else(|| vars.get("db")); let db = vars.get("DB").or_else(|| vars.get("db"));
@ -21,7 +23,7 @@ pub async fn signup(vars: Object) -> Result<String, Error> {
let db = db.to_strand().as_string(); let db = db.to_strand().as_string();
let sc = sc.to_strand().as_string(); let sc = sc.to_strand().as_string();
// Attempt to signin to specified scope // Attempt to signin to specified scope
let res = super::signup::sc(ns, db, sc, vars).await?; let res = super::signup::sc(session, ns, db, sc, vars).await?;
// Return the result to the client // Return the result to the client
Ok(res) Ok(res)
} }
@ -29,7 +31,13 @@ pub async fn signup(vars: Object) -> Result<String, Error> {
} }
} }
pub async fn sc(ns: String, db: String, sc: String, vars: Object) -> Result<String, Error> { pub async fn sc(
session: &mut Session,
ns: String,
db: String,
sc: String,
vars: Object,
) -> Result<String, Error> {
// Get a database reference // Get a database reference
let kvs = DB.get().unwrap(); let kvs = DB.get().unwrap();
// Get local copy of options // Get local copy of options
@ -64,12 +72,14 @@ pub async fn sc(ns: String, db: String, sc: String, vars: Object) -> Result<Stri
_ => Utc::now() + Duration::hours(1), _ => Utc::now() + Duration::hours(1),
} }
.timestamp(), .timestamp(),
ns: Some(ns), ns: Some(ns.to_owned()),
db: Some(db), db: Some(db.to_owned()),
sc: Some(sc), sc: Some(sc.to_owned()),
id: Some(rid.to_raw()), id: Some(rid.to_raw()),
..Claims::default() ..Claims::default()
}; };
// Set the authentication on the sesssion
session.au = Arc::new(Auth::Sc(ns, db, sc));
// Create the authentication token // Create the authentication token
match encode(&*HEADER, &val, &key) { match encode(&*HEADER, &val, &key) {
// The auth token was created successfully // The auth token was created successfully

View file

@ -116,11 +116,11 @@ impl Rpc {
_ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await,
}, },
"signup" => match params.take_one() { "signup" => match params.take_one() {
Value::Object(v) => rpc.read().await.signup(v).await, Value::Object(v) => rpc.write().await.signup(v).await,
_ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await,
}, },
"signin" => match params.take_one() { "signin" => match params.take_one() {
Value::Object(v) => rpc.read().await.signin(v).await, Value::Object(v) => rpc.write().await.signin(v).await,
_ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await,
}, },
"invalidate" => match params.len() { "invalidate" => match params.len() {
@ -208,12 +208,18 @@ impl Rpc {
Ok(Value::None) Ok(Value::None)
} }
async fn signup(&self, vars: Object) -> Result<Value, Error> { async fn signup(&mut self, vars: Object) -> Result<Value, Error> {
crate::iam::signup::signup(vars).await.map(Into::into).map_err(Into::into) crate::iam::signup::signup(&mut self.session, vars)
.await
.map(Into::into)
.map_err(Into::into)
} }
async fn signin(&self, vars: Object) -> Result<Value, Error> { async fn signin(&mut self, vars: Object) -> Result<Value, Error> {
crate::iam::signin::signin(vars).await.map(Into::into).map_err(Into::into) crate::iam::signin::signin(&mut self.session, vars)
.await
.map(Into::into)
.map_err(Into::into)
} }
async fn invalidate(&mut self) -> Result<Value, Error> { async fn invalidate(&mut self) -> Result<Value, Error> {

View file

@ -1,7 +1,9 @@
use crate::err::Error; use crate::err::Error;
use crate::net::session;
use bytes::Bytes; use bytes::Bytes;
use std::str; use std::str;
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;
@ -15,6 +17,7 @@ pub fn config() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejecti
// Set post method // Set post method
let post = base let post = base
.and(warp::post()) .and(warp::post())
.and(session::build())
.and(warp::body::content_length_limit(MAX)) .and(warp::body::content_length_limit(MAX))
.and(warp::body::bytes()) .and(warp::body::bytes())
.and_then(handler); .and_then(handler);
@ -22,13 +25,13 @@ pub fn config() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejecti
opts.or(post) opts.or(post)
} }
async fn handler(body: Bytes) -> Result<impl warp::Reply, warp::Rejection> { async fn handler(mut session: Session, body: Bytes) -> Result<impl warp::Reply, warp::Rejection> {
// Convert the HTTP body into text // 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 // 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)) => match crate::iam::signin::signin(vars).await { Ok(Value::Object(vars)) => match crate::iam::signin::signin(&mut session, vars).await {
// Authentication was successful // Authentication was successful
Ok(v) => Ok(Response::builder().body(v)), Ok(v) => Ok(Response::builder().body(v)),
// There was an error with authentication // There was an error with authentication

View file

@ -1,7 +1,9 @@
use crate::err::Error; use crate::err::Error;
use crate::net::session;
use bytes::Bytes; use bytes::Bytes;
use std::str; use std::str;
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;
@ -15,6 +17,7 @@ pub fn config() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejecti
// Set post method // Set post method
let post = base let post = base
.and(warp::post()) .and(warp::post())
.and(session::build())
.and(warp::body::content_length_limit(MAX)) .and(warp::body::content_length_limit(MAX))
.and(warp::body::bytes()) .and(warp::body::bytes())
.and_then(handler); .and_then(handler);
@ -22,13 +25,13 @@ pub fn config() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejecti
opts.or(post) opts.or(post)
} }
async fn handler(body: Bytes) -> Result<impl warp::Reply, warp::Rejection> { async fn handler(mut session: Session, body: Bytes) -> Result<impl warp::Reply, warp::Rejection> {
// Convert the HTTP body into text // 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 // 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)) => match crate::iam::signup::signup(vars).await { Ok(Value::Object(vars)) => match crate::iam::signup::signup(&mut session, vars).await {
// Authentication was successful // Authentication was successful
Ok(v) => Ok(Response::builder().body(v)), Ok(v) => Ok(Response::builder().body(v)),
// There was an error with authentication // There was an error with authentication