Revert to using legacy authentication in signin
by default (#3052)
Co-authored-by: Salvador Girones Gil <salvadorgirones@gmail.com>
This commit is contained in:
parent
69572e9e6b
commit
bc4ffcb4cf
11 changed files with 472 additions and 35 deletions
|
@ -1,4 +1,4 @@
|
|||
use super::verify::{verify_db_creds, verify_ns_creds, verify_root_creds};
|
||||
use super::verify::{verify_creds_legacy, verify_db_creds, verify_ns_creds, verify_root_creds};
|
||||
use super::{Actor, Level};
|
||||
use crate::cnf::{INSECURE_FORWARD_SCOPE_ERRORS, SERVER_NAME};
|
||||
use crate::dbs::Session;
|
||||
|
@ -194,7 +194,16 @@ pub async fn db(
|
|||
user: String,
|
||||
pass: String,
|
||||
) -> Result<Option<String>, Error> {
|
||||
match verify_db_creds(kvs, &ns, &db, &user, &pass).await {
|
||||
let verify_creds = if kvs.is_auth_level_enabled() {
|
||||
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) => {
|
||||
// Create the authentication key
|
||||
let key = EncodingKey::from_secret(u.code.as_ref());
|
||||
|
@ -236,7 +245,16 @@ pub async fn ns(
|
|||
user: String,
|
||||
pass: String,
|
||||
) -> Result<Option<String>, Error> {
|
||||
match verify_ns_creds(kvs, &ns, &user, &pass).await {
|
||||
let verify_creds = if kvs.is_auth_level_enabled() {
|
||||
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) => {
|
||||
// Create the authentication key
|
||||
let key = EncodingKey::from_secret(u.code.as_ref());
|
||||
|
@ -276,7 +294,16 @@ pub async fn root(
|
|||
user: String,
|
||||
pass: String,
|
||||
) -> Result<Option<String>, Error> {
|
||||
match verify_root_creds(kvs, &user, &pass).await {
|
||||
let verify_creds = if kvs.is_auth_level_enabled() {
|
||||
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) => {
|
||||
// Create the authentication key
|
||||
let key = EncodingKey::from_secret(u.code.as_ref());
|
||||
|
|
|
@ -125,7 +125,7 @@ pub async fn basic(
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(gguillemas): Remove this method once the legacy basic auth is deprecated in v2.0.0
|
||||
// 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,
|
||||
|
@ -501,7 +501,7 @@ fn verify_pass(pass: &str, hash: &str) -> Result<(), Error> {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(gguillemas): Remove this method once the legacy basic auth is deprecated in v2.0.0
|
||||
// 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>,
|
||||
|
|
|
@ -92,6 +92,9 @@ pub struct Datastore {
|
|||
strict: bool,
|
||||
// Whether authentication is enabled on this datastore.
|
||||
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
|
||||
query_timeout: Option<Duration>,
|
||||
// The maximum duration timeout for running multiple statements in a transaction
|
||||
|
@ -340,6 +343,8 @@ impl Datastore {
|
|||
inner,
|
||||
strict: 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,
|
||||
transaction_timeout: None,
|
||||
notification_channel: None,
|
||||
|
@ -385,6 +390,13 @@ impl Datastore {
|
|||
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
|
||||
pub fn with_capabilities(mut self, caps: Capabilities) -> Self {
|
||||
self.capabilities = caps;
|
||||
|
@ -396,6 +408,12 @@ impl Datastore {
|
|||
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
|
||||
}
|
||||
|
||||
/// Setup the initial credentials
|
||||
/// Trigger the `unreachable definition` compilation error, probably due to this issue:
|
||||
/// https://github.com/rust-lang/rust/issues/111370
|
||||
|
|
|
@ -23,7 +23,7 @@ pub(crate) struct AuthArguments {
|
|||
requires = "username"
|
||||
)]
|
||||
pub(crate) password: Option<String>,
|
||||
// TODO(gguillemas): Update this help message once the legacy basic auth is deprecated in v2.0.0
|
||||
// TODO(gguillemas): Update this help message once the legacy authentication is deprecated in v2.0.0
|
||||
// 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"
|
||||
|
|
|
@ -17,7 +17,6 @@ pub struct Config {
|
|||
pub client_ip: ClientIp,
|
||||
pub user: Option<String>,
|
||||
pub pass: Option<String>,
|
||||
pub enable_auth_level: bool,
|
||||
pub crt: Option<PathBuf>,
|
||||
pub key: Option<PathBuf>,
|
||||
pub tick_interval: Duration,
|
||||
|
|
|
@ -74,14 +74,6 @@ pub struct StartCommandArguments {
|
|||
requires = "username"
|
||||
)]
|
||||
password: Option<String>,
|
||||
// TODO(gguillemas): Remove this arg once the legacy basic auth is deprecated in v2.0.0
|
||||
// Explicit level authentication will be enabled by default after the deprecation
|
||||
#[arg(
|
||||
help = "Support specifying the level at which to authenticate",
|
||||
help_heading = "Authentication"
|
||||
)]
|
||||
#[arg(env = "SURREAL_ENABLE_AUTH_LEVEL", long = "enable-auth-level")]
|
||||
pub(crate) enable_auth_level: bool,
|
||||
|
||||
//
|
||||
// Datastore connection
|
||||
|
@ -143,7 +135,6 @@ pub async fn init(
|
|||
path,
|
||||
username: user,
|
||||
password: pass,
|
||||
enable_auth_level,
|
||||
client_ip,
|
||||
listen_addresses,
|
||||
dbs,
|
||||
|
@ -179,7 +170,6 @@ pub async fn init(
|
|||
path,
|
||||
user,
|
||||
pass,
|
||||
enable_auth_level,
|
||||
tick_interval,
|
||||
crt: web.as_ref().and_then(|x| x.web_crt.clone()),
|
||||
key: web.as_ref().and_then(|x| x.web_key.clone()),
|
||||
|
|
|
@ -27,6 +27,14 @@ pub struct StartCommandDbsOptions {
|
|||
#[arg(env = "SURREAL_AUTH", long = "auth")]
|
||||
#[arg(default_value_t = false)]
|
||||
auth_enabled: 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(next_help_heading = "Capabilities")]
|
||||
caps: DbsCapabilities,
|
||||
|
@ -204,6 +212,8 @@ pub async fn init(
|
|||
query_timeout,
|
||||
transaction_timeout,
|
||||
auth_enabled,
|
||||
// TODO(gguillemas): Remove this field once the legacy authentication is deprecated in v2.0.0
|
||||
auth_level_enabled,
|
||||
caps,
|
||||
}: StartCommandDbsOptions,
|
||||
) -> Result<(), Error> {
|
||||
|
@ -225,6 +235,11 @@ pub async fn init(
|
|||
} else {
|
||||
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();
|
||||
debug!("Server capabilities: {caps}");
|
||||
|
@ -237,6 +252,7 @@ pub async fn init(
|
|||
.with_query_timeout(query_timeout)
|
||||
.with_transaction_timeout(transaction_timeout)
|
||||
.with_auth_enabled(auth_enabled)
|
||||
.with_auth_level_enabled(auth_level_enabled)
|
||||
.with_capabilities(caps);
|
||||
|
||||
dbs.bootstrap().await?;
|
||||
|
|
|
@ -15,7 +15,6 @@ use surrealdb::{
|
|||
};
|
||||
use tower_http::auth::AsyncAuthorizeRequest;
|
||||
|
||||
use crate::cli::CF;
|
||||
use crate::{dbs::DB, err::Error};
|
||||
|
||||
use super::{
|
||||
|
@ -140,10 +139,7 @@ async fn check_auth(parts: &mut Parts) -> Result<Session, Error> {
|
|||
|
||||
// If Basic authentication data was supplied
|
||||
if let Ok(au) = parts.extract::<TypedHeader<Authorization<Basic>>>().await {
|
||||
// Get local copy of options
|
||||
let opt = CF.get().unwrap();
|
||||
|
||||
if opt.enable_auth_level {
|
||||
if kvs.is_auth_level_enabled() {
|
||||
basic(
|
||||
kvs,
|
||||
&mut session,
|
||||
|
|
|
@ -386,7 +386,7 @@ mod cli_integration {
|
|||
.expect("success");
|
||||
assert!(
|
||||
output.contains("IAM error: Not enough permissions to perform this action"),
|
||||
"auth level datbase should not be able to access root info: {output}",
|
||||
"auth level database should not be able to access root info: {output}",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -401,7 +401,7 @@ mod cli_integration {
|
|||
.expect("success");
|
||||
assert!(
|
||||
output.contains("IAM error: Not enough permissions to perform this action"),
|
||||
"auth level datbase should not be able to access namespace info: {output}",
|
||||
"auth level database should not be able to access namespace info: {output}",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -416,7 +416,7 @@ mod cli_integration {
|
|||
.expect("success");
|
||||
assert!(
|
||||
output.contains("tables: {"),
|
||||
"auth level datbase should be able to access database info: {output}"
|
||||
"auth level database should be able to access database info: {output}"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -453,6 +453,72 @@ mod cli_integration {
|
|||
}
|
||||
}
|
||||
|
||||
#[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, _server) = common::start_server_with_defaults().await.unwrap();
|
||||
let creds = format!("--user {USER} --pass {PASS}");
|
||||
let ns = Ulid::new();
|
||||
let db = 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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test(tokio::test)]
|
||||
async fn with_anon_auth() {
|
||||
// Commands without credentials when auth is enabled, should fail
|
||||
|
|
|
@ -215,7 +215,7 @@ pub async fn start_server(
|
|||
}
|
||||
|
||||
if enable_auth_level {
|
||||
extra_args.push_str(" --enable-auth-level");
|
||||
extra_args.push_str(" --auth-level-enabled");
|
||||
}
|
||||
|
||||
if !tick_interval.is_zero() {
|
||||
|
|
|
@ -210,7 +210,7 @@ mod http_integration {
|
|||
}
|
||||
|
||||
#[test(tokio::test)]
|
||||
// TODO(gguillemas): Remove this test once the legacy basic auth is deprecated in v2.0.0
|
||||
// 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");
|
||||
|
@ -748,6 +748,231 @@ mod http_integration {
|
|||
|
||||
#[test(tokio::test)]
|
||||
async fn signin_endpoint() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let (addr, _server) = common::start_server_with_auth_level().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 NS 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
|
||||
// This should fail because authentication will be attempted at DB level
|
||||
{
|
||||
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(), 401, "body: {}", res.text().await?);
|
||||
}
|
||||
|
||||
// Signin with valid NS credentials specifying NS and get the token
|
||||
{
|
||||
let req_body = serde_json::to_string(
|
||||
json!({
|
||||
"ns": ns,
|
||||
"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
|
||||
// This should fail because authentication will be attempted at DB level
|
||||
{
|
||||
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(), 401, "body: {}", res.text().await?);
|
||||
}
|
||||
|
||||
// Signin with valid ROOT credentials specifying NS and get the token
|
||||
// This should fail because authentication will be attempted at NS level
|
||||
{
|
||||
let req_body = serde_json::to_string(
|
||||
json!({
|
||||
"ns": ns,
|
||||
"user": "user_root",
|
||||
"pass": "pass_root",
|
||||
})
|
||||
.as_object()
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let res = client.post(url).body(req_body).send().await?;
|
||||
assert_eq!(res.status(), 401, "body: {}", res.text().await?);
|
||||
}
|
||||
|
||||
// Signin with valid ROOT credentials without specifying NS nor DB and get the token
|
||||
{
|
||||
let req_body = serde_json::to_string(
|
||||
json!({
|
||||
"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)]
|
||||
// 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");
|
||||
|
||||
|
@ -764,25 +989,25 @@ mod http_integration {
|
|||
.default_headers(headers)
|
||||
.build()?;
|
||||
|
||||
// Create a user
|
||||
// Create a DB user
|
||||
{
|
||||
let res = client
|
||||
.post(format!("http://{addr}/sql"))
|
||||
.basic_auth(USER, Some(PASS))
|
||||
.body(r#"DEFINE USER user ON DB PASSWORD '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 credentials and get the token
|
||||
// Signin with valid DB credentials and get the token
|
||||
{
|
||||
let req_body = serde_json::to_string(
|
||||
json!({
|
||||
"ns": ns,
|
||||
"db": db,
|
||||
"user": "user",
|
||||
"pass": "pass",
|
||||
"user": "user_db",
|
||||
"pass": "pass_db",
|
||||
})
|
||||
.as_object()
|
||||
.unwrap(),
|
||||
|
@ -796,13 +1021,113 @@ mod http_integration {
|
|||
assert!(!body["token"].as_str().unwrap().to_string().is_empty(), "body: {}", body);
|
||||
}
|
||||
|
||||
// Signin with invalid credentials returns 403
|
||||
// Signin with invalid DB credentials returns 401
|
||||
{
|
||||
let req_body = serde_json::to_string(
|
||||
json!({
|
||||
"ns": ns,
|
||||
"db": db,
|
||||
"user": "user",
|
||||
"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()
|
||||
|
|
Loading…
Reference in a new issue