Add authentication support for local engines (#1908)
This commit is contained in:
parent
2237afb21a
commit
409ad61477
58 changed files with 1303 additions and 755 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4348,6 +4348,7 @@ dependencies = [
|
|||
"geo",
|
||||
"indexmap",
|
||||
"indxdb",
|
||||
"jsonwebtoken",
|
||||
"lexicmp",
|
||||
"log",
|
||||
"md-5",
|
||||
|
|
18
flake.lock
18
flake.lock
|
@ -32,11 +32,11 @@
|
|||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1683699806,
|
||||
"narHash": "sha256-Nw3pzHv9oRdIDmgetGWG5mqLDK78v3fTsReqANMjZ1Q=",
|
||||
"lastModified": 1683872481,
|
||||
"narHash": "sha256-BLXcc6oCbv98MGn/MSUTH8C4oKdczk/C5JqZ3ZlGAXU=",
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "71e7505152ef95dfddd0f5f0e5a755f4c3bbee86",
|
||||
"rev": "dd518e99e2833bb13e25570f88f2e16cdc5f8b4e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -110,11 +110,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1683627095,
|
||||
"narHash": "sha256-8u9SejRpL2TrMuHBdhYh4FKc1OGPDLyWTpIbNTtoHsA=",
|
||||
"lastModified": 1683802553,
|
||||
"narHash": "sha256-1KQfY1wJ0QgAl2hwbvzy09uhEtFlGUKqwfAzjX7NCYA=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a08e061a4ee8329747d54ddf1566d34c55c895eb",
|
||||
"rev": "1a0ffed73a73ba9527480150412982adeeeecb03",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -136,11 +136,11 @@
|
|||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1683653808,
|
||||
"narHash": "sha256-GiKwJySG/YCPIKwz9wSm9fJa5e4CU3GvTYAKZzjBhFo=",
|
||||
"lastModified": 1683815219,
|
||||
"narHash": "sha256-dC79Q2Xw8sBGz6a41H15XaT3pHQ6xP4EVY4axdxUb4E=",
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "b7cdd93f3e1533e96d4cfa1ac8573e6210a2bedf",
|
||||
"rev": "9b3387454d7c70ec768114871682ee2946ec88a8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
@ -76,7 +76,8 @@ fuzzy-matcher = "0.3.7"
|
|||
geo = { version = "0.25.0", features = ["use-serde"] }
|
||||
indexmap = { version = "1.9.3", features = ["serde"] }
|
||||
indxdb = { version = "0.3.0", optional = true }
|
||||
js = { version = "0.2.1" , package = "rquickjs", features = ["array-buffer", "bindgen", "classes", "futures", "loader", "macro", "parallel", "properties"], optional = true }
|
||||
js = { version = "0.2.1", package = "rquickjs", features = ["array-buffer", "bindgen", "classes", "futures", "loader", "macro", "parallel", "properties"], optional = true }
|
||||
jsonwebtoken = "8.3.0"
|
||||
lexicmp = "0.1.0"
|
||||
log = "0.4.17"
|
||||
md-5 = "0.10.5"
|
||||
|
|
|
@ -90,6 +90,14 @@ mod wasm;
|
|||
|
||||
use crate::api::conn::Method;
|
||||
use crate::api::err::Error;
|
||||
#[cfg(any(
|
||||
feature = "kv-mem",
|
||||
feature = "kv-tikv",
|
||||
feature = "kv-rocksdb",
|
||||
feature = "kv-fdb",
|
||||
feature = "kv-indxdb",
|
||||
))]
|
||||
use crate::api::opt::auth::Root;
|
||||
use crate::api::opt::Endpoint;
|
||||
#[cfg(any(
|
||||
feature = "kv-mem",
|
||||
|
@ -105,6 +113,7 @@ use crate::api::opt::Tls;
|
|||
use crate::api::Connect;
|
||||
use crate::api::Result;
|
||||
use crate::api::Surreal;
|
||||
use crate::dbs::Level;
|
||||
use std::marker::PhantomData;
|
||||
use url::Url;
|
||||
|
||||
|
@ -125,6 +134,9 @@ impl IntoEndpoint for &str {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -149,9 +161,9 @@ where
|
|||
{
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (address, config) = self;
|
||||
let mut address = address.into().into_endpoint()?;
|
||||
address.tls_config = Some(Tls::Native(config));
|
||||
Ok(address)
|
||||
let mut endpoint = address.into().into_endpoint()?;
|
||||
endpoint.tls_config = Some(Tls::Native(config));
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,9 +175,9 @@ where
|
|||
{
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (address, config) = self;
|
||||
let mut address = address.into().into_endpoint()?;
|
||||
address.tls_config = Some(Tls::Rust(config));
|
||||
Ok(address)
|
||||
let mut endpoint = address.into().into_endpoint()?;
|
||||
endpoint.tls_config = Some(Tls::Rust(config));
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,9 +205,140 @@ where
|
|||
T: Into<String>,
|
||||
{
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let mut address = IntoEndpoint::into_endpoint(self.0.into())?;
|
||||
address.strict = true;
|
||||
Ok(address)
|
||||
let mut endpoint = IntoEndpoint::into_endpoint(self.0.into())?;
|
||||
endpoint.strict = true;
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
feature = "kv-mem",
|
||||
feature = "kv-tikv",
|
||||
feature = "kv-rocksdb",
|
||||
feature = "kv-fdb",
|
||||
feature = "kv-indxdb",
|
||||
))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(
|
||||
feature = "kv-mem",
|
||||
feature = "kv-tikv",
|
||||
feature = "kv-rocksdb",
|
||||
feature = "kv-fdb",
|
||||
feature = "kv-indxdb",
|
||||
)))
|
||||
)]
|
||||
impl<T> IntoEndpoint for (T, Root<'_>)
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (address, root) = self;
|
||||
let mut endpoint = IntoEndpoint::into_endpoint(address.into())?;
|
||||
endpoint.auth = Level::Kv;
|
||||
endpoint.username = root.username.to_owned();
|
||||
endpoint.password = root.password.to_owned();
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(
|
||||
feature = "kv-mem",
|
||||
feature = "kv-tikv",
|
||||
feature = "kv-rocksdb",
|
||||
feature = "kv-fdb",
|
||||
feature = "kv-indxdb",
|
||||
))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(any(
|
||||
feature = "kv-mem",
|
||||
feature = "kv-tikv",
|
||||
feature = "kv-rocksdb",
|
||||
feature = "kv-fdb",
|
||||
feature = "kv-indxdb",
|
||||
)))
|
||||
)]
|
||||
impl<T> IntoEndpoint for (T, Strict, Root<'_>)
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (address, _, root) = self;
|
||||
let mut endpoint = IntoEndpoint::into_endpoint((address, root))?;
|
||||
endpoint.strict = true;
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
any(
|
||||
feature = "kv-mem",
|
||||
feature = "kv-tikv",
|
||||
feature = "kv-rocksdb",
|
||||
feature = "kv-fdb",
|
||||
feature = "kv-indxdb",
|
||||
),
|
||||
feature = "native-tls",
|
||||
))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(all(
|
||||
any(
|
||||
feature = "kv-mem",
|
||||
feature = "kv-tikv",
|
||||
feature = "kv-rocksdb",
|
||||
feature = "kv-fdb",
|
||||
feature = "kv-indxdb",
|
||||
),
|
||||
feature = "native-tls",
|
||||
)))
|
||||
)]
|
||||
impl<T> IntoEndpoint for (T, Strict, native_tls::TlsConnector)
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (address, _, config) = self;
|
||||
let mut endpoint = address.into().into_endpoint()?;
|
||||
endpoint.tls_config = Some(Tls::Native(config));
|
||||
endpoint.strict = true;
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
any(
|
||||
feature = "kv-mem",
|
||||
feature = "kv-tikv",
|
||||
feature = "kv-rocksdb",
|
||||
feature = "kv-fdb",
|
||||
feature = "kv-indxdb",
|
||||
),
|
||||
feature = "native-tls",
|
||||
))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(all(
|
||||
any(
|
||||
feature = "kv-mem",
|
||||
feature = "kv-tikv",
|
||||
feature = "kv-rocksdb",
|
||||
feature = "kv-fdb",
|
||||
feature = "kv-indxdb",
|
||||
),
|
||||
feature = "native-tls",
|
||||
)))
|
||||
)]
|
||||
impl<T> IntoEndpoint for (T, native_tls::TlsConnector, Root<'_>)
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (address, config, root) = self;
|
||||
let mut endpoint = (address, root).into_endpoint()?;
|
||||
endpoint.tls_config = Some(Tls::Native(config));
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,16 +367,51 @@ where
|
|||
feature = "rustls",
|
||||
)))
|
||||
)]
|
||||
impl<T> IntoEndpoint for (T, rustls::ClientConfig, Strict)
|
||||
impl<T> IntoEndpoint for (T, Strict, rustls::ClientConfig)
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (address, config, _) = self;
|
||||
let mut address = address.into().into_endpoint()?;
|
||||
address.tls_config = Some(Tls::Rust(config));
|
||||
address.strict = true;
|
||||
Ok(address)
|
||||
let (address, _, config) = self;
|
||||
let mut endpoint = address.into().into_endpoint()?;
|
||||
endpoint.tls_config = Some(Tls::Rust(config));
|
||||
endpoint.strict = true;
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
any(
|
||||
feature = "kv-mem",
|
||||
feature = "kv-tikv",
|
||||
feature = "kv-rocksdb",
|
||||
feature = "kv-fdb",
|
||||
feature = "kv-indxdb",
|
||||
),
|
||||
feature = "rustls",
|
||||
))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
doc(cfg(all(
|
||||
any(
|
||||
feature = "kv-mem",
|
||||
feature = "kv-tikv",
|
||||
feature = "kv-rocksdb",
|
||||
feature = "kv-fdb",
|
||||
feature = "kv-indxdb",
|
||||
),
|
||||
feature = "rustls",
|
||||
)))
|
||||
)]
|
||||
impl<T> IntoEndpoint for (T, rustls::ClientConfig, Root<'_>)
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (address, config, root) = self;
|
||||
let mut endpoint = (address, root).into_endpoint()?;
|
||||
endpoint.tls_config = Some(Tls::Rust(config));
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -135,7 +135,6 @@ impl Connection for Any {
|
|||
"http" | "https" => {
|
||||
#[cfg(feature = "protocol-http")]
|
||||
{
|
||||
features.insert(ExtraFeatures::Auth);
|
||||
features.insert(ExtraFeatures::Backup);
|
||||
let headers = http::default_headers();
|
||||
#[allow(unused_mut)]
|
||||
|
@ -168,7 +167,6 @@ impl Connection for Any {
|
|||
"ws" | "wss" => {
|
||||
#[cfg(feature = "protocol-ws")]
|
||||
{
|
||||
features.insert(ExtraFeatures::Auth);
|
||||
let url = address.endpoint.join(engine::remote::ws::PATH)?;
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
let maybe_connector = address.tls_config.map(Connector::from);
|
||||
|
|
|
@ -9,8 +9,6 @@ use crate::api::engine::any::Any;
|
|||
use crate::api::err::Error;
|
||||
use crate::api::opt::Endpoint;
|
||||
use crate::api::DbResponse;
|
||||
#[allow(unused_imports)] // used by the `ws` and `http` protocols
|
||||
use crate::api::ExtraFeatures;
|
||||
use crate::api::Result;
|
||||
use crate::api::Surreal;
|
||||
use crate::error::Db as DbError;
|
||||
|
@ -143,7 +141,6 @@ impl Connection for Any {
|
|||
"http" | "https" => {
|
||||
#[cfg(feature = "protocol-http")]
|
||||
{
|
||||
features.insert(ExtraFeatures::Auth);
|
||||
engine::remote::http::wasm::router(address, conn_tx, route_rx);
|
||||
}
|
||||
|
||||
|
@ -157,7 +154,6 @@ impl Connection for Any {
|
|||
"ws" | "wss" => {
|
||||
#[cfg(feature = "protocol-ws")]
|
||||
{
|
||||
features.insert(ExtraFeatures::Auth);
|
||||
let mut address = address;
|
||||
address.endpoint = address.endpoint.join(engine::remote::ws::PATH)?;
|
||||
engine::remote::ws::wasm::router(address, capacity, conn_tx, route_rx);
|
||||
|
|
|
@ -46,6 +46,7 @@ use crate::channel;
|
|||
use crate::dbs::Response;
|
||||
use crate::dbs::Session;
|
||||
use crate::kvs::Datastore;
|
||||
use crate::opt::auth::Root;
|
||||
use crate::opt::IntoEndpoint;
|
||||
use crate::sql::Array;
|
||||
use crate::sql::Query;
|
||||
|
@ -402,9 +403,10 @@ async fn take(one: bool, responses: Vec<Response>) -> Result<Value> {
|
|||
async fn router(
|
||||
(_, method, param): (i64, Method, Param),
|
||||
kvs: &Datastore,
|
||||
configured_root: &Option<Root<'_>>,
|
||||
strict: bool,
|
||||
session: &mut Session,
|
||||
vars: &mut BTreeMap<String, Value>,
|
||||
strict: bool,
|
||||
) -> Result<DbResponse> {
|
||||
let mut params = param.other;
|
||||
|
||||
|
@ -425,8 +427,35 @@ async fn router(
|
|||
}
|
||||
Ok(DbResponse::Other(Value::None))
|
||||
}
|
||||
Method::Signin | Method::Signup | Method::Authenticate | Method::Invalidate => {
|
||||
unreachable!()
|
||||
Method::Signup => {
|
||||
let credentials = match &mut params[..] {
|
||||
[Value::Object(credentials)] => mem::take(credentials),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let response = crate::iam::signup::signup(kvs, strict, session, credentials).await?;
|
||||
Ok(DbResponse::Other(response.into()))
|
||||
}
|
||||
Method::Signin => {
|
||||
let credentials = match &mut params[..] {
|
||||
[Value::Object(credentials)] => mem::take(credentials),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let response =
|
||||
crate::iam::signin::signin(kvs, configured_root, strict, session, credentials)
|
||||
.await?;
|
||||
Ok(DbResponse::Other(response.into()))
|
||||
}
|
||||
Method::Authenticate => {
|
||||
let token = match &mut params[..] {
|
||||
[Value::Strand(Strand(token))] => mem::take(token),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
crate::iam::verify::token(kvs, session, token).await?;
|
||||
Ok(DbResponse::Other(Value::None))
|
||||
}
|
||||
Method::Invalidate => {
|
||||
crate::iam::clear::clear(session)?;
|
||||
Ok(DbResponse::Other(Value::None))
|
||||
}
|
||||
Method::Create => {
|
||||
let statement = create_statement(&mut params);
|
||||
|
|
|
@ -10,8 +10,10 @@ use crate::api::opt::Endpoint;
|
|||
use crate::api::ExtraFeatures;
|
||||
use crate::api::Result;
|
||||
use crate::api::Surreal;
|
||||
use crate::dbs::Level;
|
||||
use crate::dbs::Session;
|
||||
use crate::kvs::Datastore;
|
||||
use crate::opt::auth::Root;
|
||||
use flume::Receiver;
|
||||
use flume::Sender;
|
||||
use futures::StreamExt;
|
||||
|
@ -114,12 +116,33 @@ pub(crate) fn router(
|
|||
}
|
||||
};
|
||||
|
||||
let mut session = Session::for_kv();
|
||||
let mut vars = BTreeMap::new();
|
||||
let mut stream = route_rx.into_stream();
|
||||
let configured_root = match address.auth {
|
||||
Level::Kv => Some(Root {
|
||||
username: &address.username,
|
||||
password: &address.password,
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
let mut session = if configured_root.is_some() {
|
||||
// If a root user is specified, lock down the database
|
||||
Session::default()
|
||||
} else {
|
||||
// If no root user is specified, the database should be open
|
||||
Session::for_kv()
|
||||
};
|
||||
|
||||
while let Some(Some(route)) = stream.next().await {
|
||||
match super::router(route.request, &kvs, &mut session, &mut vars, address.strict).await
|
||||
match super::router(
|
||||
route.request,
|
||||
&kvs,
|
||||
&configured_root,
|
||||
address.strict,
|
||||
&mut session,
|
||||
&mut vars,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(value) => {
|
||||
let _ = route.response.into_send_async(Ok(value)).await;
|
||||
|
|
|
@ -8,8 +8,10 @@ use crate::api::engine::local::Db;
|
|||
use crate::api::opt::Endpoint;
|
||||
use crate::api::Result;
|
||||
use crate::api::Surreal;
|
||||
use crate::dbs::Level;
|
||||
use crate::dbs::Session;
|
||||
use crate::kvs::Datastore;
|
||||
use crate::opt::auth::Root;
|
||||
use flume::Receiver;
|
||||
use flume::Sender;
|
||||
use futures::StreamExt;
|
||||
|
@ -102,12 +104,33 @@ pub(crate) fn router(
|
|||
}
|
||||
};
|
||||
|
||||
let mut session = Session::for_kv();
|
||||
let mut vars = BTreeMap::new();
|
||||
let mut stream = route_rx.into_stream();
|
||||
let configured_root = match address.auth {
|
||||
Level::Kv => Some(Root {
|
||||
username: &address.username,
|
||||
password: &address.password,
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
let mut session = if configured_root.is_some() {
|
||||
// If a root user is specified, lock down the database
|
||||
Session::default()
|
||||
} else {
|
||||
// If no root user is specified, the database should be open
|
||||
Session::for_kv()
|
||||
};
|
||||
|
||||
while let Some(Some(route)) = stream.next().await {
|
||||
match super::router(route.request, &kvs, &mut session, &mut vars, address.strict).await
|
||||
match super::router(
|
||||
route.request,
|
||||
&kvs,
|
||||
&configured_root,
|
||||
address.strict,
|
||||
&mut session,
|
||||
&mut vars,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(value) => {
|
||||
let _ = route.response.into_send_async(Ok(value)).await;
|
||||
|
|
|
@ -68,7 +68,6 @@ impl Connection for Client {
|
|||
router(base_url, client, route_rx);
|
||||
|
||||
let mut features = HashSet::new();
|
||||
features.insert(ExtraFeatures::Auth);
|
||||
features.insert(ExtraFeatures::Backup);
|
||||
|
||||
Ok(Surreal {
|
||||
|
|
|
@ -7,7 +7,6 @@ use crate::api::conn::Param;
|
|||
use crate::api::conn::Route;
|
||||
use crate::api::conn::Router;
|
||||
use crate::api::opt::Endpoint;
|
||||
use crate::api::ExtraFeatures;
|
||||
use crate::api::Result;
|
||||
use crate::api::Surreal;
|
||||
use flume::Receiver;
|
||||
|
@ -53,12 +52,9 @@ impl Connection for Client {
|
|||
return Err(error);
|
||||
}
|
||||
|
||||
let mut features = HashSet::new();
|
||||
features.insert(ExtraFeatures::Auth);
|
||||
|
||||
Ok(Surreal {
|
||||
router: OnceCell::with_value(Arc::new(Router {
|
||||
features,
|
||||
features: HashSet::new(),
|
||||
conn: PhantomData,
|
||||
sender: route_tx,
|
||||
last_id: AtomicI64::new(0),
|
||||
|
|
|
@ -14,7 +14,6 @@ use crate::api::err::Error;
|
|||
use crate::api::opt::Endpoint;
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
use crate::api::opt::Tls;
|
||||
use crate::api::ExtraFeatures;
|
||||
use crate::api::Result;
|
||||
use crate::api::Surreal;
|
||||
use crate::engine::remote::ws::IntervalStream;
|
||||
|
@ -128,12 +127,9 @@ impl Connection for Client {
|
|||
|
||||
router(url, maybe_connector, capacity, config, socket, route_rx);
|
||||
|
||||
let mut features = HashSet::new();
|
||||
features.insert(ExtraFeatures::Auth);
|
||||
|
||||
Ok(Surreal {
|
||||
router: OnceCell::with_value(Arc::new(Router {
|
||||
features,
|
||||
features: HashSet::new(),
|
||||
conn: PhantomData,
|
||||
sender: route_tx,
|
||||
last_id: AtomicI64::new(0),
|
||||
|
|
|
@ -12,7 +12,6 @@ use crate::api::engine::remote::ws::PING_INTERVAL;
|
|||
use crate::api::engine::remote::ws::PING_METHOD;
|
||||
use crate::api::err::Error;
|
||||
use crate::api::opt::Endpoint;
|
||||
use crate::api::ExtraFeatures;
|
||||
use crate::api::Result;
|
||||
use crate::api::Surreal;
|
||||
use crate::engine::remote::ws::IntervalStream;
|
||||
|
@ -83,12 +82,9 @@ impl Connection for Client {
|
|||
return Err(error);
|
||||
}
|
||||
|
||||
let mut features = HashSet::new();
|
||||
features.insert(ExtraFeatures::Auth);
|
||||
|
||||
Ok(Surreal {
|
||||
router: OnceCell::with_value(Arc::new(Router {
|
||||
features,
|
||||
features: HashSet::new(),
|
||||
conn: PhantomData,
|
||||
sender: route_tx,
|
||||
last_id: AtomicI64::new(0),
|
||||
|
|
|
@ -146,11 +146,6 @@ pub enum Error {
|
|||
/// it's running on
|
||||
#[error("The protocol or storage engine does not support backups on this architecture")]
|
||||
BackupsNotSupported,
|
||||
|
||||
/// The protocol or storage engine being used does not support authentication on the
|
||||
/// architecture it's running on
|
||||
#[error("The protocol or storage engine does not support authentication on this architecture")]
|
||||
AuthNotSupported,
|
||||
}
|
||||
|
||||
#[cfg(feature = "protocol-http")]
|
||||
|
|
|
@ -3,8 +3,6 @@ use crate::api::conn::Param;
|
|||
use crate::api::conn::Router;
|
||||
use crate::api::opt::auth::Jwt;
|
||||
use crate::api::Connection;
|
||||
use crate::api::Error;
|
||||
use crate::api::ExtraFeatures;
|
||||
use crate::api::Result;
|
||||
use std::future::Future;
|
||||
use std::future::IntoFuture;
|
||||
|
@ -28,9 +26,6 @@ where
|
|||
fn into_future(self) -> Self::IntoFuture {
|
||||
Box::pin(async move {
|
||||
let router = self.router?;
|
||||
if !router.features.contains(&ExtraFeatures::Auth) {
|
||||
return Err(Error::AuthNotSupported.into());
|
||||
}
|
||||
let mut conn = Client::new(Method::Authenticate);
|
||||
conn.execute_unit(router, Param::new(vec![self.token.into()])).await
|
||||
})
|
||||
|
|
|
@ -2,8 +2,6 @@ use crate::api::conn::Method;
|
|||
use crate::api::conn::Param;
|
||||
use crate::api::conn::Router;
|
||||
use crate::api::Connection;
|
||||
use crate::api::Error;
|
||||
use crate::api::ExtraFeatures;
|
||||
use crate::api::Result;
|
||||
use std::future::Future;
|
||||
use std::future::IntoFuture;
|
||||
|
@ -26,9 +24,6 @@ where
|
|||
fn into_future(self) -> Self::IntoFuture {
|
||||
Box::pin(async {
|
||||
let router = self.router?;
|
||||
if !router.features.contains(&ExtraFeatures::Auth) {
|
||||
return Err(Error::AuthNotSupported.into());
|
||||
}
|
||||
let mut conn = Client::new(Method::Invalidate);
|
||||
conn.execute_unit(router, Param::new(Vec::new())).await
|
||||
})
|
||||
|
|
|
@ -366,10 +366,6 @@ where
|
|||
|
||||
/// Signs up a user to a specific authentication scope
|
||||
///
|
||||
/// # Support
|
||||
///
|
||||
/// Currently only supported by the WS and HTTP protocols.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
|
@ -430,10 +426,6 @@ where
|
|||
|
||||
/// Signs this connection in to a specific authentication scope
|
||||
///
|
||||
/// # Support
|
||||
///
|
||||
/// Currently only supported by the WS and HTTP protocols.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Namespace signin
|
||||
|
@ -553,10 +545,6 @@ where
|
|||
|
||||
/// Invalidates the authentication for the current connection
|
||||
///
|
||||
/// # Support
|
||||
///
|
||||
/// Currently only supported by the WS and HTTP protocols.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
|
@ -575,10 +563,6 @@ where
|
|||
|
||||
/// Authenticates the current connection with a JWT token
|
||||
///
|
||||
/// # Support
|
||||
///
|
||||
/// Currently only supported by the WS and HTTP protocols.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
|
|
|
@ -486,7 +486,7 @@ mod tests {
|
|||
Err(Error::BackupsNotSupported.into()),
|
||||
Ok(vec![6.into()]),
|
||||
Ok(vec![7.into()]),
|
||||
Err(Error::AuthNotSupported.into()),
|
||||
Err(Error::DuplicateRequestId(0).into()),
|
||||
];
|
||||
let response = Response(to_map(response));
|
||||
let crate::Error::Api(Error::ConnectionUninitialised) = response.check().unwrap_err() else {
|
||||
|
@ -507,14 +507,14 @@ mod tests {
|
|||
Err(Error::BackupsNotSupported.into()),
|
||||
Ok(vec![6.into()]),
|
||||
Ok(vec![7.into()]),
|
||||
Err(Error::AuthNotSupported.into()),
|
||||
Err(Error::DuplicateRequestId(0).into()),
|
||||
];
|
||||
let mut response = Response(to_map(response));
|
||||
let errors = response.take_errors();
|
||||
assert_eq!(response.num_statements(), 8);
|
||||
assert_eq!(errors.len(), 3);
|
||||
let crate::Error::Api(Error::AuthNotSupported) = errors.get(&10).unwrap() else {
|
||||
panic!("index `10` is not `AuthNotSupported`");
|
||||
let crate::Error::Api(Error::DuplicateRequestId(0)) = errors.get(&10).unwrap() else {
|
||||
panic!("index `10` is not `DuplicateRequestId`");
|
||||
};
|
||||
let crate::Error::Api(Error::BackupsNotSupported) = errors.get(&7).unwrap() else {
|
||||
panic!("index `7` is not `BackupsNotSupported`");
|
||||
|
|
|
@ -2,8 +2,6 @@ use crate::api::conn::Method;
|
|||
use crate::api::conn::Param;
|
||||
use crate::api::conn::Router;
|
||||
use crate::api::Connection;
|
||||
use crate::api::Error;
|
||||
use crate::api::ExtraFeatures;
|
||||
use crate::api::Result;
|
||||
use crate::sql::Value;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
@ -37,9 +35,6 @@ where
|
|||
} = self;
|
||||
Box::pin(async move {
|
||||
let router = router?;
|
||||
if !router.features.contains(&ExtraFeatures::Auth) {
|
||||
return Err(Error::AuthNotSupported.into());
|
||||
}
|
||||
let mut conn = Client::new(Method::Signin);
|
||||
conn.execute(router, Param::new(vec![credentials?])).await
|
||||
})
|
||||
|
|
|
@ -2,8 +2,6 @@ use crate::api::conn::Method;
|
|||
use crate::api::conn::Param;
|
||||
use crate::api::conn::Router;
|
||||
use crate::api::Connection;
|
||||
use crate::api::Error;
|
||||
use crate::api::ExtraFeatures;
|
||||
use crate::api::Result;
|
||||
use crate::sql::Value;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
@ -37,9 +35,6 @@ where
|
|||
} = self;
|
||||
Box::pin(async move {
|
||||
let router = router?;
|
||||
if !router.features.contains(&ExtraFeatures::Auth) {
|
||||
return Err(Error::AuthNotSupported.into());
|
||||
}
|
||||
let mut conn = Client::new(Method::Signup);
|
||||
conn.execute(router, Param::new(vec![credentials?])).await
|
||||
})
|
||||
|
|
|
@ -11,6 +11,7 @@ use crate::api::Connect;
|
|||
use crate::api::ExtraFeatures;
|
||||
use crate::api::Result;
|
||||
use crate::api::Surreal;
|
||||
use crate::dbs::Level;
|
||||
use flume::Receiver;
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::collections::HashSet;
|
||||
|
@ -33,6 +34,9 @@ impl IntoEndpoint<Test> for () {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +77,6 @@ impl Connection for Client {
|
|||
Box::pin(async move {
|
||||
let (route_tx, route_rx) = flume::bounded(capacity);
|
||||
let mut features = HashSet::new();
|
||||
features.insert(ExtraFeatures::Auth);
|
||||
features.insert(ExtraFeatures::Backup);
|
||||
let router = Router {
|
||||
features,
|
||||
|
|
|
@ -128,7 +128,6 @@ where
|
|||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub(crate) enum ExtraFeatures {
|
||||
Auth,
|
||||
Backup,
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ pub struct Signin;
|
|||
pub trait Credentials<Action, Response>: Serialize {}
|
||||
|
||||
/// Credentials for the root user
|
||||
#[derive(Debug, Serialize)]
|
||||
#[derive(Debug, Clone, Copy, Serialize)]
|
||||
pub struct Root<'a> {
|
||||
/// The username of the root user
|
||||
#[serde(rename = "user")]
|
||||
|
@ -30,7 +30,7 @@ pub struct Root<'a> {
|
|||
impl Credentials<Signin, ()> for Root<'_> {}
|
||||
|
||||
/// Credentials for the namespace user
|
||||
#[derive(Debug, Serialize)]
|
||||
#[derive(Debug, Clone, Copy, Serialize)]
|
||||
pub struct Namespace<'a> {
|
||||
/// The namespace the user has access to
|
||||
#[serde(rename = "ns")]
|
||||
|
@ -46,7 +46,7 @@ pub struct Namespace<'a> {
|
|||
impl Credentials<Signin, Jwt> for Namespace<'_> {}
|
||||
|
||||
/// Credentials for the database user
|
||||
#[derive(Debug, Serialize)]
|
||||
#[derive(Debug, Clone, Copy, Serialize)]
|
||||
pub struct Database<'a> {
|
||||
/// The namespace the user has access to
|
||||
#[serde(rename = "ns")]
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
use crate::api::engine::local::Db;
|
||||
use crate::api::engine::local::FDb;
|
||||
use crate::api::err::Error;
|
||||
use crate::api::opt::auth::Root;
|
||||
use crate::api::opt::Endpoint;
|
||||
use crate::api::opt::IntoEndpoint;
|
||||
use crate::api::opt::Strict;
|
||||
use crate::api::Result;
|
||||
use crate::dbs::Level;
|
||||
use std::path::Path;
|
||||
use url::Url;
|
||||
|
||||
|
@ -18,6 +20,7 @@ impl IntoEndpoint<FDb> for &str {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -26,13 +29,8 @@ impl IntoEndpoint<FDb> for &Path {
|
|||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let url = format!("fdb://{}", self.display());
|
||||
Ok(Endpoint {
|
||||
endpoint: Url::parse(&url).map_err(|_| Error::InvalidUrl(url))?,
|
||||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
})
|
||||
let path = self.display();
|
||||
IntoEndpoint::<FDb>::into_endpoint(path.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,12 +41,39 @@ where
|
|||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let url = format!("fdb://{}", self.0.as_ref().display());
|
||||
Ok(Endpoint {
|
||||
endpoint: Url::parse(&url).map_err(|_| Error::InvalidUrl(url))?,
|
||||
strict: true,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
})
|
||||
let (path, _) = self;
|
||||
let mut endpoint = IntoEndpoint::<FDb>::into_endpoint(path.as_ref());
|
||||
endpoint.strict = true;
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoEndpoint<FDb> for (T, Root<'_>)
|
||||
where
|
||||
T: AsRef<Path>,
|
||||
{
|
||||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (path, root) = self;
|
||||
let mut endpoint = path.as_ref().into_endpoint()?;
|
||||
endpoint.auth = Level::Kv;
|
||||
endpoint.username = root.username.to_owned();
|
||||
endpoint.password = root.password.to_owned();
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoEndpoint<FDb> for (T, Strict, Root<'_>)
|
||||
where
|
||||
T: AsRef<Path>,
|
||||
{
|
||||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (path, _, root) = self;
|
||||
let mut endpoint = (path, root).into_endpoint()?;
|
||||
endpoint.strict = true;
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::api::opt::IntoEndpoint;
|
|||
use crate::api::opt::Tls;
|
||||
use crate::api::Endpoint;
|
||||
use crate::api::Result;
|
||||
use crate::dbs::Level;
|
||||
use std::net::SocketAddr;
|
||||
use url::Url;
|
||||
|
||||
|
@ -20,6 +21,9 @@ impl IntoEndpoint<Http> for &str {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +38,9 @@ impl IntoEndpoint<Http> for SocketAddr {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +55,9 @@ impl IntoEndpoint<Http> for String {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +72,9 @@ impl IntoEndpoint<Https> for &str {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -76,6 +89,9 @@ impl IntoEndpoint<Https> for SocketAddr {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -90,6 +106,9 @@ impl IntoEndpoint<Https> for String {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -104,9 +123,9 @@ where
|
|||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (address, config) = self;
|
||||
let mut address = address.into_endpoint()?;
|
||||
address.tls_config = Some(Tls::Native(config));
|
||||
Ok(address)
|
||||
let mut endpoint = address.into_endpoint()?;
|
||||
endpoint.tls_config = Some(Tls::Native(config));
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,8 +139,8 @@ where
|
|||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (address, config) = self;
|
||||
let mut address = address.into_endpoint()?;
|
||||
address.tls_config = Some(Tls::Rust(config));
|
||||
Ok(address)
|
||||
let mut endpoint = address.into_endpoint()?;
|
||||
endpoint.tls_config = Some(Tls::Rust(config));
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
use crate::api::engine::local::Db;
|
||||
use crate::api::engine::local::IndxDb;
|
||||
use crate::api::err::Error;
|
||||
use crate::api::opt::auth::Root;
|
||||
use crate::api::opt::Endpoint;
|
||||
use crate::api::opt::IntoEndpoint;
|
||||
use crate::api::opt::Strict;
|
||||
use crate::api::Result;
|
||||
use crate::dbs::Level;
|
||||
use url::Url;
|
||||
|
||||
impl IntoEndpoint<IndxDb> for &str {
|
||||
|
@ -17,6 +19,9 @@ impl IntoEndpoint<IndxDb> for &str {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +30,32 @@ impl IntoEndpoint<IndxDb> for (&str, Strict) {
|
|||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let mut address = IntoEndpoint::<IndxDb>::into_endpoint(self.0)?;
|
||||
address.strict = true;
|
||||
Ok(address)
|
||||
let mut endpoint = IntoEndpoint::<IndxDb>::into_endpoint(self.0)?;
|
||||
endpoint.strict = true;
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoEndpoint<IndxDb> for (&str, Root<'_>) {
|
||||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (name, root) = self;
|
||||
let mut endpoint = IntoEndpoint::<IndxDb>::into_endpoint(name)?;
|
||||
endpoint.auth = Level::Kv;
|
||||
endpoint.username = root.username.to_owned();
|
||||
endpoint.password = root.password.to_owned();
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoEndpoint<IndxDb> for (&str, Strict, Root<'_>) {
|
||||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (name, _, root) = self;
|
||||
let mut endpoint = IntoEndpoint::<IndxDb>::into_endpoint((name, root))?;
|
||||
endpoint.strict = true;
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use crate::api::engine::local::Db;
|
||||
use crate::api::engine::local::Mem;
|
||||
use crate::api::opt::auth::Root;
|
||||
use crate::api::opt::Endpoint;
|
||||
use crate::api::opt::IntoEndpoint;
|
||||
use crate::api::opt::Strict;
|
||||
use crate::api::Result;
|
||||
use crate::dbs::Level;
|
||||
use url::Url;
|
||||
|
||||
impl IntoEndpoint<Mem> for () {
|
||||
|
@ -15,6 +17,9 @@ impl IntoEndpoint<Mem> for () {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -23,8 +28,31 @@ impl IntoEndpoint<Mem> for Strict {
|
|||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let mut address = IntoEndpoint::<Mem>::into_endpoint(())?;
|
||||
address.strict = true;
|
||||
Ok(address)
|
||||
let mut endpoint = IntoEndpoint::<Mem>::into_endpoint(())?;
|
||||
endpoint.strict = true;
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoEndpoint<Mem> for Root<'_> {
|
||||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let mut endpoint = IntoEndpoint::<Mem>::into_endpoint(())?;
|
||||
endpoint.auth = Level::Kv;
|
||||
endpoint.username = self.username.to_owned();
|
||||
endpoint.password = self.password.to_owned();
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoEndpoint<Mem> for (Strict, Root<'_>) {
|
||||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (_, root) = self;
|
||||
let mut endpoint = IntoEndpoint::<Mem>::into_endpoint(root)?;
|
||||
endpoint.strict = true;
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ mod tikv;
|
|||
|
||||
use crate::api::Connection;
|
||||
use crate::api::Result;
|
||||
use crate::dbs::Level;
|
||||
use url::Url;
|
||||
|
||||
/// A server address used to connect to the server
|
||||
|
@ -29,6 +30,11 @@ pub struct Endpoint {
|
|||
pub(crate) strict: bool,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
pub(crate) tls_config: Option<super::Tls>,
|
||||
// Only used by the local engines
|
||||
// `Level::No` in this context means no authentication information was configured
|
||||
pub(crate) auth: Level,
|
||||
pub(crate) username: String,
|
||||
pub(crate) password: String,
|
||||
}
|
||||
|
||||
/// A trait for converting inputs to a server address object
|
||||
|
|
|
@ -2,10 +2,12 @@ use crate::api::engine::local::Db;
|
|||
use crate::api::engine::local::File;
|
||||
use crate::api::engine::local::RocksDb;
|
||||
use crate::api::err::Error;
|
||||
use crate::api::opt::auth::Root;
|
||||
use crate::api::opt::Endpoint;
|
||||
use crate::api::opt::IntoEndpoint;
|
||||
use crate::api::opt::Strict;
|
||||
use crate::api::Result;
|
||||
use crate::dbs::Level;
|
||||
use std::path::Path;
|
||||
use url::Url;
|
||||
|
||||
|
@ -19,6 +21,9 @@ impl IntoEndpoint<RocksDb> for &str {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -27,13 +32,8 @@ impl IntoEndpoint<RocksDb> for &Path {
|
|||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let url = format!("rocksdb://{}", self.display());
|
||||
Ok(Endpoint {
|
||||
endpoint: Url::parse(&url).map_err(|_| Error::InvalidUrl(url))?,
|
||||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
})
|
||||
let path = self.display().to_string();
|
||||
IntoEndpoint::<RocksDb>::into_endpoint(path.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,13 +44,40 @@ where
|
|||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let url = format!("rocksdb://{}", self.0.as_ref().display());
|
||||
Ok(Endpoint {
|
||||
endpoint: Url::parse(&url).map_err(|_| Error::InvalidUrl(url))?,
|
||||
strict: true,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
})
|
||||
let (path, _) = self;
|
||||
let mut endpoint = IntoEndpoint::<RocksDb>::into_endpoint(path.as_ref())?;
|
||||
endpoint.strict = true;
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoEndpoint<RocksDb> for (T, Root<'_>)
|
||||
where
|
||||
T: AsRef<Path>,
|
||||
{
|
||||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (path, root) = self;
|
||||
let mut endpoint = IntoEndpoint::<RocksDb>::into_endpoint(path.as_ref())?;
|
||||
endpoint.auth = Level::Kv;
|
||||
endpoint.username = root.username.to_owned();
|
||||
endpoint.password = root.password.to_owned();
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoEndpoint<RocksDb> for (T, Strict, Root<'_>)
|
||||
where
|
||||
T: AsRef<Path>,
|
||||
{
|
||||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (path, _, root) = self;
|
||||
let mut endpoint = IntoEndpoint::<RocksDb>::into_endpoint((path.as_ref(), root))?;
|
||||
endpoint.strict = true;
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,6 +91,9 @@ impl IntoEndpoint<File> for &str {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -72,13 +102,8 @@ impl IntoEndpoint<File> for &Path {
|
|||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let url = format!("file://{}", self.display());
|
||||
Ok(Endpoint {
|
||||
endpoint: Url::parse(&url).map_err(|_| Error::InvalidUrl(url))?,
|
||||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
})
|
||||
let path = self.display().to_string();
|
||||
IntoEndpoint::<File>::into_endpoint(path.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,12 +114,39 @@ where
|
|||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let url = format!("file://{}", self.0.as_ref().display());
|
||||
Ok(Endpoint {
|
||||
endpoint: Url::parse(&url).map_err(|_| Error::InvalidUrl(url))?,
|
||||
strict: true,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
})
|
||||
let (path, _) = self;
|
||||
let mut endpoint = IntoEndpoint::<RocksDb>::into_endpoint(path.as_ref())?;
|
||||
endpoint.strict = true;
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoEndpoint<File> for (T, Root<'_>)
|
||||
where
|
||||
T: AsRef<Path>,
|
||||
{
|
||||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (path, root) = self;
|
||||
let mut endpoint = IntoEndpoint::<File>::into_endpoint(path.as_ref())?;
|
||||
endpoint.auth = Level::Kv;
|
||||
endpoint.username = root.username.to_owned();
|
||||
endpoint.password = root.password.to_owned();
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoEndpoint<File> for (T, Strict, Root<'_>)
|
||||
where
|
||||
T: AsRef<Path>,
|
||||
{
|
||||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (path, _, root) = self;
|
||||
let mut endpoint = IntoEndpoint::<File>::into_endpoint((path.as_ref(), root))?;
|
||||
endpoint.strict = true;
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ use crate::api::opt::Endpoint;
|
|||
use crate::api::opt::IntoEndpoint;
|
||||
use crate::api::opt::Strict;
|
||||
use crate::api::Result;
|
||||
use crate::dbs::Level;
|
||||
use crate::opt::auth::Root;
|
||||
use std::path::Path;
|
||||
use url::Url;
|
||||
|
||||
|
@ -18,6 +20,9 @@ impl IntoEndpoint<SpeeDb> for &str {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -26,13 +31,8 @@ impl IntoEndpoint<SpeeDb> for &Path {
|
|||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let url = format!("speedb://{}", self.display());
|
||||
Ok(Endpoint {
|
||||
endpoint: Url::parse(&url).map_err(|_| Error::InvalidUrl(url))?,
|
||||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
})
|
||||
let path = self.display().to_string();
|
||||
IntoEndpoint::<SpeeDb>::into_endpoint(path.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,12 +43,39 @@ where
|
|||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let url = format!("speedb://{}", self.0.as_ref().display());
|
||||
Ok(Endpoint {
|
||||
endpoint: Url::parse(&url).map_err(|_| Error::InvalidUrl(url))?,
|
||||
strict: true,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
})
|
||||
let (path, _) = self;
|
||||
let mut endpoint = IntoEndpoint::<SpeeDb>::into_endpoint(path.as_ref())?;
|
||||
endpoint.strict = true;
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoEndpoint<SpeeDb> for (T, Root<'_>)
|
||||
where
|
||||
T: AsRef<Path>,
|
||||
{
|
||||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (path, root) = self;
|
||||
let mut endpoint = IntoEndpoint::<SpeeDb>::into_endpoint(path.as_ref())?;
|
||||
endpoint.auth = Level::Kv;
|
||||
endpoint.username = root.username.to_owned();
|
||||
endpoint.password = root.password.to_owned();
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoEndpoint<SpeeDb> for (T, Strict, Root<'_>)
|
||||
where
|
||||
T: AsRef<Path>,
|
||||
{
|
||||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (path, _, root) = self;
|
||||
let mut endpoint = IntoEndpoint::<SpeeDb>::into_endpoint((path.as_ref(), root))?;
|
||||
endpoint.strict = true;
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
use crate::api::engine::local::Db;
|
||||
use crate::api::engine::local::TiKv;
|
||||
use crate::api::err::Error;
|
||||
use crate::api::opt::auth::Root;
|
||||
use crate::api::opt::Endpoint;
|
||||
use crate::api::opt::IntoEndpoint;
|
||||
use crate::api::opt::Strict;
|
||||
use crate::api::Result;
|
||||
use crate::dbs::Level;
|
||||
use std::fmt::Display;
|
||||
use std::net::SocketAddr;
|
||||
use url::Url;
|
||||
|
||||
|
@ -18,6 +21,9 @@ impl IntoEndpoint<TiKv> for &str {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +38,9 @@ impl IntoEndpoint<TiKv> for SocketAddr {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -46,19 +55,53 @@ impl IntoEndpoint<TiKv> for String {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoEndpoint<TiKv> for (T, Strict)
|
||||
where
|
||||
T: IntoEndpoint<TiKv>,
|
||||
T: IntoEndpoint<TiKv> + Display,
|
||||
{
|
||||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let mut address = self.0.into_endpoint()?;
|
||||
address.strict = true;
|
||||
Ok(address)
|
||||
let (address, _) = self;
|
||||
let mut endpoint = address.into_endpoint()?;
|
||||
endpoint.strict = true;
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoEndpoint<TiKv> for (T, Root<'_>)
|
||||
where
|
||||
T: IntoEndpoint<TiKv> + Display,
|
||||
{
|
||||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (address, root) = self;
|
||||
let mut endpoint = address.into_endpoint()?;
|
||||
endpoint.auth = Level::Kv;
|
||||
endpoint.username = root.username.to_owned();
|
||||
endpoint.password = root.password.to_owned();
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IntoEndpoint<TiKv> for (T, Strict, Root<'_>)
|
||||
where
|
||||
T: IntoEndpoint<TiKv> + Display,
|
||||
{
|
||||
type Client = Db;
|
||||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (address, _, root) = self;
|
||||
let mut endpoint = (address, root).into_endpoint()?;
|
||||
endpoint.strict = true;
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::api::opt::IntoEndpoint;
|
|||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
use crate::api::opt::Tls;
|
||||
use crate::api::Result;
|
||||
use crate::dbs::Level;
|
||||
use std::net::SocketAddr;
|
||||
use url::Url;
|
||||
|
||||
|
@ -20,6 +21,9 @@ impl IntoEndpoint<Ws> for &str {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +38,9 @@ impl IntoEndpoint<Ws> for SocketAddr {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +55,9 @@ impl IntoEndpoint<Ws> for String {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +72,9 @@ impl IntoEndpoint<Wss> for &str {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -76,6 +89,9 @@ impl IntoEndpoint<Wss> for SocketAddr {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -90,6 +106,9 @@ impl IntoEndpoint<Wss> for String {
|
|||
strict: false,
|
||||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
tls_config: None,
|
||||
auth: Level::No,
|
||||
username: String::new(),
|
||||
password: String::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -104,9 +123,9 @@ where
|
|||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (address, config) = self;
|
||||
let mut address = address.into_endpoint()?;
|
||||
address.tls_config = Some(Tls::Native(config));
|
||||
Ok(address)
|
||||
let mut endpoint = address.into_endpoint()?;
|
||||
endpoint.tls_config = Some(Tls::Native(config));
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,8 +139,8 @@ where
|
|||
|
||||
fn into_endpoint(self) -> Result<Endpoint> {
|
||||
let (address, config) = self;
|
||||
let mut address = address.into_endpoint()?;
|
||||
address.tls_config = Some(Tls::Rust(config));
|
||||
Ok(address)
|
||||
let mut endpoint = address.into_endpoint()?;
|
||||
endpoint.tls_config = Some(Tls::Rust(config));
|
||||
Ok(endpoint)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,3 +20,6 @@ pub const ID_CHARS: [char; 36] = [
|
|||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
|
||||
'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
|
||||
];
|
||||
|
||||
/// The publicly visible name of the server
|
||||
pub const SERVER_NAME: &str = "SurrealDB";
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::value::Value;
|
||||
use base64_lib::DecodeError as Base64Error;
|
||||
use bincode::Error as BincodeError;
|
||||
use bung::encode::Error as SerdeError;
|
||||
use fst::Error as FstError;
|
||||
use jsonwebtoken::errors::Error as JWTError;
|
||||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
use std::string::FromUtf8Error;
|
||||
|
@ -75,6 +77,10 @@ pub enum Error {
|
|||
#[error("The SQL query was not parsed fully")]
|
||||
QueryRemaining,
|
||||
|
||||
/// There was an error with authentication
|
||||
#[error("There was a problem with authentication")]
|
||||
InvalidAuth,
|
||||
|
||||
/// There was an error with the SQL query
|
||||
#[error("Parse error on line {line} at character {char} when parsing '{sql}'")]
|
||||
InvalidQuery {
|
||||
|
@ -459,6 +465,18 @@ impl From<Error> for String {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Base64Error> for Error {
|
||||
fn from(_: Base64Error) -> Error {
|
||||
Error::InvalidAuth
|
||||
}
|
||||
}
|
||||
|
||||
impl From<JWTError> for Error {
|
||||
fn from(_: JWTError) -> Error {
|
||||
Error::InvalidAuth
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "kv-mem")]
|
||||
impl From<echodb::err::Error> for Error {
|
||||
fn from(e: echodb::err::Error) -> Error {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use base64::alphabet::STANDARD;
|
||||
use base64::engine::general_purpose::GeneralPurpose;
|
||||
use base64::engine::general_purpose::GeneralPurposeConfig;
|
||||
use base64::engine::DecodePaddingMode;
|
||||
use base64_lib::alphabet::STANDARD;
|
||||
use base64_lib::engine::general_purpose::GeneralPurpose;
|
||||
use base64_lib::engine::general_purpose::GeneralPurposeConfig;
|
||||
use base64_lib::engine::DecodePaddingMode;
|
||||
|
||||
pub use base64::Engine;
|
||||
pub use base64_lib::Engine;
|
||||
|
||||
pub const BASE64: GeneralPurpose = GeneralPurpose::new(&STANDARD, CONFIG);
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
use crate::dbs::Auth;
|
||||
use crate::dbs::Session;
|
||||
use crate::err::Error;
|
||||
use std::sync::Arc;
|
||||
use surrealdb::dbs::Auth;
|
||||
use surrealdb::dbs::Session;
|
||||
|
||||
pub async fn clear(session: &mut Session) -> Result<(), Error> {
|
||||
pub fn clear(session: &mut Session) -> Result<(), Error> {
|
||||
session.au = Arc::new(Auth::No);
|
||||
session.tk = None;
|
||||
session.sc = None;
|
10
lib/src/iam/mod.rs
Normal file
10
lib/src/iam/mod.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
pub mod base;
|
||||
pub mod clear;
|
||||
pub mod parse;
|
||||
pub mod signin;
|
||||
pub mod signup;
|
||||
pub mod token;
|
||||
pub mod verify;
|
||||
|
||||
pub const LOG: &str = "surrealdb::iam";
|
||||
pub const TOKEN: &str = "Bearer ";
|
|
@ -1,8 +1,8 @@
|
|||
use crate::err::Error;
|
||||
use crate::iam::base::{Engine, BASE64};
|
||||
use crate::sql::json;
|
||||
use crate::sql::Value;
|
||||
use std::str;
|
||||
use surrealdb::sql::json;
|
||||
use surrealdb::sql::Value;
|
||||
|
||||
pub fn parse(value: &str) -> Result<Value, Error> {
|
||||
// Extract the middle part of the token
|
|
@ -1,19 +1,25 @@
|
|||
use crate::cli::CF;
|
||||
use crate::cnf::SERVER_NAME;
|
||||
use crate::dbs::DB;
|
||||
use crate::dbs::Auth;
|
||||
use crate::dbs::Session;
|
||||
use crate::err::Error;
|
||||
use crate::iam::token::{Claims, HEADER};
|
||||
use crate::kvs::Datastore;
|
||||
use crate::opt::auth::Root;
|
||||
use crate::sql::Object;
|
||||
use crate::sql::Value;
|
||||
use argon2::password_hash::{PasswordHash, PasswordVerifier};
|
||||
use argon2::Argon2;
|
||||
use chrono::{Duration, Utc};
|
||||
use jsonwebtoken::{encode, EncodingKey};
|
||||
use std::sync::Arc;
|
||||
use surrealdb::dbs::Auth;
|
||||
use surrealdb::dbs::Session;
|
||||
use surrealdb::sql::Object;
|
||||
use surrealdb::sql::Value;
|
||||
|
||||
pub async fn signin(session: &mut Session, vars: Object) -> Result<Option<String>, Error> {
|
||||
pub async fn signin(
|
||||
kvs: &Datastore,
|
||||
configured_root: &Option<Root<'_>>,
|
||||
strict: bool,
|
||||
session: &mut Session,
|
||||
vars: Object,
|
||||
) -> Result<Option<String>, Error> {
|
||||
// Parse the specified variables
|
||||
let ns = vars.get("NS").or_else(|| vars.get("ns"));
|
||||
let db = vars.get("DB").or_else(|| vars.get("db"));
|
||||
|
@ -26,7 +32,7 @@ pub async fn signin(session: &mut Session, vars: Object) -> Result<Option<String
|
|||
let db = db.to_raw_string();
|
||||
let sc = sc.to_raw_string();
|
||||
// Attempt to signin to specified scope
|
||||
super::signin::sc(session, ns, db, sc, vars).await
|
||||
super::signin::sc(kvs, strict, session, ns, db, sc, vars).await
|
||||
}
|
||||
(Some(ns), Some(db), None) => {
|
||||
// Get the provided user and pass
|
||||
|
@ -42,7 +48,7 @@ pub async fn signin(session: &mut Session, vars: Object) -> Result<Option<String
|
|||
let user = user.to_raw_string();
|
||||
let pass = pass.to_raw_string();
|
||||
// Attempt to signin to database
|
||||
super::signin::db(session, ns, db, user, pass).await
|
||||
super::signin::db(kvs, session, ns, db, user, pass).await
|
||||
}
|
||||
// There is no username or password
|
||||
_ => Err(Error::InvalidAuth),
|
||||
|
@ -61,7 +67,7 @@ pub async fn signin(session: &mut Session, vars: Object) -> Result<Option<String
|
|||
let user = user.to_raw_string();
|
||||
let pass = pass.to_raw_string();
|
||||
// Attempt to signin to namespace
|
||||
super::signin::ns(session, ns, user, pass).await
|
||||
super::signin::ns(kvs, session, ns, user, pass).await
|
||||
}
|
||||
// There is no username or password
|
||||
_ => Err(Error::InvalidAuth),
|
||||
|
@ -79,7 +85,8 @@ pub async fn signin(session: &mut Session, vars: Object) -> Result<Option<String
|
|||
let user = user.to_raw_string();
|
||||
let pass = pass.to_raw_string();
|
||||
// Attempt to signin to namespace
|
||||
super::signin::su(session, user, pass).await
|
||||
super::signin::su(configured_root, session, user, pass)?;
|
||||
Ok(None)
|
||||
}
|
||||
// There is no username or password
|
||||
_ => Err(Error::InvalidAuth),
|
||||
|
@ -90,16 +97,14 @@ pub async fn signin(session: &mut Session, vars: Object) -> Result<Option<String
|
|||
}
|
||||
|
||||
pub async fn sc(
|
||||
kvs: &Datastore,
|
||||
strict: bool,
|
||||
session: &mut Session,
|
||||
ns: String,
|
||||
db: String,
|
||||
sc: String,
|
||||
vars: Object,
|
||||
) -> Result<Option<String>, Error> {
|
||||
// Get a database reference
|
||||
let kvs = DB.get().unwrap();
|
||||
// Get local copy of options
|
||||
let opt = CF.get().unwrap();
|
||||
// Create a new readonly transaction
|
||||
let mut tx = kvs.transaction(false, false).await?;
|
||||
// Check if the supplied NS Login exists
|
||||
|
@ -113,7 +118,7 @@ pub async fn sc(
|
|||
// Setup the query session
|
||||
let sess = Session::for_db(&ns, &db);
|
||||
// Compute the value with the params
|
||||
match kvs.compute(val, &sess, vars, opt.strict).await {
|
||||
match kvs.compute(val, &sess, vars, strict).await {
|
||||
// The signin value succeeded
|
||||
Ok(val) => match val.record() {
|
||||
// There is a record returned
|
||||
|
@ -174,14 +179,13 @@ pub async fn sc(
|
|||
}
|
||||
|
||||
pub async fn db(
|
||||
kvs: &Datastore,
|
||||
session: &mut Session,
|
||||
ns: String,
|
||||
db: String,
|
||||
user: String,
|
||||
pass: String,
|
||||
) -> Result<Option<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
|
||||
|
@ -230,13 +234,12 @@ pub async fn db(
|
|||
}
|
||||
|
||||
pub async fn ns(
|
||||
kvs: &Datastore,
|
||||
session: &mut Session,
|
||||
ns: String,
|
||||
user: String,
|
||||
pass: String,
|
||||
) -> Result<Option<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
|
||||
|
@ -282,18 +285,17 @@ pub async fn ns(
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn su(
|
||||
pub fn su(
|
||||
configured_root: &Option<Root<'_>>,
|
||||
session: &mut Session,
|
||||
user: String,
|
||||
pass: String,
|
||||
) -> Result<Option<String>, Error> {
|
||||
// Get the config options
|
||||
let opts = CF.get().unwrap();
|
||||
) -> Result<(), Error> {
|
||||
// Attempt to verify the root user
|
||||
if let Some(root) = &opts.pass {
|
||||
if user == opts.user && &pass == root {
|
||||
if let Some(root) = configured_root {
|
||||
if user == root.username && pass == root.password {
|
||||
session.au = Arc::new(Auth::Kv);
|
||||
return Ok(None);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
// The specified user login does not exist
|
|
@ -1,17 +1,21 @@
|
|||
use crate::cli::CF;
|
||||
use crate::cnf::SERVER_NAME;
|
||||
use crate::dbs::DB;
|
||||
use crate::dbs::Auth;
|
||||
use crate::dbs::Session;
|
||||
use crate::err::Error;
|
||||
use crate::iam::token::{Claims, HEADER};
|
||||
use crate::kvs::Datastore;
|
||||
use crate::sql::Object;
|
||||
use crate::sql::Value;
|
||||
use chrono::{Duration, Utc};
|
||||
use jsonwebtoken::{encode, EncodingKey};
|
||||
use std::sync::Arc;
|
||||
use surrealdb::dbs::Auth;
|
||||
use surrealdb::dbs::Session;
|
||||
use surrealdb::sql::Object;
|
||||
use surrealdb::sql::Value;
|
||||
|
||||
pub async fn signup(session: &mut Session, vars: Object) -> Result<Option<String>, Error> {
|
||||
pub async fn signup(
|
||||
kvs: &Datastore,
|
||||
strict: bool,
|
||||
session: &mut Session,
|
||||
vars: Object,
|
||||
) -> Result<Option<String>, Error> {
|
||||
// Parse the specified variables
|
||||
let ns = vars.get("NS").or_else(|| vars.get("ns"));
|
||||
let db = vars.get("DB").or_else(|| vars.get("db"));
|
||||
|
@ -24,23 +28,21 @@ pub async fn signup(session: &mut Session, vars: Object) -> Result<Option<String
|
|||
let db = db.to_raw_string();
|
||||
let sc = sc.to_raw_string();
|
||||
// Attempt to signin to specified scope
|
||||
super::signup::sc(session, ns, db, sc, vars).await
|
||||
super::signup::sc(kvs, strict, session, ns, db, sc, vars).await
|
||||
}
|
||||
_ => Err(Error::InvalidAuth),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn sc(
|
||||
kvs: &Datastore,
|
||||
strict: bool,
|
||||
session: &mut Session,
|
||||
ns: String,
|
||||
db: String,
|
||||
sc: String,
|
||||
vars: Object,
|
||||
) -> Result<Option<String>, Error> {
|
||||
// Get a database reference
|
||||
let kvs = DB.get().unwrap();
|
||||
// Get local copy of options
|
||||
let opt = CF.get().unwrap();
|
||||
// Create a new readonly transaction
|
||||
let mut tx = kvs.transaction(false, false).await?;
|
||||
// Check if the supplied NS Login exists
|
||||
|
@ -54,7 +56,7 @@ pub async fn sc(
|
|||
// Setup the query session
|
||||
let sess = Session::for_db(&ns, &db);
|
||||
// Compute the value with the params
|
||||
match kvs.compute(val, &sess, vars, opt.strict).await {
|
||||
match kvs.compute(val, &sess, vars, strict).await {
|
||||
// The signin value succeeded
|
||||
Ok(val) => match val.record() {
|
||||
// There is a record returned
|
|
@ -1,8 +1,8 @@
|
|||
use crate::sql::Object;
|
||||
use crate::sql::Value;
|
||||
use jsonwebtoken::{Algorithm, Header};
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use surrealdb::sql::Object;
|
||||
use surrealdb::sql::Value;
|
||||
|
||||
pub static HEADER: Lazy<Header> = Lazy::new(|| Header::new(Algorithm::HS512));
|
||||
|
270
lib/src/iam/verify.rs
Normal file
270
lib/src/iam/verify.rs
Normal file
|
@ -0,0 +1,270 @@
|
|||
use crate::dbs::Auth;
|
||||
use crate::dbs::Session;
|
||||
use crate::err::Error;
|
||||
use crate::iam::token::Claims;
|
||||
use crate::iam::LOG;
|
||||
use crate::iam::TOKEN;
|
||||
use crate::kvs::Datastore;
|
||||
use crate::sql::Algorithm;
|
||||
use crate::sql::Value;
|
||||
use chrono::Utc;
|
||||
use jsonwebtoken::{decode, DecodingKey, Validation};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::Arc;
|
||||
|
||||
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 = false;
|
||||
validation.validate_exp = false;
|
||||
validation
|
||||
});
|
||||
|
||||
pub async fn token(kvs: &Datastore, session: &mut Session, auth: String) -> Result<(), Error> {
|
||||
// Log the authentication type
|
||||
trace!(target: LOG, "Attempting token authentication");
|
||||
// Retrieve just the auth data
|
||||
let auth = auth.trim_start_matches(TOKEN).trim();
|
||||
// Decode the token without verifying
|
||||
let token = decode::<Claims>(auth, &KEY, &DUD)?;
|
||||
// Parse the token and catch any errors
|
||||
let value = super::parse::parse(auth)?;
|
||||
// Check if the auth token can be used
|
||||
if let Some(nbf) = token.claims.nbf {
|
||||
if nbf > Utc::now().timestamp() {
|
||||
trace!(target: LOG, "The 'nbf' field in the authentication token was invalid");
|
||||
return Err(Error::InvalidAuth);
|
||||
}
|
||||
}
|
||||
// Check if the auth token has expired
|
||||
if let Some(exp) = token.claims.exp {
|
||||
if exp < Utc::now().timestamp() {
|
||||
trace!(target: LOG, "The 'exp' field in the authentication token was invalid");
|
||||
return Err(Error::InvalidAuth);
|
||||
}
|
||||
}
|
||||
// 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,
|
||||
..
|
||||
} => {
|
||||
// Log the decoded authentication claims
|
||||
trace!(target: LOG, "Authenticating to scope `{}` with token `{}`", sc, tk);
|
||||
// Create a new readonly transaction
|
||||
let mut tx = kvs.transaction(false, false).await?;
|
||||
// Parse the record id
|
||||
let id = match id {
|
||||
Some(id) => crate::sql::thing(&id)?.into(),
|
||||
None => Value::None,
|
||||
};
|
||||
// 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)?;
|
||||
// Log the success
|
||||
debug!(target: LOG, "Authenticated to scope `{}` with token `{}`", sc, tk);
|
||||
// Set the session
|
||||
session.sd = Some(id);
|
||||
session.tk = Some(value);
|
||||
session.ns = Some(ns.to_owned());
|
||||
session.db = Some(db.to_owned());
|
||||
session.sc = Some(sc.to_owned());
|
||||
session.au = Arc::new(Auth::Sc(ns, db, sc));
|
||||
Ok(())
|
||||
}
|
||||
// Check if this is scope authentication
|
||||
Claims {
|
||||
ns: Some(ns),
|
||||
db: Some(db),
|
||||
sc: Some(sc),
|
||||
id: Some(id),
|
||||
..
|
||||
} => {
|
||||
// Log the decoded authentication claims
|
||||
trace!(target: LOG, "Authenticating to scope `{}`", sc);
|
||||
// Create a new readonly transaction
|
||||
let mut tx = kvs.transaction(false, false).await?;
|
||||
// Parse the record id
|
||||
let id = crate::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)?;
|
||||
// Log the success
|
||||
debug!(target: LOG, "Authenticated to scope `{}`", sc);
|
||||
// Set the session
|
||||
session.tk = Some(value);
|
||||
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));
|
||||
Ok(())
|
||||
}
|
||||
// Check if this is database token authentication
|
||||
Claims {
|
||||
ns: Some(ns),
|
||||
db: Some(db),
|
||||
tk: Some(tk),
|
||||
..
|
||||
} => {
|
||||
// Log the decoded authentication claims
|
||||
trace!(target: LOG, "Authenticating to database `{}` with token `{}`", db, 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)?;
|
||||
// Log the success
|
||||
debug!(target: LOG, "Authenticated to database `{}` with token `{}`", db, tk);
|
||||
// Set the session
|
||||
session.tk = Some(value);
|
||||
session.ns = Some(ns.to_owned());
|
||||
session.db = Some(db.to_owned());
|
||||
session.au = Arc::new(Auth::Db(ns, db));
|
||||
Ok(())
|
||||
}
|
||||
// Check if this is database authentication
|
||||
Claims {
|
||||
ns: Some(ns),
|
||||
db: Some(db),
|
||||
id: Some(id),
|
||||
..
|
||||
} => {
|
||||
// Log the decoded authentication claims
|
||||
trace!(target: LOG, "Authenticating to database `{}` with login `{}`", db, 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)?;
|
||||
// Log the success
|
||||
debug!(target: LOG, "Authenticated to database `{}` with login `{}`", db, id);
|
||||
// Set the session
|
||||
session.tk = Some(value);
|
||||
session.ns = Some(ns.to_owned());
|
||||
session.db = Some(db.to_owned());
|
||||
session.au = Arc::new(Auth::Db(ns, db));
|
||||
Ok(())
|
||||
}
|
||||
// Check if this is namespace token authentication
|
||||
Claims {
|
||||
ns: Some(ns),
|
||||
tk: Some(tk),
|
||||
..
|
||||
} => {
|
||||
// Log the decoded authentication claims
|
||||
trace!(target: LOG, "Authenticating to namespace `{}` with token `{}`", ns, 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)?;
|
||||
// Log the success
|
||||
trace!(target: LOG, "Authenticated to namespace `{}` with token `{}`", ns, tk);
|
||||
// Set the session
|
||||
session.tk = Some(value);
|
||||
session.ns = Some(ns.to_owned());
|
||||
session.au = Arc::new(Auth::Ns(ns));
|
||||
Ok(())
|
||||
}
|
||||
// Check if this is namespace authentication
|
||||
Claims {
|
||||
ns: Some(ns),
|
||||
id: Some(id),
|
||||
..
|
||||
} => {
|
||||
// Log the decoded authentication claims
|
||||
trace!(target: LOG, "Authenticating to namespace `{}` with login `{}`", ns, 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)?;
|
||||
// Log the success
|
||||
trace!(target: LOG, "Authenticated to namespace `{}` with login `{}`", ns, id);
|
||||
// Set the session
|
||||
session.tk = Some(value);
|
||||
session.ns = Some(ns.to_owned());
|
||||
session.au = Arc::new(Auth::Ns(ns));
|
||||
Ok(())
|
||||
}
|
||||
// There was an auth error
|
||||
_ => Err(Error::InvalidAuth),
|
||||
}
|
||||
}
|
|
@ -107,7 +107,6 @@ extern crate log;
|
|||
mod mac;
|
||||
|
||||
mod api;
|
||||
mod cnf;
|
||||
mod ctx;
|
||||
mod doc;
|
||||
mod exe;
|
||||
|
@ -116,12 +115,24 @@ mod key;
|
|||
|
||||
pub mod sql;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod cnf;
|
||||
#[doc(hidden)]
|
||||
pub mod dbs;
|
||||
#[doc(hidden)]
|
||||
pub mod env;
|
||||
#[doc(hidden)]
|
||||
pub mod err;
|
||||
#[cfg(any(
|
||||
feature = "kv-mem",
|
||||
feature = "kv-tikv",
|
||||
feature = "kv-rocksdb",
|
||||
feature = "kv-speedb",
|
||||
feature = "kv-fdb",
|
||||
feature = "kv-indxdb",
|
||||
))]
|
||||
#[doc(hidden)]
|
||||
pub mod iam;
|
||||
#[doc(hidden)]
|
||||
pub mod idx;
|
||||
#[doc(hidden)]
|
||||
|
|
|
@ -72,7 +72,6 @@ mod api_integration {
|
|||
}
|
||||
|
||||
include!("api/mod.rs");
|
||||
include!("api/auth.rs");
|
||||
}
|
||||
|
||||
#[cfg(feature = "protocol-http")]
|
||||
|
@ -93,7 +92,6 @@ mod api_integration {
|
|||
}
|
||||
|
||||
include!("api/mod.rs");
|
||||
include!("api/auth.rs");
|
||||
include!("api/backup.rs");
|
||||
}
|
||||
|
||||
|
@ -105,7 +103,13 @@ mod api_integration {
|
|||
use surrealdb::engine::local::Mem;
|
||||
|
||||
async fn new_db() -> Surreal<Db> {
|
||||
Surreal::new::<Mem>(()).await.unwrap()
|
||||
let root = Root {
|
||||
username: ROOT_USER,
|
||||
password: ROOT_PASS,
|
||||
};
|
||||
let db = Surreal::new::<Mem>(root).await.unwrap();
|
||||
db.signin(root).await.unwrap();
|
||||
db
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
@ -113,6 +117,43 @@ mod api_integration {
|
|||
any::connect("memory").await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn signin_first_not_necessary() {
|
||||
let db = Surreal::new::<Mem>(()).await.unwrap();
|
||||
db.use_ns("namespace").use_db("database").await.unwrap();
|
||||
let Some(record): Option<RecordId> = db.create(("item", "foo")).await.unwrap() else {
|
||||
panic!("record not found");
|
||||
};
|
||||
assert_eq!(record.id.to_string(), "item:foo");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn cant_sign_into_default_root_account() {
|
||||
let db = Surreal::new::<Mem>(()).await.unwrap();
|
||||
let Error::Db(DbError::InvalidAuth) = db.signin(Root {
|
||||
username: ROOT_USER,
|
||||
password: ROOT_PASS,
|
||||
})
|
||||
.await
|
||||
.unwrap_err() else {
|
||||
panic!("unexpected successful login");
|
||||
};
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn credentials_activate_authentication() {
|
||||
let db = Surreal::new::<Mem>(Root {
|
||||
username: ROOT_USER,
|
||||
password: ROOT_PASS,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
db.use_ns("namespace").use_db("database").await.unwrap();
|
||||
let Error::Db(DbError::QueryPermissions) = db.create(Resource::from("item:foo")).await.unwrap_err() else {
|
||||
panic!("record not found");
|
||||
};
|
||||
}
|
||||
|
||||
include!("api/mod.rs");
|
||||
include!("api/backup.rs");
|
||||
}
|
||||
|
@ -125,7 +166,13 @@ mod api_integration {
|
|||
|
||||
async fn new_db() -> Surreal<Db> {
|
||||
let path = format!("/tmp/{}.db", Ulid::new());
|
||||
Surreal::new::<File>(path.as_str()).await.unwrap()
|
||||
let root = Root {
|
||||
username: ROOT_USER,
|
||||
password: ROOT_PASS,
|
||||
};
|
||||
let db = Surreal::new::<File>((path.as_str(), root)).await.unwrap();
|
||||
db.signin(root).await.unwrap();
|
||||
db
|
||||
}
|
||||
|
||||
include!("api/mod.rs");
|
||||
|
@ -169,7 +216,13 @@ mod api_integration {
|
|||
use surrealdb::engine::local::TiKv;
|
||||
|
||||
async fn new_db() -> Surreal<Db> {
|
||||
Surreal::new::<TiKv>("127.0.0.1:2379").await.unwrap()
|
||||
let root = Root {
|
||||
username: ROOT_USER,
|
||||
password: ROOT_PASS,
|
||||
};
|
||||
let db = Surreal::new::<TiKv>(("127.0.0.1:2379", root)).await.unwrap();
|
||||
db.signin(root).await.unwrap();
|
||||
db
|
||||
}
|
||||
|
||||
include!("api/mod.rs");
|
||||
|
@ -183,7 +236,13 @@ mod api_integration {
|
|||
use surrealdb::engine::local::FDb;
|
||||
|
||||
async fn new_db() -> Surreal<Db> {
|
||||
Surreal::new::<FDb>("/tmp/fdb.cluster").await.unwrap()
|
||||
let root = Root {
|
||||
username: ROOT_USER,
|
||||
password: ROOT_PASS,
|
||||
};
|
||||
let db = Surreal::new::<FDb>(("/tmp/fdb.cluster", root)).await.unwrap();
|
||||
db.signin(root).await.unwrap();
|
||||
db
|
||||
}
|
||||
|
||||
include!("api/mod.rs");
|
||||
|
@ -207,7 +266,6 @@ mod api_integration {
|
|||
}
|
||||
|
||||
include!("api/mod.rs");
|
||||
include!("api/auth.rs");
|
||||
include!("api/backup.rs");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,138 +0,0 @@
|
|||
// Auth tests
|
||||
// Supported by both HTTP and WS protocols
|
||||
|
||||
#[tokio::test]
|
||||
async fn invalidate() {
|
||||
let db = new_db().await;
|
||||
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||
db.invalidate().await.unwrap();
|
||||
let error = db.create::<Option<RecordId>>(("user", "john")).await.unwrap_err();
|
||||
assert!(error.to_string().contains("You don't have permission to perform this query type"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn signup_scope() {
|
||||
let db = new_db().await;
|
||||
let database = Ulid::new().to_string();
|
||||
db.use_ns(NS).use_db(&database).await.unwrap();
|
||||
let scope = Ulid::new().to_string();
|
||||
let sql = format!(
|
||||
"
|
||||
DEFINE SCOPE {scope} SESSION 1s
|
||||
SIGNUP ( CREATE user SET email = $email, pass = crypto::argon2::generate($pass) )
|
||||
SIGNIN ( SELECT * FROM user WHERE email = $email AND crypto::argon2::compare(pass, $pass) )
|
||||
"
|
||||
);
|
||||
let response = db.query(sql).await.unwrap();
|
||||
response.check().unwrap();
|
||||
db.signup(Scope {
|
||||
namespace: NS,
|
||||
database: &database,
|
||||
scope: &scope,
|
||||
params: AuthParams {
|
||||
email: "john.doe@example.com",
|
||||
pass: "password123",
|
||||
},
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn signin_ns() {
|
||||
let db = new_db().await;
|
||||
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||
let user = Ulid::new().to_string();
|
||||
let pass = "password123";
|
||||
let sql = format!("DEFINE LOGIN {user} ON NAMESPACE PASSWORD '{pass}'");
|
||||
let response = db.query(sql).await.unwrap();
|
||||
response.check().unwrap();
|
||||
db.signin(Namespace {
|
||||
namespace: NS,
|
||||
username: &user,
|
||||
password: pass,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn signin_db() {
|
||||
let db = new_db().await;
|
||||
let database = Ulid::new().to_string();
|
||||
db.use_ns(NS).use_db(&database).await.unwrap();
|
||||
let user = Ulid::new().to_string();
|
||||
let pass = "password123";
|
||||
let sql = format!("DEFINE LOGIN {user} ON DATABASE PASSWORD '{pass}'");
|
||||
let response = db.query(sql).await.unwrap();
|
||||
response.check().unwrap();
|
||||
db.signin(Database {
|
||||
namespace: NS,
|
||||
database: &database,
|
||||
username: &user,
|
||||
password: pass,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn signin_scope() {
|
||||
let db = new_db().await;
|
||||
let database = Ulid::new().to_string();
|
||||
db.use_ns(NS).use_db(&database).await.unwrap();
|
||||
let scope = Ulid::new().to_string();
|
||||
let email = format!("{scope}@example.com");
|
||||
let pass = "password123";
|
||||
let sql = format!(
|
||||
"
|
||||
DEFINE SCOPE {scope} SESSION 1s
|
||||
SIGNUP ( CREATE user SET email = $email, pass = crypto::argon2::generate($pass) )
|
||||
SIGNIN ( SELECT * FROM user WHERE email = $email AND crypto::argon2::compare(pass, $pass) )
|
||||
"
|
||||
);
|
||||
let response = db.query(sql).await.unwrap();
|
||||
response.check().unwrap();
|
||||
db.signup(Scope {
|
||||
namespace: NS,
|
||||
database: &database,
|
||||
scope: &scope,
|
||||
params: AuthParams {
|
||||
pass,
|
||||
email: &email,
|
||||
},
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
db.signin(Scope {
|
||||
namespace: NS,
|
||||
database: &database,
|
||||
scope: &scope,
|
||||
params: AuthParams {
|
||||
pass,
|
||||
email: &email,
|
||||
},
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn authenticate() {
|
||||
let db = new_db().await;
|
||||
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||
let user = Ulid::new().to_string();
|
||||
let pass = "password123";
|
||||
let sql = format!("DEFINE LOGIN {user} ON NAMESPACE PASSWORD '{pass}'");
|
||||
let response = db.query(sql).await.unwrap();
|
||||
response.check().unwrap();
|
||||
let token = db
|
||||
.signin(Namespace {
|
||||
namespace: NS,
|
||||
username: &user,
|
||||
password: pass,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
db.authenticate(token).await.unwrap();
|
||||
}
|
|
@ -29,6 +29,142 @@ async fn yuse() {
|
|||
db.create(Resource::from(item)).await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn invalidate() {
|
||||
let db = new_db().await;
|
||||
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||
db.invalidate().await.unwrap();
|
||||
let error = db.create::<Option<RecordId>>(("user", "john")).await.unwrap_err();
|
||||
assert!(error.to_string().contains("You don't have permission to perform this query type"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn signup_scope() {
|
||||
let db = new_db().await;
|
||||
let database = Ulid::new().to_string();
|
||||
db.use_ns(NS).use_db(&database).await.unwrap();
|
||||
let scope = Ulid::new().to_string();
|
||||
let sql = format!(
|
||||
"
|
||||
DEFINE SCOPE {scope} SESSION 1s
|
||||
SIGNUP ( CREATE user SET email = $email, pass = crypto::argon2::generate($pass) )
|
||||
SIGNIN ( SELECT * FROM user WHERE email = $email AND crypto::argon2::compare(pass, $pass) )
|
||||
"
|
||||
);
|
||||
let response = db.query(sql).await.unwrap();
|
||||
response.check().unwrap();
|
||||
db.signup(Scope {
|
||||
namespace: NS,
|
||||
database: &database,
|
||||
scope: &scope,
|
||||
params: AuthParams {
|
||||
email: "john.doe@example.com",
|
||||
pass: "password123",
|
||||
},
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn signin_ns() {
|
||||
let db = new_db().await;
|
||||
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||
let user = Ulid::new().to_string();
|
||||
let pass = "password123";
|
||||
let sql = format!("DEFINE LOGIN {user} ON NAMESPACE PASSWORD '{pass}'");
|
||||
let response = db.query(sql).await.unwrap();
|
||||
response.check().unwrap();
|
||||
db.signin(Namespace {
|
||||
namespace: NS,
|
||||
username: &user,
|
||||
password: pass,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn signin_db() {
|
||||
let db = new_db().await;
|
||||
let database = Ulid::new().to_string();
|
||||
db.use_ns(NS).use_db(&database).await.unwrap();
|
||||
let user = Ulid::new().to_string();
|
||||
let pass = "password123";
|
||||
let sql = format!("DEFINE LOGIN {user} ON DATABASE PASSWORD '{pass}'");
|
||||
let response = db.query(sql).await.unwrap();
|
||||
response.check().unwrap();
|
||||
db.signin(Database {
|
||||
namespace: NS,
|
||||
database: &database,
|
||||
username: &user,
|
||||
password: pass,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn signin_scope() {
|
||||
let db = new_db().await;
|
||||
let database = Ulid::new().to_string();
|
||||
db.use_ns(NS).use_db(&database).await.unwrap();
|
||||
let scope = Ulid::new().to_string();
|
||||
let email = format!("{scope}@example.com");
|
||||
let pass = "password123";
|
||||
let sql = format!(
|
||||
"
|
||||
DEFINE SCOPE {scope} SESSION 1s
|
||||
SIGNUP ( CREATE user SET email = $email, pass = crypto::argon2::generate($pass) )
|
||||
SIGNIN ( SELECT * FROM user WHERE email = $email AND crypto::argon2::compare(pass, $pass) )
|
||||
"
|
||||
);
|
||||
let response = db.query(sql).await.unwrap();
|
||||
response.check().unwrap();
|
||||
db.signup(Scope {
|
||||
namespace: NS,
|
||||
database: &database,
|
||||
scope: &scope,
|
||||
params: AuthParams {
|
||||
pass,
|
||||
email: &email,
|
||||
},
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
db.signin(Scope {
|
||||
namespace: NS,
|
||||
database: &database,
|
||||
scope: &scope,
|
||||
params: AuthParams {
|
||||
pass,
|
||||
email: &email,
|
||||
},
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn authenticate() {
|
||||
let db = new_db().await;
|
||||
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||
let user = Ulid::new().to_string();
|
||||
let pass = "password123";
|
||||
let sql = format!("DEFINE LOGIN {user} ON NAMESPACE PASSWORD '{pass}'");
|
||||
let response = db.query(sql).await.unwrap();
|
||||
response.check().unwrap();
|
||||
let token = db
|
||||
.signin(Namespace {
|
||||
namespace: NS,
|
||||
username: &user,
|
||||
password: pass,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
db.authenticate(token).await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn query() {
|
||||
let db = new_db().await;
|
||||
|
|
|
@ -5,9 +5,7 @@ use crate::cli::LOG;
|
|||
use crate::err::Error;
|
||||
use clap::Args;
|
||||
use surrealdb::engine::any::connect;
|
||||
use surrealdb::error::Api as ApiError;
|
||||
use surrealdb::opt::auth::Root;
|
||||
use surrealdb::Error as SurrealError;
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct ExportCommandArguments {
|
||||
|
@ -43,22 +41,14 @@ pub async fn init(
|
|||
// Initialize opentelemetry and logging
|
||||
crate::o11y::builder().with_log_level("error").init();
|
||||
|
||||
// Connect to the database engine
|
||||
let client = connect(endpoint).await?;
|
||||
// Sign in to the server if the specified database engine supports it
|
||||
let root = Root {
|
||||
username: &username,
|
||||
password: &password,
|
||||
};
|
||||
if let Err(error) = client.signin(root).await {
|
||||
match error {
|
||||
// Authentication not supported by this engine, we can safely continue
|
||||
SurrealError::Api(ApiError::AuthNotSupported) => {}
|
||||
error => {
|
||||
return Err(error.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Connect to the database engine
|
||||
let client = connect((endpoint, root)).await?;
|
||||
// Sign in to the server
|
||||
client.signin(root).await?;
|
||||
// Use the specified namespace / database
|
||||
client.use_ns(ns).use_db(db).await?;
|
||||
// Export the data from the database
|
||||
|
|
|
@ -5,9 +5,7 @@ use crate::cli::LOG;
|
|||
use crate::err::Error;
|
||||
use clap::Args;
|
||||
use surrealdb::engine::any::connect;
|
||||
use surrealdb::error::Api as ApiError;
|
||||
use surrealdb::opt::auth::Root;
|
||||
use surrealdb::Error as SurrealError;
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct ImportCommandArguments {
|
||||
|
@ -41,22 +39,14 @@ pub async fn init(
|
|||
// Initialize opentelemetry and logging
|
||||
crate::o11y::builder().with_log_level("error").init();
|
||||
|
||||
// Connect to the database engine
|
||||
let client = connect(endpoint).await?;
|
||||
// Sign in to the server if the specified database engine supports it
|
||||
let root = Root {
|
||||
username: &username,
|
||||
password: &password,
|
||||
};
|
||||
if let Err(error) = client.signin(root).await {
|
||||
match error {
|
||||
// Authentication not supported by this engine, we can safely continue
|
||||
SurrealError::Api(ApiError::AuthNotSupported) => {}
|
||||
error => {
|
||||
return Err(error.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Connect to the database engine
|
||||
let client = connect((endpoint, root)).await?;
|
||||
// Sign in to the server
|
||||
client.signin(root).await?;
|
||||
// Use the specified namespace / database
|
||||
client.use_ns(ns).use_db(db).await?;
|
||||
// Import the data into the database
|
||||
|
|
|
@ -9,10 +9,9 @@ use rustyline::{Completer, Editor, Helper, Highlighter, Hinter};
|
|||
use serde::Serialize;
|
||||
use serde_json::ser::PrettyFormatter;
|
||||
use surrealdb::engine::any::connect;
|
||||
use surrealdb::error::Api as ApiError;
|
||||
use surrealdb::opt::auth::Root;
|
||||
use surrealdb::sql::{self, Statement, Value};
|
||||
use surrealdb::{Error as SurrealError, Response};
|
||||
use surrealdb::Response;
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct SqlCommandArguments {
|
||||
|
@ -52,22 +51,14 @@ pub async fn init(
|
|||
// Initialize opentelemetry and logging
|
||||
crate::o11y::builder().with_log_level("warn").init();
|
||||
|
||||
// Connect to the database engine
|
||||
let client = connect(endpoint).await?;
|
||||
// Sign in to the server if the specified database engine supports it
|
||||
let root = Root {
|
||||
username: &username,
|
||||
password: &password,
|
||||
};
|
||||
if let Err(error) = client.signin(root).await {
|
||||
match error {
|
||||
// Authentication not supported by this engine, we can safely continue
|
||||
SurrealError::Api(ApiError::AuthNotSupported) => {}
|
||||
error => {
|
||||
return Err(error.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Connect to the database engine
|
||||
let client = connect((endpoint, root)).await?;
|
||||
// Sign in to the server
|
||||
client.signin(root).await?;
|
||||
// Create a new terminal REPL
|
||||
let mut rl = Editor::new().unwrap();
|
||||
// Set custom input validation
|
||||
|
|
|
@ -16,9 +16,6 @@ Y88b d88P Y88b 888 888 888 Y8b. 888 888 888 888 .d88P 888 d88P
|
|||
/// The publicly visible name of the server
|
||||
pub const PKG_NAME: &str = "surrealdb";
|
||||
|
||||
/// The publicly visible name of the server
|
||||
pub const SERVER_NAME: &str = "SurrealDB";
|
||||
|
||||
/// The publicly visible user-agent of the command-line tool
|
||||
pub const SERVER_AGENT: &str = concat!("SurrealDB ", env!("CARGO_PKG_VERSION"));
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use base64::DecodeError as Base64Error;
|
||||
use jsonwebtoken::errors::Error as JWTError;
|
||||
use reqwest::Error as ReqwestError;
|
||||
use serde::Serialize;
|
||||
use serde_cbor::error::Error as CborError;
|
||||
|
@ -72,12 +71,6 @@ impl From<Utf8Error> for Error {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<JWTError> for Error {
|
||||
fn from(_: JWTError) -> Error {
|
||||
Error::InvalidAuth
|
||||
}
|
||||
}
|
||||
|
||||
impl From<surrealdb::error::Db> for Error {
|
||||
fn from(error: surrealdb::error::Db) -> Error {
|
||||
Error::Db(error.into())
|
||||
|
|
|
@ -1,18 +1,10 @@
|
|||
pub mod base;
|
||||
pub mod clear;
|
||||
pub mod parse;
|
||||
pub mod signin;
|
||||
pub mod signup;
|
||||
pub mod token;
|
||||
pub mod verify;
|
||||
|
||||
use crate::cli::CF;
|
||||
use crate::err::Error;
|
||||
use surrealdb::iam::LOG;
|
||||
|
||||
pub const BASIC: &str = "Basic ";
|
||||
pub const TOKEN: &str = "Bearer ";
|
||||
|
||||
const LOG: &str = "surrealdb::iam";
|
||||
|
||||
pub async fn init() -> Result<(), Error> {
|
||||
// Get local copy of options
|
||||
|
|
|
@ -1,88 +1,14 @@
|
|||
use crate::cli::CF;
|
||||
use crate::dbs::DB;
|
||||
use crate::err::Error;
|
||||
use crate::iam::base::{Engine, BASE64};
|
||||
use crate::iam::token::Claims;
|
||||
use crate::iam::BASIC;
|
||||
use crate::iam::LOG;
|
||||
use crate::iam::TOKEN;
|
||||
use argon2::password_hash::{PasswordHash, PasswordVerifier};
|
||||
use argon2::Argon2;
|
||||
use chrono::Utc;
|
||||
use jsonwebtoken::{decode, DecodingKey, Validation};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::Arc;
|
||||
use surrealdb::dbs::Auth;
|
||||
use surrealdb::dbs::Session;
|
||||
use surrealdb::sql::Algorithm;
|
||||
use surrealdb::sql::Value;
|
||||
|
||||
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 = false;
|
||||
validation.validate_exp = false;
|
||||
validation
|
||||
});
|
||||
use surrealdb::iam::base::{Engine, BASE64};
|
||||
use surrealdb::iam::LOG;
|
||||
|
||||
pub async fn basic(session: &mut Session, auth: String) -> Result<(), Error> {
|
||||
// Log the authentication type
|
||||
|
@ -149,195 +75,3 @@ pub async fn basic(session: &mut Session, auth: String) -> Result<(), Error> {
|
|||
// There was an auth error
|
||||
Err(Error::InvalidAuth)
|
||||
}
|
||||
|
||||
pub async fn token(session: &mut Session, auth: String) -> Result<(), Error> {
|
||||
// Log the authentication type
|
||||
trace!(target: LOG, "Attempting token authentication");
|
||||
// Retrieve just the auth data
|
||||
let auth = auth.trim_start_matches(TOKEN).trim();
|
||||
// Get a database reference
|
||||
let kvs = DB.get().unwrap();
|
||||
// Decode the token without verifying
|
||||
let token = decode::<Claims>(auth, &KEY, &DUD)?;
|
||||
// Parse the token and catch any errors
|
||||
let value = super::parse::parse(auth)?;
|
||||
// Check if the auth token can be used
|
||||
if let Some(nbf) = token.claims.nbf {
|
||||
if nbf > Utc::now().timestamp() {
|
||||
trace!(target: LOG, "The 'nbf' field in the authentication token was invalid");
|
||||
return Err(Error::InvalidAuth);
|
||||
}
|
||||
}
|
||||
// Check if the auth token has expired
|
||||
if let Some(exp) = token.claims.exp {
|
||||
if exp < Utc::now().timestamp() {
|
||||
trace!(target: LOG, "The 'exp' field in the authentication token was invalid");
|
||||
return Err(Error::InvalidAuth);
|
||||
}
|
||||
}
|
||||
// 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,
|
||||
..
|
||||
} => {
|
||||
// Log the decoded authentication claims
|
||||
trace!(target: LOG, "Authenticating to scope `{}` with token `{}`", sc, tk);
|
||||
// Create a new readonly transaction
|
||||
let mut tx = kvs.transaction(false, false).await?;
|
||||
// Parse the record id
|
||||
let id = match id {
|
||||
Some(id) => surrealdb::sql::thing(&id)?.into(),
|
||||
None => Value::None,
|
||||
};
|
||||
// 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)?;
|
||||
// Log the success
|
||||
debug!(target: LOG, "Authenticated to scope `{}` with token `{}`", sc, tk);
|
||||
// Set the session
|
||||
session.sd = Some(id);
|
||||
session.tk = Some(value);
|
||||
session.ns = Some(ns.to_owned());
|
||||
session.db = Some(db.to_owned());
|
||||
session.sc = Some(sc.to_owned());
|
||||
session.au = Arc::new(Auth::Sc(ns, db, sc));
|
||||
Ok(())
|
||||
}
|
||||
// Check if this is scope authentication
|
||||
Claims {
|
||||
ns: Some(ns),
|
||||
db: Some(db),
|
||||
sc: Some(sc),
|
||||
id: Some(id),
|
||||
..
|
||||
} => {
|
||||
// Log the decoded authentication claims
|
||||
trace!(target: LOG, "Authenticating to scope `{}`", sc);
|
||||
// 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)?;
|
||||
// Log the success
|
||||
debug!(target: LOG, "Authenticated to scope `{}`", sc);
|
||||
// Set the session
|
||||
session.tk = Some(value);
|
||||
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));
|
||||
Ok(())
|
||||
}
|
||||
// Check if this is database token authentication
|
||||
Claims {
|
||||
ns: Some(ns),
|
||||
db: Some(db),
|
||||
tk: Some(tk),
|
||||
..
|
||||
} => {
|
||||
// Log the decoded authentication claims
|
||||
trace!(target: LOG, "Authenticating to database `{}` with token `{}`", db, 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)?;
|
||||
// Log the success
|
||||
debug!(target: LOG, "Authenticated to database `{}` with token `{}`", db, tk);
|
||||
// Set the session
|
||||
session.tk = Some(value);
|
||||
session.ns = Some(ns.to_owned());
|
||||
session.db = Some(db.to_owned());
|
||||
session.au = Arc::new(Auth::Db(ns, db));
|
||||
Ok(())
|
||||
}
|
||||
// Check if this is database authentication
|
||||
Claims {
|
||||
ns: Some(ns),
|
||||
db: Some(db),
|
||||
id: Some(id),
|
||||
..
|
||||
} => {
|
||||
// Log the decoded authentication claims
|
||||
trace!(target: LOG, "Authenticating to database `{}` with login `{}`", db, 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)?;
|
||||
// Log the success
|
||||
debug!(target: LOG, "Authenticated to database `{}` with login `{}`", db, id);
|
||||
// Set the session
|
||||
session.tk = Some(value);
|
||||
session.ns = Some(ns.to_owned());
|
||||
session.db = Some(db.to_owned());
|
||||
session.au = Arc::new(Auth::Db(ns, db));
|
||||
Ok(())
|
||||
}
|
||||
// Check if this is namespace token authentication
|
||||
Claims {
|
||||
ns: Some(ns),
|
||||
tk: Some(tk),
|
||||
..
|
||||
} => {
|
||||
// Log the decoded authentication claims
|
||||
trace!(target: LOG, "Authenticating to namespace `{}` with token `{}`", ns, 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)?;
|
||||
// Log the success
|
||||
trace!(target: LOG, "Authenticated to namespace `{}` with token `{}`", ns, tk);
|
||||
// Set the session
|
||||
session.tk = Some(value);
|
||||
session.ns = Some(ns.to_owned());
|
||||
session.au = Arc::new(Auth::Ns(ns));
|
||||
Ok(())
|
||||
}
|
||||
// Check if this is namespace authentication
|
||||
Claims {
|
||||
ns: Some(ns),
|
||||
id: Some(id),
|
||||
..
|
||||
} => {
|
||||
// Log the decoded authentication claims
|
||||
trace!(target: LOG, "Authenticating to namespace `{}` with login `{}`", ns, 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)?;
|
||||
// Log the success
|
||||
trace!(target: LOG, "Authenticated to namespace `{}` with login `{}`", ns, id);
|
||||
// Set the session
|
||||
session.tk = Some(value);
|
||||
session.ns = Some(ns.to_owned());
|
||||
session.au = Arc::new(Auth::Ns(ns));
|
||||
Ok(())
|
||||
}
|
||||
// There was an auth error
|
||||
_ => Err(Error::InvalidAuth),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::cnf::PKG_NAME;
|
||||
use crate::cnf::PKG_VERSION;
|
||||
use crate::cnf::SERVER_NAME;
|
||||
use surrealdb::cnf::SERVER_NAME;
|
||||
|
||||
const ID: &str = "ID";
|
||||
const NS: &str = "NS";
|
||||
|
|
|
@ -20,7 +20,9 @@ use std::collections::HashMap;
|
|||
use std::sync::Arc;
|
||||
use surrealdb::channel;
|
||||
use surrealdb::channel::Sender;
|
||||
use surrealdb::dbs::Response;
|
||||
use surrealdb::dbs::Session;
|
||||
use surrealdb::opt::auth::Root;
|
||||
use surrealdb::sql::Array;
|
||||
use surrealdb::sql::Object;
|
||||
use surrealdb::sql::Strand;
|
||||
|
@ -381,7 +383,9 @@ impl Rpc {
|
|||
|
||||
#[instrument(skip_all, name = "rpc signup", fields(websocket=self.uuid.to_string()))]
|
||||
async fn signup(&mut self, vars: Object) -> Result<Value, Error> {
|
||||
crate::iam::signup::signup(&mut self.session, vars)
|
||||
let kvs = DB.get().unwrap();
|
||||
let opts = CF.get().unwrap();
|
||||
surrealdb::iam::signup::signup(kvs, opts.strict, &mut self.session, vars)
|
||||
.await
|
||||
.map(Into::into)
|
||||
.map_err(Into::into)
|
||||
|
@ -389,20 +393,27 @@ impl Rpc {
|
|||
|
||||
#[instrument(skip_all, name = "rpc signin", fields(websocket=self.uuid.to_string()))]
|
||||
async fn signin(&mut self, vars: Object) -> Result<Value, Error> {
|
||||
crate::iam::signin::signin(&mut self.session, vars)
|
||||
let kvs = DB.get().unwrap();
|
||||
let opts = CF.get().unwrap();
|
||||
let root = opts.pass.as_ref().map(|pass| Root {
|
||||
username: &opts.user,
|
||||
password: pass,
|
||||
});
|
||||
surrealdb::iam::signin::signin(kvs, &root, opts.strict, &mut self.session, vars)
|
||||
.await
|
||||
.map(Into::into)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
#[instrument(skip_all, name = "rpc invalidate", fields(websocket=self.uuid.to_string()))]
|
||||
async fn invalidate(&mut self) -> Result<Value, Error> {
|
||||
crate::iam::clear::clear(&mut self.session).await?;
|
||||
surrealdb::iam::clear::clear(&mut self.session)?;
|
||||
Ok(Value::None)
|
||||
}
|
||||
|
||||
#[instrument(skip_all, name = "rpc auth", fields(websocket=self.uuid.to_string()))]
|
||||
async fn authenticate(&mut self, token: Strand) -> Result<Value, Error> {
|
||||
crate::iam::verify::token(&mut self.session, token.0).await?;
|
||||
let kvs = DB.get().unwrap();
|
||||
surrealdb::iam::verify::token(kvs, &mut self.session, token.0).await?;
|
||||
Ok(Value::None)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
use crate::dbs::DB;
|
||||
use crate::err::Error;
|
||||
use crate::iam::verify::{basic, token};
|
||||
use crate::iam::verify::basic;
|
||||
use crate::iam::BASIC;
|
||||
use crate::iam::TOKEN;
|
||||
use crate::net::client_ip;
|
||||
use std::net::SocketAddr;
|
||||
use surrealdb::dbs::Session;
|
||||
use surrealdb::iam::verify::token;
|
||||
use surrealdb::iam::TOKEN;
|
||||
use warp::Filter;
|
||||
|
||||
pub fn build() -> impl Filter<Extract = (Session,), Error = warp::Rejection> + Clone {
|
||||
|
@ -33,6 +36,7 @@ async fn process(
|
|||
ns: Option<String>,
|
||||
db: Option<String>,
|
||||
) -> Result<Session, warp::Rejection> {
|
||||
let kvs = DB.get().unwrap();
|
||||
// Create session
|
||||
#[rustfmt::skip]
|
||||
let mut session = Session { ip, or, id, ns, db, ..Default::default() };
|
||||
|
@ -41,7 +45,9 @@ async fn process(
|
|||
// Basic authentication data was supplied
|
||||
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(&mut session, auth).await,
|
||||
Some(auth) if auth.starts_with(TOKEN) => {
|
||||
token(kvs, &mut session, auth).await.map_err(Error::from)
|
||||
}
|
||||
// Wrong authentication data was supplied
|
||||
Some(_) => Err(Error::InvalidAuth),
|
||||
// No authentication data was supplied
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
use crate::dbs::DB;
|
||||
use crate::err::Error;
|
||||
use crate::net::input::bytes_to_utf8;
|
||||
use crate::net::output;
|
||||
use crate::net::session;
|
||||
use crate::net::CF;
|
||||
use bytes::Bytes;
|
||||
use serde::Serialize;
|
||||
use surrealdb::dbs::Session;
|
||||
use surrealdb::opt::auth::Root;
|
||||
use surrealdb::sql::Value;
|
||||
use warp::Filter;
|
||||
|
||||
|
@ -51,30 +54,43 @@ async fn handler(
|
|||
body: Bytes,
|
||||
mut session: Session,
|
||||
) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
// Get a database reference
|
||||
let kvs = DB.get().unwrap();
|
||||
// Get the config options
|
||||
let opts = CF.get().unwrap();
|
||||
// Convert the HTTP body into text
|
||||
let data = bytes_to_utf8(&body)?;
|
||||
// Parse the provided data as JSON
|
||||
match surrealdb::sql::json(data) {
|
||||
// The provided value was an object
|
||||
Ok(Value::Object(vars)) => match crate::iam::signin::signin(&mut session, vars).await {
|
||||
// Authentication was successful
|
||||
Ok(v) => match output.as_deref() {
|
||||
// Simple serialization
|
||||
Some("application/json") => Ok(output::json(&Success::new(v))),
|
||||
Some("application/cbor") => Ok(output::cbor(&Success::new(v))),
|
||||
Some("application/pack") => Ok(output::pack(&Success::new(v))),
|
||||
// Internal serialization
|
||||
Some("application/bung") => Ok(output::full(&Success::new(v))),
|
||||
// Text serialization
|
||||
Some("text/plain") => Ok(output::text(v.unwrap_or_default())),
|
||||
// Return nothing
|
||||
None => Ok(output::none()),
|
||||
// An incorrect content-type was requested
|
||||
_ => Err(warp::reject::custom(Error::InvalidType)),
|
||||
},
|
||||
// There was an error with authentication
|
||||
Err(e) => Err(warp::reject::custom(e)),
|
||||
},
|
||||
Ok(Value::Object(vars)) => {
|
||||
let root = opts.pass.as_ref().map(|pass| Root {
|
||||
username: &opts.user,
|
||||
password: pass,
|
||||
});
|
||||
match surrealdb::iam::signin::signin(kvs, &root, opts.strict, &mut session, vars)
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
{
|
||||
// Authentication was successful
|
||||
Ok(v) => match output.as_deref() {
|
||||
// Simple serialization
|
||||
Some("application/json") => Ok(output::json(&Success::new(v))),
|
||||
Some("application/cbor") => Ok(output::cbor(&Success::new(v))),
|
||||
Some("application/pack") => Ok(output::pack(&Success::new(v))),
|
||||
// Internal serialization
|
||||
Some("application/bung") => Ok(output::full(&Success::new(v))),
|
||||
// Text serialization
|
||||
Some("text/plain") => Ok(output::text(v.unwrap_or_default())),
|
||||
// Return nothing
|
||||
None => Ok(output::none()),
|
||||
// An incorrect content-type was requested
|
||||
_ => Err(warp::reject::custom(Error::InvalidType)),
|
||||
},
|
||||
// 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)),
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use crate::dbs::DB;
|
||||
use crate::err::Error;
|
||||
use crate::net::input::bytes_to_utf8;
|
||||
use crate::net::output;
|
||||
use crate::net::session;
|
||||
use crate::net::CF;
|
||||
use bytes::Bytes;
|
||||
use serde::Serialize;
|
||||
use surrealdb::dbs::Session;
|
||||
|
@ -51,30 +53,39 @@ async fn handler(
|
|||
body: Bytes,
|
||||
mut session: Session,
|
||||
) -> Result<impl warp::Reply, warp::Rejection> {
|
||||
// Get a database reference
|
||||
let kvs = DB.get().unwrap();
|
||||
// Get the config options
|
||||
let opts = CF.get().unwrap();
|
||||
// Convert the HTTP body into text
|
||||
let data = bytes_to_utf8(&body)?;
|
||||
// Parse the provided data as JSON
|
||||
match surrealdb::sql::json(data) {
|
||||
// The provided value was an object
|
||||
Ok(Value::Object(vars)) => match crate::iam::signup::signup(&mut session, vars).await {
|
||||
// Authentication was successful
|
||||
Ok(v) => match output.as_deref() {
|
||||
// Simple serialization
|
||||
Some("application/json") => Ok(output::json(&Success::new(v))),
|
||||
Some("application/cbor") => Ok(output::cbor(&Success::new(v))),
|
||||
Some("application/pack") => Ok(output::pack(&Success::new(v))),
|
||||
// Internal serialization
|
||||
Some("application/bung") => Ok(output::full(&Success::new(v))),
|
||||
// Text serialization
|
||||
Some("text/plain") => Ok(output::text(v.unwrap_or_default())),
|
||||
// Return nothing
|
||||
None => Ok(output::none()),
|
||||
// An incorrect content-type was requested
|
||||
_ => Err(warp::reject::custom(Error::InvalidType)),
|
||||
},
|
||||
// There was an error with authentication
|
||||
Err(e) => Err(warp::reject::custom(e)),
|
||||
},
|
||||
Ok(Value::Object(vars)) => {
|
||||
match surrealdb::iam::signup::signup(kvs, opts.strict, &mut session, vars)
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
{
|
||||
// Authentication was successful
|
||||
Ok(v) => match output.as_deref() {
|
||||
// Simple serialization
|
||||
Some("application/json") => Ok(output::json(&Success::new(v))),
|
||||
Some("application/cbor") => Ok(output::cbor(&Success::new(v))),
|
||||
Some("application/pack") => Ok(output::pack(&Success::new(v))),
|
||||
// Internal serialization
|
||||
Some("application/bung") => Ok(output::full(&Success::new(v))),
|
||||
// Text serialization
|
||||
Some("text/plain") => Ok(output::text(v.unwrap_or_default())),
|
||||
// Return nothing
|
||||
None => Ok(output::none()),
|
||||
// An incorrect content-type was requested
|
||||
_ => Err(warp::reject::custom(Error::InvalidType)),
|
||||
},
|
||||
// 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)),
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue