Require explicit authentication level (#4089)

This commit is contained in:
Gerard Guillemas Martos 2024-05-27 10:15:15 +02:00 committed by GitHub
parent 3ccadb0740
commit bf702b0d67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 92 additions and 962 deletions

View file

@ -1,4 +1,4 @@
use super::verify::{verify_creds_legacy, verify_db_creds, verify_ns_creds, verify_root_creds}; use super::verify::{verify_db_creds, verify_ns_creds, verify_root_creds};
use super::{Actor, Level}; use super::{Actor, Level};
use crate::cnf::{INSECURE_FORWARD_RECORD_ACCESS_ERRORS, SERVER_NAME}; use crate::cnf::{INSECURE_FORWARD_RECORD_ACCESS_ERRORS, SERVER_NAME};
use crate::dbs::Session; use crate::dbs::Session;
@ -210,16 +210,7 @@ pub async fn db_user(
user: String, user: String,
pass: String, pass: String,
) -> Result<Option<String>, Error> { ) -> Result<Option<String>, Error> {
let verify_creds = if kvs.is_auth_level_enabled() { match verify_db_creds(kvs, &ns, &db, &user, &pass).await {
verify_db_creds(kvs, &ns, &db, &user, &pass).await
} else {
// TODO(gguillemas): Remove this condition once the legacy authentication is deprecated in v2.0.0
match verify_creds_legacy(kvs, Some(&ns), Some(&db), &user, &pass).await {
Ok((_, u)) => Ok(u),
Err(e) => Err(e),
}
};
match verify_creds {
Ok(u) => { Ok(u) => {
// Create the authentication key // Create the authentication key
let key = EncodingKey::from_secret(u.code.as_ref()); let key = EncodingKey::from_secret(u.code.as_ref());
@ -265,16 +256,7 @@ pub async fn ns_user(
user: String, user: String,
pass: String, pass: String,
) -> Result<Option<String>, Error> { ) -> Result<Option<String>, Error> {
let verify_creds = if kvs.is_auth_level_enabled() { match verify_ns_creds(kvs, &ns, &user, &pass).await {
verify_ns_creds(kvs, &ns, &user, &pass).await
} else {
// TODO(gguillemas): Remove this condition once the legacy authentication is deprecated in v2.0.0
match verify_creds_legacy(kvs, Some(&ns), None, &user, &pass).await {
Ok((_, u)) => Ok(u),
Err(e) => Err(e),
}
};
match verify_creds {
Ok(u) => { Ok(u) => {
// Create the authentication key // Create the authentication key
let key = EncodingKey::from_secret(u.code.as_ref()); let key = EncodingKey::from_secret(u.code.as_ref());
@ -318,16 +300,7 @@ pub async fn root_user(
user: String, user: String,
pass: String, pass: String,
) -> Result<Option<String>, Error> { ) -> Result<Option<String>, Error> {
let verify_creds = if kvs.is_auth_level_enabled() { match verify_root_creds(kvs, &user, &pass).await {
verify_root_creds(kvs, &user, &pass).await
} else {
// TODO(gguillemas): Remove this condition once the legacy authentication is deprecated in v2.0.0
match verify_creds_legacy(kvs, None, None, &user, &pass).await {
Ok((_, u)) => Ok(u),
Err(e) => Err(e),
}
};
match verify_creds {
Ok(u) => { Ok(u) => {
// Create the authentication key // Create the authentication key
let key = EncodingKey::from_secret(u.code.as_ref()); let key = EncodingKey::from_secret(u.code.as_ref());

View file

@ -131,43 +131,6 @@ pub async fn basic(
} }
} }
// TODO(gguillemas): Remove this method once the legacy authentication is deprecated in v2.0.0
pub async fn basic_legacy(
kvs: &Datastore,
session: &mut Session,
user: &str,
pass: &str,
) -> Result<(), Error> {
// Log the authentication type
trace!("Attempting legacy basic authentication");
match verify_creds_legacy(kvs, session.ns.as_ref(), session.db.as_ref(), user, pass).await {
Ok((au, _)) if au.is_root() => {
debug!("Authenticated as root user '{}'", user);
// TODO(gguillemas): Enforce expiration once session lifetime can be customized.
session.exp = None;
session.au = Arc::new(au);
Ok(())
}
Ok((au, _)) if au.is_ns() => {
debug!("Authenticated as namespace user '{}'", user);
// TODO(gguillemas): Enforce expiration once session lifetime can be customized.
session.exp = None;
session.au = Arc::new(au);
Ok(())
}
Ok((au, _)) if au.is_db() => {
debug!("Authenticated as database user '{}'", user);
// TODO(gguillemas): Enforce expiration once session lifetime can be customized.
session.exp = None;
session.au = Arc::new(au);
Ok(())
}
Ok(_) => Err(Error::InvalidAuth),
Err(e) => Err(e),
}
}
pub async fn token(kvs: &Datastore, session: &mut Session, token: &str) -> Result<(), Error> { pub async fn token(kvs: &Datastore, session: &mut Session, token: &str) -> Result<(), Error> {
// Log the authentication type // Log the authentication type
trace!("Attempting token authentication"); trace!("Attempting token authentication");
@ -520,48 +483,6 @@ fn verify_pass(pass: &str, hash: &str) -> Result<(), Error> {
} }
} }
// TODO(gguillemas): Remove this method once the legacy authentication is deprecated in v2.0.0
pub async fn verify_creds_legacy(
ds: &Datastore,
ns: Option<&String>,
db: Option<&String>,
user: &str,
pass: &str,
) -> Result<(Auth, DefineUserStatement), Error> {
if user.is_empty() || pass.is_empty() {
return Err(Error::InvalidAuth);
}
// Try to authenticate as a ROOT user
match verify_root_creds(ds, user, pass).await {
Ok(u) => Ok(((&u, Level::Root).into(), u)),
Err(_) => {
// Try to authenticate as a NS user
match ns {
Some(ns) => {
match verify_ns_creds(ds, ns, user, pass).await {
Ok(u) => Ok(((&u, Level::Namespace(ns.to_owned())).into(), u)),
Err(_) => {
// Try to authenticate as a DB user
match db {
Some(db) => match verify_db_creds(ds, ns, db, user, pass).await {
Ok(u) => Ok((
(&u, Level::Database(ns.to_owned(), db.to_owned())).into(),
u,
)),
Err(_) => Err(Error::InvalidAuth),
},
None => Err(Error::InvalidAuth),
}
}
}
}
None => Err(Error::InvalidAuth),
}
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -71,9 +71,6 @@ pub struct Datastore {
strict: bool, strict: bool,
// Whether authentication is enabled on this datastore. // Whether authentication is enabled on this datastore.
auth_enabled: bool, auth_enabled: bool,
// Whether authentication level is enabled on this datastore.
// TODO(gguillemas): Remove this field once the legacy authentication is deprecated in v2.0.0
auth_level_enabled: bool,
// The maximum duration timeout for running multiple statements in a query // The maximum duration timeout for running multiple statements in a query
query_timeout: Option<Duration>, query_timeout: Option<Duration>,
// The maximum duration timeout for running multiple statements in a transaction // The maximum duration timeout for running multiple statements in a transaction
@ -365,8 +362,6 @@ impl Datastore {
inner, inner,
strict: false, strict: false,
auth_enabled: false, auth_enabled: false,
// TODO(gguillemas): Remove this field once the legacy authentication is deprecated in v2.0.0
auth_level_enabled: false,
query_timeout: None, query_timeout: None,
transaction_timeout: None, transaction_timeout: None,
notification_channel: None, notification_channel: None,
@ -427,13 +422,6 @@ impl Datastore {
self self
} }
/// Set whether authentication levels are enabled for this Datastore
/// TODO(gguillemas): Remove this method once the legacy authentication is deprecated in v2.0.0
pub fn with_auth_level_enabled(mut self, enabled: bool) -> Self {
self.auth_level_enabled = enabled;
self
}
/// Set specific capabilities for this Datastore /// Set specific capabilities for this Datastore
pub fn with_capabilities(mut self, caps: Capabilities) -> Self { pub fn with_capabilities(mut self, caps: Capabilities) -> Self {
self.capabilities = caps; self.capabilities = caps;
@ -469,12 +457,6 @@ impl Datastore {
self.auth_enabled self.auth_enabled
} }
/// Is authentication level enabled for this Datastore?
/// TODO(gguillemas): Remove this method once the legacy authentication is deprecated in v2.0.0
pub fn is_auth_level_enabled(&self) -> bool {
self.auth_level_enabled
}
/// Does the datastore allow connections to a network target? /// Does the datastore allow connections to a network target?
#[cfg(feature = "jwks")] #[cfg(feature = "jwks")]
pub(crate) fn allows_network_target(&self, net_target: &NetTarget) -> bool { pub(crate) fn allows_network_target(&self, net_target: &NetTarget) -> bool {

View file

@ -28,8 +28,8 @@ use crate::api::Surreal;
use crate::dbs::Status; use crate::dbs::Status;
use crate::headers::AUTH_DB; use crate::headers::AUTH_DB;
use crate::headers::AUTH_NS; use crate::headers::AUTH_NS;
use crate::headers::DB_LEGACY; use crate::headers::DB;
use crate::headers::NS_LEGACY; use crate::headers::NS;
use crate::method::Stats; use crate::method::Stats;
use crate::opt::IntoEndpoint; use crate::opt::IntoEndpoint;
use crate::sql::from_value; use crate::sql::from_value;
@ -369,7 +369,7 @@ async fn router(
let ns = match ns { let ns = match ns {
Some(ns) => match HeaderValue::try_from(&ns) { Some(ns) => match HeaderValue::try_from(&ns) {
Ok(ns) => { Ok(ns) => {
request = request.header(&NS_LEGACY, &ns); request = request.header(&NS, &ns);
Some(ns) Some(ns)
} }
Err(_) => { Err(_) => {
@ -381,7 +381,7 @@ async fn router(
let db = match db { let db = match db {
Some(db) => match HeaderValue::try_from(&db) { Some(db) => match HeaderValue::try_from(&db) {
Ok(db) => { Ok(db) => {
request = request.header(&DB_LEGACY, &db); request = request.header(&DB, &db);
Some(db) Some(db)
} }
Err(_) => { Err(_) => {
@ -393,10 +393,10 @@ async fn router(
request = request.auth(auth).body("RETURN true"); request = request.auth(auth).body("RETURN true");
take(true, request).await?; take(true, request).await?;
if let Some(ns) = ns { if let Some(ns) = ns {
headers.insert(&NS_LEGACY, ns); headers.insert(&NS, ns);
} }
if let Some(db) = db { if let Some(db) = db {
headers.insert(&DB_LEGACY, db); headers.insert(&DB, db);
} }
Ok(DbResponse::Other(Value::None)) Ok(DbResponse::Other(Value::None))
} }

View file

@ -3,12 +3,8 @@
use reqwest::header::HeaderName; use reqwest::header::HeaderName;
pub static ID: HeaderName = HeaderName::from_static("surreal-id"); pub static ID: HeaderName = HeaderName::from_static("surreal-id");
pub static ID_LEGACY: HeaderName = HeaderName::from_static("id");
pub static NS: HeaderName = HeaderName::from_static("surreal-ns"); pub static NS: HeaderName = HeaderName::from_static("surreal-ns");
pub static NS_LEGACY: HeaderName = HeaderName::from_static("ns");
pub static DB: HeaderName = HeaderName::from_static("surreal-db"); pub static DB: HeaderName = HeaderName::from_static("surreal-db");
pub static DB_LEGACY: HeaderName = HeaderName::from_static("db");
pub static AUTH_NS: HeaderName = HeaderName::from_static("surreal-auth-ns"); pub static AUTH_NS: HeaderName = HeaderName::from_static("surreal-auth-ns");
pub static AUTH_DB: HeaderName = HeaderName::from_static("surreal-auth-db"); pub static AUTH_DB: HeaderName = HeaderName::from_static("surreal-auth-db");
pub static VERSION: HeaderName = HeaderName::from_static("surreal-version"); pub static VERSION: HeaderName = HeaderName::from_static("surreal-version");
pub static VERSION_LEGACY: HeaderName = HeaderName::from_static("version");

View file

@ -23,11 +23,7 @@ pub(crate) struct AuthArguments {
requires = "username" requires = "username"
)] )]
pub(crate) password: Option<String>, pub(crate) password: Option<String>,
// TODO(gguillemas): Update this help message once the legacy authentication is deprecated in v2.0.0 #[arg(help = "Authentication level to use when connecting")]
// Explicit level authentication will be enabled by default after the deprecation
#[arg(
help = "Authentication level to use when connecting\nMust be enabled in the server and uses the values of '--namespace' and '--database'\n"
)]
#[arg(env = "SURREAL_AUTH_LEVEL", long = "auth-level", default_value = "root")] #[arg(env = "SURREAL_AUTH_LEVEL", long = "auth-level", default_value = "root")]
#[arg(value_parser = super::validator::parser::creds_level::CredentialsLevelParser::new())] #[arg(value_parser = super::validator::parser::creds_level::CredentialsLevelParser::new())]
pub(crate) auth_level: CredentialsLevel, pub(crate) auth_level: CredentialsLevel,

View file

@ -27,14 +27,6 @@ pub struct StartCommandDbsOptions {
#[arg(env = "SURREAL_UNAUTHENTICATED", long = "unauthenticated")] #[arg(env = "SURREAL_UNAUTHENTICATED", long = "unauthenticated")]
#[arg(default_value_t = false)] #[arg(default_value_t = false)]
unauthenticated: bool, unauthenticated: bool,
// TODO(gguillemas): Remove this argument once the legacy authentication is deprecated in v2.0.0
#[arg(
help = "Whether to enable explicit authentication level selection",
help_heading = "Authentication"
)]
#[arg(env = "SURREAL_AUTH_LEVEL_ENABLED", long = "auth-level-enabled")]
#[arg(default_value_t = false)]
auth_level_enabled: bool,
#[command(flatten)] #[command(flatten)]
#[command(next_help_heading = "Capabilities")] #[command(next_help_heading = "Capabilities")]
caps: DbsCapabilities, caps: DbsCapabilities,
@ -216,8 +208,6 @@ pub async fn init(
query_timeout, query_timeout,
transaction_timeout, transaction_timeout,
unauthenticated, unauthenticated,
// TODO(gguillemas): Remove this field once the legacy authentication is deprecated in v2.0.0
auth_level_enabled,
caps, caps,
temporary_directory, temporary_directory,
}: StartCommandDbsOptions, }: StartCommandDbsOptions,
@ -238,11 +228,6 @@ pub async fn init(
if unauthenticated { if unauthenticated {
warn!("❌🔒 IMPORTANT: Authentication is disabled. This is not recommended for production use. 🔒❌"); warn!("❌🔒 IMPORTANT: Authentication is disabled. This is not recommended for production use. 🔒❌");
} }
// Log whether authentication levels are enabled
// TODO(gguillemas): Remove this condition once the legacy authentication is deprecated in v2.0.0
if auth_level_enabled {
info!("Authentication levels are enabled");
}
let caps = caps.into(); let caps = caps.into();
debug!("Server capabilities: {caps}"); debug!("Server capabilities: {caps}");
@ -256,7 +241,6 @@ pub async fn init(
.with_query_timeout(query_timeout) .with_query_timeout(query_timeout)
.with_transaction_timeout(transaction_timeout) .with_transaction_timeout(transaction_timeout)
.with_auth_enabled(!unauthenticated) .with_auth_enabled(!unauthenticated)
.with_auth_level_enabled(auth_level_enabled)
.with_capabilities(caps); .with_capabilities(caps);
let mut dbs = match temporary_directory { let mut dbs = match temporary_directory {

View file

@ -11,7 +11,7 @@ use http::{request::Parts, StatusCode};
use hyper::{Request, Response}; use hyper::{Request, Response};
use surrealdb::{ use surrealdb::{
dbs::Session, dbs::Session,
iam::verify::{basic, basic_legacy, token}, iam::verify::{basic, token},
}; };
use tower_http::auth::AsyncAuthorizeRequest; use tower_http::auth::AsyncAuthorizeRequest;
@ -20,9 +20,8 @@ use crate::{dbs::DB, err::Error};
use super::{ use super::{
client_ip::ExtractClientIP, client_ip::ExtractClientIP,
headers::{ headers::{
parse_typed_header, SurrealAuthDatabase, SurrealAuthNamespace, SurrealDatabase, parse_typed_header, SurrealAuthDatabase, SurrealAuthNamespace, SurrealDatabase, SurrealId,
SurrealDatabaseLegacy, SurrealId, SurrealIdLegacy, SurrealNamespace, SurrealNamespace,
SurrealNamespaceLegacy,
}, },
AppState, AppState,
}; };
@ -88,34 +87,18 @@ async fn check_auth(parts: &mut Parts) -> Result<Session, Error> {
None None
}; };
// Extract the session id from the headers. If not found, fallback to the legacy header name. // Extract the session id from the headers.
let id = match parse_typed_header::<SurrealId>(parts.extract::<TypedHeader<SurrealId>>().await) let id = parse_typed_header::<SurrealId>(parts.extract::<TypedHeader<SurrealId>>().await)?;
{
Ok(None) => parse_typed_header::<SurrealIdLegacy>(
parts.extract::<TypedHeader<SurrealIdLegacy>>().await,
),
res => res,
}?;
// Extract the namespace from the headers. If not found, fallback to the legacy header name. // Extract the namespace from the headers.
let ns = match parse_typed_header::<SurrealNamespace>( let ns = parse_typed_header::<SurrealNamespace>(
parts.extract::<TypedHeader<SurrealNamespace>>().await, parts.extract::<TypedHeader<SurrealNamespace>>().await,
) { )?;
Ok(None) => parse_typed_header::<SurrealNamespaceLegacy>(
parts.extract::<TypedHeader<SurrealNamespaceLegacy>>().await,
),
res => res,
}?;
// Extract the database from the headers. If not found, fallback to the legacy header name. // Extract the database from the headers.
let db = match parse_typed_header::<SurrealDatabase>( let db = parse_typed_header::<SurrealDatabase>(
parts.extract::<TypedHeader<SurrealDatabase>>().await, parts.extract::<TypedHeader<SurrealDatabase>>().await,
) { )?;
Ok(None) => parse_typed_header::<SurrealDatabaseLegacy>(
parts.extract::<TypedHeader<SurrealDatabaseLegacy>>().await,
),
res => res,
}?;
// Extract the authentication namespace and database from the headers. // Extract the authentication namespace and database from the headers.
let auth_ns = parse_typed_header::<SurrealAuthNamespace>( let auth_ns = parse_typed_header::<SurrealAuthNamespace>(
@ -143,19 +126,15 @@ async fn check_auth(parts: &mut Parts) -> Result<Session, Error> {
// If Basic authentication data was supplied // If Basic authentication data was supplied
if let Ok(au) = parts.extract::<TypedHeader<Authorization<Basic>>>().await { if let Ok(au) = parts.extract::<TypedHeader<Authorization<Basic>>>().await {
if kvs.is_auth_level_enabled() { basic(
basic( kvs,
kvs, &mut session,
&mut session, au.username(),
au.username(), au.password(),
au.password(), auth_ns.as_deref(),
auth_ns.as_deref(), auth_db.as_deref(),
auth_db.as_deref(), )
) .await?;
.await?;
} else {
basic_legacy(kvs, &mut session, au.username(), au.password()).await?;
}
}; };
// If Token authentication data was supplied // If Token authentication data was supplied

View file

@ -50,53 +50,3 @@ impl From<&SurrealDatabase> for HeaderValue {
HeaderValue::from_str(value.0.as_str()).unwrap() HeaderValue::from_str(value.0.as_str()).unwrap()
} }
} }
//
// Legacy header
//
static DB_LEGACY_HEADER: HeaderName = HeaderName::from_static("db");
pub struct SurrealDatabaseLegacy(String);
impl Header for SurrealDatabaseLegacy {
fn name() -> &'static HeaderName {
&DB_LEGACY_HEADER
}
fn decode<'i, I>(values: &mut I) -> Result<Self, headers::Error>
where
I: Iterator<Item = &'i HeaderValue>,
{
let value = values.next().ok_or_else(headers::Error::invalid)?;
let value = value.to_str().map_err(|_| headers::Error::invalid())?.to_string();
Ok(SurrealDatabaseLegacy(value))
}
fn encode<E>(&self, values: &mut E)
where
E: Extend<HeaderValue>,
{
values.extend(std::iter::once(self.into()));
}
}
impl std::ops::Deref for SurrealDatabaseLegacy {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<SurrealDatabaseLegacy> for HeaderValue {
fn from(value: SurrealDatabaseLegacy) -> Self {
HeaderValue::from(&value)
}
}
impl From<&SurrealDatabaseLegacy> for HeaderValue {
fn from(value: &SurrealDatabaseLegacy) -> Self {
HeaderValue::from_str(value.0.as_str()).unwrap()
}
}

View file

@ -50,52 +50,3 @@ impl From<&SurrealId> for HeaderValue {
HeaderValue::from_str(value.0.as_str()).unwrap() HeaderValue::from_str(value.0.as_str()).unwrap()
} }
} }
//
// Legacy header
//
static ID_LEGACY_HEADER: HeaderName = HeaderName::from_static("id");
pub struct SurrealIdLegacy(String);
impl Header for SurrealIdLegacy {
fn name() -> &'static HeaderName {
&ID_LEGACY_HEADER
}
fn decode<'i, I>(values: &mut I) -> Result<Self, headers::Error>
where
I: Iterator<Item = &'i HeaderValue>,
{
let value = values.next().ok_or_else(headers::Error::invalid)?;
let value = value.to_str().map_err(|_| headers::Error::invalid())?.to_string();
Ok(SurrealIdLegacy(value))
}
fn encode<E>(&self, values: &mut E)
where
E: Extend<HeaderValue>,
{
values.extend(std::iter::once(self.into()));
}
}
impl std::ops::Deref for SurrealIdLegacy {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<SurrealIdLegacy> for HeaderValue {
fn from(value: SurrealIdLegacy) -> Self {
HeaderValue::from(&value)
}
}
impl From<&SurrealIdLegacy> for HeaderValue {
fn from(value: &SurrealIdLegacy) -> Self {
HeaderValue::from_str(value.0.as_str()).unwrap()
}
}

View file

@ -23,9 +23,9 @@ pub use accept::Accept;
pub use auth_db::SurrealAuthDatabase; pub use auth_db::SurrealAuthDatabase;
pub use auth_ns::SurrealAuthNamespace; pub use auth_ns::SurrealAuthNamespace;
pub use content_type::ContentType; pub use content_type::ContentType;
pub use db::{SurrealDatabase, SurrealDatabaseLegacy}; pub use db::SurrealDatabase;
pub use id::{SurrealId, SurrealIdLegacy}; pub use id::SurrealId;
pub use ns::{SurrealNamespace, SurrealNamespaceLegacy}; pub use ns::SurrealNamespace;
pub fn add_version_header() -> SetResponseHeaderLayer<HeaderValue> { pub fn add_version_header() -> SetResponseHeaderLayer<HeaderValue> {
let val = format!("{PKG_NAME}-{}", *PKG_VERSION); let val = format!("{PKG_NAME}-{}", *PKG_VERSION);

View file

@ -50,53 +50,3 @@ impl From<&SurrealNamespace> for HeaderValue {
HeaderValue::from_str(value.0.as_str()).unwrap() HeaderValue::from_str(value.0.as_str()).unwrap()
} }
} }
//
// Legacy header
//
static NS_LEGACY_HEADER: HeaderName = HeaderName::from_static("ns");
pub struct SurrealNamespaceLegacy(String);
impl Header for SurrealNamespaceLegacy {
fn name() -> &'static HeaderName {
&NS_LEGACY_HEADER
}
fn decode<'i, I>(values: &mut I) -> Result<Self, headers::Error>
where
I: Iterator<Item = &'i HeaderValue>,
{
let value = values.next().ok_or_else(headers::Error::invalid)?;
let value = value.to_str().map_err(|_| headers::Error::invalid())?.to_string();
Ok(SurrealNamespaceLegacy(value))
}
fn encode<E>(&self, values: &mut E)
where
E: Extend<HeaderValue>,
{
values.extend(std::iter::once(self.into()));
}
}
impl std::ops::Deref for SurrealNamespaceLegacy {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<SurrealNamespaceLegacy> for HeaderValue {
fn from(value: SurrealNamespaceLegacy) -> Self {
HeaderValue::from(&value)
}
}
impl From<&SurrealNamespaceLegacy> for HeaderValue {
fn from(value: &SurrealNamespaceLegacy) -> Self {
HeaderValue::from_str(value.0.as_str()).unwrap()
}
}

View file

@ -35,7 +35,7 @@ use http::header;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use surrealdb::headers::{AUTH_DB, AUTH_NS, DB, DB_LEGACY, ID, ID_LEGACY, NS, NS_LEGACY}; use surrealdb::headers::{AUTH_DB, AUTH_NS, DB, ID, NS};
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
use tower::ServiceBuilder; use tower::ServiceBuilder;
use tower_http::add_extension::AddExtensionLayer; use tower_http::add_extension::AddExtensionLayer;
@ -108,10 +108,6 @@ pub async fn init(ct: CancellationToken) -> Result<(), Error> {
ID.clone(), ID.clone(),
AUTH_NS.clone(), AUTH_NS.clone(),
AUTH_DB.clone(), AUTH_DB.clone(),
// TODO(gguillemas): Remove these headers once the legacy authentication is deprecated in v2.0.0
NS_LEGACY.clone(),
DB_LEGACY.clone(),
ID_LEGACY.clone(),
]; ];
#[cfg(not(feature = "http-compression"))] #[cfg(not(feature = "http-compression"))]
@ -125,10 +121,6 @@ pub async fn init(ct: CancellationToken) -> Result<(), Error> {
ID.clone(), ID.clone(),
AUTH_NS.clone(), AUTH_NS.clone(),
AUTH_DB.clone(), AUTH_DB.clone(),
// TODO(gguillemas): Remove these headers once the legacy authentication is deprecated in v2.0.0
NS_LEGACY.clone(),
DB_LEGACY.clone(),
ID_LEGACY.clone(),
]; ];
let service = service let service = service

View file

@ -306,7 +306,7 @@ mod cli_integration {
#[test(tokio::test)] #[test(tokio::test)]
async fn with_auth_level() { async fn with_auth_level() {
// Commands with credentials for different auth levels // Commands with credentials for different auth levels
let (addr, mut server) = common::start_server_with_auth_level().await.unwrap(); let (addr, mut server) = common::start_server_with_defaults().await.unwrap();
let creds = format!("--user {USER} --pass {PASS}"); let creds = format!("--user {USER} --pass {PASS}");
let ns = Ulid::new(); let ns = Ulid::new();
let db = Ulid::new(); let db = Ulid::new();
@ -488,74 +488,6 @@ mod cli_integration {
server.finish().unwrap(); server.finish().unwrap();
} }
#[test(tokio::test)]
// TODO(gguillemas): Remove this test once the legacy authentication is deprecated in v2.0.0
async fn without_auth_level() {
// Commands with credentials for different auth levels
let (addr, mut server) = common::start_server_with_defaults().await.unwrap();
let creds = format!("--user {USER} --pass {PASS}");
// Prefix with 'a' so that we don't start with a number and cause a parsing error
let ns = format!("a{}", Ulid::new());
let db = format!("a{}", Ulid::new());
info!("* Create users with identical credentials at ROOT, NS and DB levels");
{
let args = format!("sql --conn http://{addr} --db {db} --ns {ns} {creds}");
let _ = common::run(&args)
.input(format!("DEFINE USER {USER}_root ON ROOT PASSWORD '{PASS}' ROLES OWNER;
DEFINE USER {USER}_ns ON NAMESPACE PASSWORD '{PASS}' ROLES OWNER;
DEFINE USER {USER}_db ON DATABASE PASSWORD '{PASS}' ROLES OWNER;\n").as_str())
.output()
.expect("success");
}
info!("* Pass root level credentials and access root info");
{
let args = format!(
"sql --conn http://{addr} --db {db} --ns {ns} --user {USER}_root --pass {PASS}"
);
let output = common::run(&args)
.input(format!("USE NS {ns} DB {db}; INFO FOR ROOT;\n").as_str())
.output()
.expect("success");
assert!(
output.contains("namespaces: {"),
"auth level root should be able to access root info: {output}"
);
}
info!("* Pass namespace level credentials and access namespace info");
{
let args = format!(
"sql --conn http://{addr} --db {db} --ns {ns} --user {USER}_ns --pass {PASS}"
);
let output = common::run(&args)
.input(format!("USE NS {ns} DB {db}; INFO FOR NS;\n").as_str())
.output();
assert!(
output.clone().unwrap_err().contains("401 Unauthorized"),
"namespace level credentials should not work with CLI authentication: {:?}",
output
);
}
info!("* Pass database level credentials and access database info");
{
let args = format!(
"sql --conn http://{addr} --db {db} --ns {ns} --user {USER}_db --pass {PASS}"
);
let output = common::run(&args)
.input(format!("USE NS {ns} DB {db}; INFO FOR DB;\n").as_str())
.output();
assert!(
output.clone().unwrap_err().contains("401 Unauthorized"),
"database level credentials should not work with CLI authentication: {:?}",
output
);
}
server.finish().unwrap();
}
#[test(tokio::test)] #[test(tokio::test)]
async fn with_anon_auth() { async fn with_anon_auth() {
// Commands without credentials when auth is enabled, should fail // Commands without credentials when auth is enabled, should fail

View file

@ -150,7 +150,6 @@ pub struct StartServerArguments {
pub auth: bool, pub auth: bool,
pub tls: bool, pub tls: bool,
pub wait_is_ready: bool, pub wait_is_ready: bool,
pub enable_auth_level: bool,
pub tick_interval: time::Duration, pub tick_interval: time::Duration,
pub temporary_directory: Option<String>, pub temporary_directory: Option<String>,
pub args: String, pub args: String,
@ -163,7 +162,6 @@ impl Default for StartServerArguments {
auth: true, auth: true,
tls: false, tls: false,
wait_is_ready: true, wait_is_ready: true,
enable_auth_level: false,
tick_interval: time::Duration::new(1, 0), tick_interval: time::Duration::new(1, 0),
temporary_directory: None, temporary_directory: None,
args: "--allow-all".to_string(), args: "--allow-all".to_string(),
@ -179,14 +177,6 @@ pub async fn start_server_without_auth() -> Result<(String, Child), Box<dyn Erro
.await .await
} }
pub async fn start_server_with_auth_level() -> Result<(String, Child), Box<dyn Error>> {
start_server(StartServerArguments {
enable_auth_level: true,
..Default::default()
})
.await
}
pub async fn start_server_with_defaults() -> Result<(String, Child), Box<dyn Error>> { pub async fn start_server_with_defaults() -> Result<(String, Child), Box<dyn Error>> {
start_server(StartServerArguments::default()).await start_server(StartServerArguments::default()).await
} }
@ -207,7 +197,6 @@ pub async fn start_server(
auth, auth,
tls, tls,
wait_is_ready, wait_is_ready,
enable_auth_level,
tick_interval, tick_interval,
temporary_directory, temporary_directory,
args, args,
@ -234,10 +223,6 @@ pub async fn start_server(
extra_args.push_str(" --unauthenticated"); extra_args.push_str(" --unauthenticated");
} }
if enable_auth_level {
extra_args.push_str(" --auth-level-enabled");
}
if !tick_interval.is_zero() { if !tick_interval.is_zero() {
let sec = tick_interval.as_secs(); let sec = tick_interval.as_secs();
extra_args.push_str(format!(" --tick-interval {sec}s").as_str()); extra_args.push_str(format!(" --tick-interval {sec}s").as_str());

View file

@ -15,15 +15,15 @@ mod http_integration {
#[test(tokio::test)] #[test(tokio::test)]
async fn basic_auth() -> Result<(), Box<dyn std::error::Error>> { async fn basic_auth() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_auth_level().await.unwrap(); let (addr, _server) = common::start_server_with_defaults().await.unwrap();
let url = &format!("http://{addr}/sql"); let url = &format!("http://{addr}/sql");
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
let ns = Ulid::new().to_string(); let ns = Ulid::new().to_string();
let db = Ulid::new().to_string(); let db = Ulid::new().to_string();
headers.insert("NS", ns.parse()?); headers.insert("surreal-ns", ns.parse()?);
headers.insert("DB", db.parse()?); headers.insert("surreal-db", db.parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10)) .connect_timeout(Duration::from_millis(10))
@ -209,295 +209,6 @@ mod http_integration {
Ok(()) Ok(())
} }
#[test(tokio::test)]
// TODO(gguillemas): Remove this test once the legacy authentication is deprecated in v2.0.0
async fn basic_auth_legacy() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_defaults().await.unwrap();
let url = &format!("http://{addr}/sql");
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
let ns = Ulid::new().to_string();
let db = Ulid::new().to_string();
headers.insert("NS", ns.parse()?);
headers.insert("DB", db.parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
// Request without credentials, gives an anonymous session
{
let res = client.post(url).body("CREATE foo").send().await?;
assert_eq!(res.status(), 200);
let body = res.text().await?;
assert!(body.contains("Not enough permissions"), "body: {}", body);
}
// Request with invalid credentials, returns 401
{
let res =
client.post(url).basic_auth("user", Some("pass")).body("CREATE foo").send().await?;
assert_eq!(res.status(), 401);
}
// Request with valid root credentials, gives a ROOT session
{
let res =
client.post(url).basic_auth(USER, Some(PASS)).body("CREATE foo").send().await?;
assert_eq!(res.status(), 200);
let body = res.text().await?;
assert!(body.contains(r#"[{"result":[{"id":"foo:"#), "body: {}", body);
}
// Prepare users with identical credentials on ROOT, NAMESPACE and DATABASE levels
{
let res =
client.post(url).basic_auth(USER, Some(PASS))
.body(format!("DEFINE USER {USER}_root ON ROOT PASSWORD '{PASS}' ROLES OWNER;
DEFINE USER {USER}_root ON NAMESPACE PASSWORD '{PASS}' ROLES OWNER;
DEFINE USER {USER}_root ON DATABASE PASSWORD '{PASS}' ROLES OWNER",
)).send().await?;
assert_eq!(res.status(), 200);
}
// Prepare users with identical credentials on NAMESPACE and DATABASE levels
{
let res =
client.post(url).basic_auth(USER, Some(PASS))
.body(format!("DEFINE USER {USER}_ns ON NAMESPACE PASSWORD '{PASS}' ROLES OWNER;
DEFINE USER {USER}_ns ON DATABASE PASSWORD '{PASS}' ROLES OWNER",
)).send().await?;
assert_eq!(res.status(), 200);
}
// Prepare users with on DATABASE level
{
let res = client
.post(url)
.basic_auth(USER, Some(PASS))
.body(format!("DEFINE USER {USER}_db ON DATABASE PASSWORD '{PASS}' ROLES OWNER",))
.send()
.await?;
assert_eq!(res.status(), 200);
}
// Request with ROOT level shared credentials to access ROOT, returns 200 and succeeds
{
let res = client
.post(url)
.basic_auth(format!("{}_{}", USER, "root"), Some(PASS))
.body("INFO FOR ROOT")
.send()
.await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "OK", "body: {}", body);
}
// Request with ROOT level shared credentials to access NS, returns 200 and succeeds
{
let res = client
.post(url)
.basic_auth(format!("{}_{}", USER, "root"), Some(PASS))
.body("INFO FOR NS")
.send()
.await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "OK", "body: {}", body);
}
// Request with ROOT level shared credentials to access NS, returns 200 and succeeds
{
let res = client
.post(url)
.basic_auth(format!("{}_{}", USER, "root"), Some(PASS))
.body("INFO FOR DB")
.send()
.await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "OK", "body: {}", body);
}
// Request with NS level shared credentials to access ROOT, returns 200 but fails
{
let res = client
.post(url)
.basic_auth(format!("{}_{}", USER, "ns"), Some(PASS))
.body("INFO FOR ROOT")
.send()
.await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "ERR", "body: {}", body);
assert_eq!(
body[0]["result"], "IAM error: Not enough permissions to perform this action",
"body: {}",
body
);
}
// Request with NS level shared credentials to access NS, returns 200 and succeeds
{
let res = client
.post(url)
.basic_auth(format!("{}_{}", USER, "ns"), Some(PASS))
.body("INFO FOR NS")
.send()
.await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "OK", "body: {}", body);
}
// Request with NS level shared credentials to access DB, returns 200 and succeeds
{
let res = client
.post(url)
.basic_auth(format!("{}_{}", USER, "ns"), Some(PASS))
.body("INFO FOR DB")
.send()
.await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "OK", "body: {}", body);
}
// Request with DB level shared credentials to access ROOT, returns 200 but fails
{
let res = client
.post(url)
.basic_auth(format!("{}_{}", USER, "db"), Some(PASS))
.body("INFO FOR ROOT")
.send()
.await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "ERR", "body: {}", body);
assert_eq!(
body[0]["result"], "IAM error: Not enough permissions to perform this action",
"body: {}",
body
);
}
// Request with DB level shared credentials to access NS, returns 200 but fails
{
let res = client
.post(url)
.basic_auth(format!("{}_{}", USER, "db"), Some(PASS))
.body("INFO FOR NS")
.send()
.await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "ERR", "body: {}", body);
assert_eq!(
body[0]["result"], "IAM error: Not enough permissions to perform this action",
"body: {}",
body
);
}
// Request with DB level shared credentials to access DB, returns 200 and succeeds
{
let res = client
.post(url)
.basic_auth(format!("{}_{}", USER, "db"), Some(PASS))
.body("INFO FOR DB")
.send()
.await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "OK", "body: {}", body);
}
// Prepare users with identical name but different password on ROOT, NAMESPACE and DATABASE levels
let shared_user_name = format!("{}_{}", USER, "all");
let shared_user_pass_root = format!("{}_{}", PASS, "root");
let shared_user_pass_ns = format!("{}_{}", PASS, "ns");
let shared_user_pass_db = format!("{}_{}", PASS, "db");
{
let res =
client.post(url).basic_auth(USER, Some(PASS))
.body(format!("DEFINE USER {shared_user_name} ON ROOT PASSWORD '{shared_user_pass_root}' ROLES OWNER;
DEFINE USER {shared_user_name} ON NAMESPACE PASSWORD '{shared_user_pass_ns}' ROLES OWNER;
DEFINE USER {shared_user_name} ON DATABASE PASSWORD '{shared_user_pass_db}' ROLES OWNER",
)).send().await?;
assert_eq!(res.status(), 200);
}
// Request with shared user with password for ROOT to access ROOT, returns 200 and succeeds
{
let res = client
.post(url)
.basic_auth(&shared_user_name, Some(&shared_user_pass_root))
.body("INFO FOR ROOT")
.send()
.await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "OK", "body: {}", body);
}
// Request with shared user with password for NS to access ROOT, returns 200 but fails
{
let res = client
.post(url)
.basic_auth(&shared_user_name, Some(&shared_user_pass_ns))
.body("INFO FOR ROOT")
.send()
.await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "ERR", "body: {}", body);
}
// Request with shared user with password for NS to access NS, returns 200 and succeeds
{
let res = client
.post(url)
.basic_auth(&shared_user_name, Some(&shared_user_pass_ns))
.body("INFO FOR NS")
.send()
.await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "OK", "body: {}", body);
}
// Request with shared user with password for DB to access NS, returns 200 but fails
{
let res = client
.post(url)
.basic_auth(&shared_user_name, Some(&shared_user_pass_db))
.body("INFO FOR NS")
.send()
.await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "ERR", "body: {}", body);
}
// Request with shared user with password for DB to access DB, returns 200 and succeeds
{
let res = client
.post(url)
.basic_auth(&shared_user_name, Some(&shared_user_pass_db))
.body("INFO FOR DB")
.send()
.await?;
assert_eq!(res.status(), 200);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert_eq!(body[0]["status"], "OK", "body: {}", body);
}
Ok(())
}
#[test(tokio::test)] #[test(tokio::test)]
async fn bearer_auth() -> Result<(), Box<dyn std::error::Error>> { async fn bearer_auth() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_defaults().await.unwrap(); let (addr, _server) = common::start_server_with_defaults().await.unwrap();
@ -508,8 +219,8 @@ mod http_integration {
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", ns.parse()?); headers.insert("surreal-ns", ns.parse()?);
headers.insert("DB", db.parse()?); headers.insert("surreal-db", db.parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10)) .connect_timeout(Duration::from_millis(10))
@ -594,8 +305,8 @@ mod http_integration {
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", Ulid::new().to_string().parse()?); headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("DB", Ulid::new().to_string().parse()?); headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10)) .connect_timeout(Duration::from_millis(10))
@ -648,8 +359,8 @@ mod http_integration {
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", Ulid::new().to_string().parse()?); headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("DB", Ulid::new().to_string().parse()?); headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10)) .connect_timeout(Duration::from_millis(10))
@ -720,8 +431,8 @@ mod http_integration {
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", Ulid::new().to_string().parse()?); headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("DB", Ulid::new().to_string().parse()?); headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10)) .connect_timeout(Duration::from_millis(10))
@ -748,7 +459,7 @@ mod http_integration {
#[test(tokio::test)] #[test(tokio::test)]
async fn signin_endpoint() -> Result<(), Box<dyn std::error::Error>> { async fn signin_endpoint() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_auth_level().await.unwrap(); let (addr, _server) = common::start_server_with_defaults().await.unwrap();
let url = &format!("http://{addr}/signin"); let url = &format!("http://{addr}/signin");
let ns = Ulid::new().to_string(); let ns = Ulid::new().to_string();
@ -756,8 +467,8 @@ mod http_integration {
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", ns.parse()?); headers.insert("surreal-ns", ns.parse()?);
headers.insert("DB", db.parse()?); headers.insert("surreal-db", db.parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10)) .connect_timeout(Duration::from_millis(10))
@ -970,178 +681,6 @@ mod http_integration {
Ok(()) Ok(())
} }
#[test(tokio::test)]
// TODO(gguillemas): Remove this test once the legacy authentication is deprecated in v2.0.0
async fn signin_endpoint_legacy() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_defaults().await.unwrap();
let url = &format!("http://{addr}/signin");
let ns = Ulid::new().to_string();
let db = Ulid::new().to_string();
// Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", ns.parse()?);
headers.insert("DB", db.parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10))
.default_headers(headers)
.build()?;
// Create a DB user
{
let res = client
.post(format!("http://{addr}/sql"))
.basic_auth(USER, Some(PASS))
.body(r#"DEFINE USER user_db ON DB PASSWORD 'pass_db'"#)
.send()
.await?;
assert!(res.status().is_success(), "body: {}", res.text().await?);
}
// Signin with valid DB credentials and get the token
{
let req_body = serde_json::to_string(
json!({
"ns": ns,
"db": db,
"user": "user_db",
"pass": "pass_db",
})
.as_object()
.unwrap(),
)
.unwrap();
let res = client.post(url).body(req_body).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert!(!body["token"].as_str().unwrap().to_string().is_empty(), "body: {}", body);
}
// Signin with invalid DB credentials returns 401
{
let req_body = serde_json::to_string(
json!({
"ns": ns,
"db": db,
"user": "user_db",
"pass": "invalid_pass",
})
.as_object()
.unwrap(),
)
.unwrap();
let res = client.post(url).body(req_body).send().await?;
assert_eq!(res.status(), 401, "body: {}", res.text().await?);
}
// Create a NS user
{
let res = client
.post(format!("http://{addr}/sql"))
.basic_auth(USER, Some(PASS))
.body(r#"DEFINE USER user_ns ON ROOT PASSWORD 'pass_ns'"#)
.send()
.await?;
assert!(res.status().is_success(), "body: {}", res.text().await?);
}
// Signin with valid NS credentials specifying NS and DB and get the token
{
let req_body = serde_json::to_string(
json!({
"ns": ns,
"db": db,
"user": "user_ns",
"pass": "pass_ns",
})
.as_object()
.unwrap(),
)
.unwrap();
let res = client.post(url).body(req_body).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert!(!body["token"].as_str().unwrap().to_string().is_empty(), "body: {}", body);
}
// Signin with invalid NS credentials returns 401
{
let req_body = serde_json::to_string(
json!({
"ns": ns,
"db": db,
"user": "user_ns",
"pass": "invalid_pass",
})
.as_object()
.unwrap(),
)
.unwrap();
let res = client.post(url).body(req_body).send().await?;
assert_eq!(res.status(), 401, "body: {}", res.text().await?);
}
// Create a ROOT user
{
let res = client
.post(format!("http://{addr}/sql"))
.basic_auth(USER, Some(PASS))
.body(r#"DEFINE USER user_root ON ROOT PASSWORD 'pass_root'"#)
.send()
.await?;
assert!(res.status().is_success(), "body: {}", res.text().await?);
}
// Signin with valid ROOT credentials specifying NS and DB and get the token
{
let req_body = serde_json::to_string(
json!({
"ns": ns,
"db": db,
"user": "user_root",
"pass": "pass_root",
})
.as_object()
.unwrap(),
)
.unwrap();
let res = client.post(url).body(req_body).send().await?;
assert_eq!(res.status(), 200, "body: {}", res.text().await?);
let body: serde_json::Value = serde_json::from_str(&res.text().await?).unwrap();
assert!(!body["token"].as_str().unwrap().to_string().is_empty(), "body: {}", body);
}
// Signin with invalid ROOT credentials returns 401
{
let req_body = serde_json::to_string(
json!({
"ns": ns,
"db": db,
"user": "user_root",
"pass": "invalid_pass",
})
.as_object()
.unwrap(),
)
.unwrap();
let res = client.post(url).body(req_body).send().await?;
assert_eq!(res.status(), 401, "body: {}", res.text().await?);
}
Ok(())
}
#[test(tokio::test)] #[test(tokio::test)]
async fn signup_endpoint() -> Result<(), Box<dyn std::error::Error>> { async fn signup_endpoint() -> Result<(), Box<dyn std::error::Error>> {
let (addr, _server) = common::start_server_with_defaults().await.unwrap(); let (addr, _server) = common::start_server_with_defaults().await.unwrap();
@ -1152,8 +691,8 @@ mod http_integration {
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", ns.parse()?); headers.insert("surreal-ns", ns.parse()?);
headers.insert("DB", db.parse()?); headers.insert("surreal-db", db.parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10)) .connect_timeout(Duration::from_millis(10))
@ -1214,8 +753,8 @@ mod http_integration {
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", Ulid::new().to_string().parse()?); headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("DB", Ulid::new().to_string().parse()?); headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
@ -1328,8 +867,8 @@ mod http_integration {
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", Ulid::new().to_string().parse()?); headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("DB", Ulid::new().to_string().parse()?); headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
headers.insert(header::ACCEPT_ENCODING, "gzip".parse()?); headers.insert(header::ACCEPT_ENCODING, "gzip".parse()?);
@ -1361,8 +900,8 @@ mod http_integration {
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", Ulid::new().to_string().parse()?); headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("DB", Ulid::new().to_string().parse()?); headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10)) .connect_timeout(Duration::from_millis(10))
@ -1437,8 +976,8 @@ mod http_integration {
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", Ulid::new().to_string().parse()?); headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("DB", Ulid::new().to_string().parse()?); headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10)) .connect_timeout(Duration::from_millis(10))
@ -1525,8 +1064,8 @@ mod http_integration {
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", Ulid::new().to_string().parse()?); headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("DB", Ulid::new().to_string().parse()?); headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10)) .connect_timeout(Duration::from_millis(10))
@ -1590,8 +1129,8 @@ mod http_integration {
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", Ulid::new().to_string().parse()?); headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("DB", Ulid::new().to_string().parse()?); headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10)) .connect_timeout(Duration::from_millis(10))
@ -1659,8 +1198,8 @@ mod http_integration {
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", Ulid::new().to_string().parse()?); headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("DB", Ulid::new().to_string().parse()?); headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10)) .connect_timeout(Duration::from_millis(10))
@ -1728,8 +1267,8 @@ mod http_integration {
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", Ulid::new().to_string().parse()?); headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("DB", Ulid::new().to_string().parse()?); headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10)) .connect_timeout(Duration::from_millis(10))
@ -1780,8 +1319,8 @@ mod http_integration {
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", Ulid::new().to_string().parse()?); headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("DB", Ulid::new().to_string().parse()?); headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10)) .connect_timeout(Duration::from_millis(10))
@ -1819,8 +1358,8 @@ mod http_integration {
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", Ulid::new().to_string().parse()?); headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("DB", Ulid::new().to_string().parse()?); headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10)) .connect_timeout(Duration::from_millis(10))
@ -1916,8 +1455,8 @@ mod http_integration {
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", Ulid::new().to_string().parse()?); headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("DB", Ulid::new().to_string().parse()?); headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10)) .connect_timeout(Duration::from_millis(10))
@ -1992,8 +1531,8 @@ mod http_integration {
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", Ulid::new().to_string().parse()?); headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("DB", Ulid::new().to_string().parse()?); headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10)) .connect_timeout(Duration::from_millis(10))
@ -2069,8 +1608,8 @@ mod http_integration {
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", Ulid::new().to_string().parse()?); headers.insert("surreal-ns", Ulid::new().to_string().parse()?);
headers.insert("DB", Ulid::new().to_string().parse()?); headers.insert("surreal-db", Ulid::new().to_string().parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10)) .connect_timeout(Duration::from_millis(10))

View file

@ -55,8 +55,8 @@ mod ml_integration {
let body = Body::wrap_stream(generator); let body = Body::wrap_stream(generator);
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", ns.parse()?); headers.insert("surreal-ns", ns.parse()?);
headers.insert("DB", db.parse()?); headers.insert("surreal-db", db.parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_secs(1)) .connect_timeout(Duration::from_secs(1))
.default_headers(headers) .default_headers(headers)
@ -93,8 +93,8 @@ mod ml_integration {
let body = Body::wrap_stream(generator); let body = Body::wrap_stream(generator);
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", ns.parse()?); headers.insert("surreal-ns", ns.parse()?);
headers.insert("DB", db.parse()?); headers.insert("surreal-db", db.parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_secs(1)) .connect_timeout(Duration::from_secs(1))
.default_headers(headers) .default_headers(headers)
@ -128,8 +128,8 @@ mod ml_integration {
let body = Body::wrap_stream(generator); let body = Body::wrap_stream(generator);
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", ns.parse()?); headers.insert("surreal-ns", ns.parse()?);
headers.insert("DB", db.parse()?); headers.insert("surreal-db", db.parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_secs(1)) .connect_timeout(Duration::from_secs(1))
.default_headers(headers) .default_headers(headers)
@ -160,8 +160,8 @@ mod ml_integration {
let body = Body::wrap_stream(generator); let body = Body::wrap_stream(generator);
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", ns.parse()?); headers.insert("surreal-ns", ns.parse()?);
headers.insert("DB", db.parse()?); headers.insert("surreal-db", db.parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_secs(1)) .connect_timeout(Duration::from_secs(1))
.default_headers(headers) .default_headers(headers)
@ -193,8 +193,8 @@ mod ml_integration {
let body = Body::wrap_stream(generator); let body = Body::wrap_stream(generator);
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", ns.parse()?); headers.insert("surreal-ns", ns.parse()?);
headers.insert("DB", db.parse()?); headers.insert("surreal-db", db.parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_secs(1)) .connect_timeout(Duration::from_secs(1))
.default_headers(headers) .default_headers(headers)
@ -227,8 +227,8 @@ mod ml_integration {
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", ns.parse()?); headers.insert("surreal-ns", ns.parse()?);
headers.insert("DB", db.parse()?); headers.insert("surreal-db", db.parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10)) .connect_timeout(Duration::from_millis(10))
@ -263,8 +263,8 @@ mod ml_integration {
// Prepare HTTP client // Prepare HTTP client
let mut headers = reqwest::header::HeaderMap::new(); let mut headers = reqwest::header::HeaderMap::new();
headers.insert("NS", ns.parse()?); headers.insert("surreal-ns", ns.parse()?);
headers.insert("DB", db.parse()?); headers.insert("surreal-db", db.parse()?);
headers.insert(header::ACCEPT, "application/json".parse()?); headers.insert(header::ACCEPT, "application/json".parse()?);
let client = reqwest::Client::builder() let client = reqwest::Client::builder()
.connect_timeout(Duration::from_millis(10)) .connect_timeout(Duration::from_millis(10))