Add tests for the signin and signup methods ()

This commit is contained in:
Gerard Guillemas Martos 2024-03-13 14:15:42 +01:00 committed by GitHub
parent 2e27000c8e
commit 900fc58328
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 491 additions and 0 deletions
core/src/iam

View file

@ -362,3 +362,375 @@ pub async fn root(
_ => Err(Error::InvalidAuth),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::iam::Role;
use std::collections::HashMap;
#[tokio::test]
async fn test_signin_scope() {
// Test with correct credentials
{
let ds = Datastore::new("memory").await.unwrap();
let sess = Session::owner().with_ns("test").with_db("test");
ds.execute(
r#"
DEFINE SCOPE user SESSION 1h
SIGNIN (
SELECT * FROM user WHERE name = $user AND crypto::argon2::compare(pass, $pass)
)
SIGNUP (
CREATE user CONTENT {
name: $user,
pass: crypto::argon2::generate($pass)
}
);
CREATE user:test CONTENT {
name: 'user',
pass: crypto::argon2::generate('pass')
}
"#,
&sess,
None,
)
.await
.unwrap();
// Signin with the user
let mut sess = Session {
ns: Some("test".to_string()),
db: Some("test".to_string()),
..Default::default()
};
let mut vars: HashMap<&str, Value> = HashMap::new();
vars.insert("user", "user".into());
vars.insert("pass", "pass".into());
let res = sc(
&ds,
&mut sess,
"test".to_string(),
"test".to_string(),
"user".to_string(),
vars.into(),
)
.await;
assert!(res.is_ok(), "Failed to signin with credentials: {:?}", res);
assert_eq!(sess.ns, Some("test".to_string()));
assert_eq!(sess.db, Some("test".to_string()));
assert_eq!(sess.au.id(), "user:test");
assert!(sess.au.is_scope());
assert_eq!(sess.au.level().ns(), Some("test"));
assert_eq!(sess.au.level().db(), Some("test"));
// Scope users should not have roles
assert!(!sess.au.has_role(&Role::Viewer), "Auth user expected to not have Viewer role");
assert!(!sess.au.has_role(&Role::Editor), "Auth user expected to not have Editor role");
assert!(!sess.au.has_role(&Role::Owner), "Auth user expected to not have Owner role");
// Expiration should always be set for tokens issued by SurrealDB
let exp = sess.exp.unwrap();
// Expiration should match the current time plus session duration with some margin
let min_exp = (Utc::now() + Duration::hours(1) - Duration::seconds(10)).timestamp();
let max_exp = (Utc::now() + Duration::hours(1) + Duration::seconds(10)).timestamp();
assert!(
exp > min_exp && exp < max_exp,
"Session expiration is expected to follow scope duration"
);
}
// Test with incorrect credentials
{
let ds = Datastore::new("memory").await.unwrap();
let sess = Session::owner().with_ns("test").with_db("test");
ds.execute(
r#"
DEFINE SCOPE user SESSION 1h
SIGNIN (
SELECT * FROM user WHERE name = $user AND crypto::argon2::compare(pass, $pass)
)
SIGNUP (
CREATE user CONTENT {
name: $user,
pass: crypto::argon2::generate($pass)
}
);
CREATE user:test CONTENT {
name: 'user',
pass: crypto::argon2::generate('pass')
}
"#,
&sess,
None,
)
.await
.unwrap();
// Signin with the user
let mut sess = Session {
ns: Some("test".to_string()),
db: Some("test".to_string()),
..Default::default()
};
let mut vars: HashMap<&str, Value> = HashMap::new();
vars.insert("user", "user".into());
vars.insert("pass", "incorrect".into());
let res = sc(
&ds,
&mut sess,
"test".to_string(),
"test".to_string(),
"user".to_string(),
vars.into(),
)
.await;
assert!(res.is_err(), "Unexpected successful signin: {:?}", res);
}
}
#[tokio::test]
async fn test_signin_db() {
//
// Test without roles defined
//
{
let ds = Datastore::new("memory").await.unwrap();
let sess = Session::owner().with_ns("test").with_db("test");
ds.execute("DEFINE USER user ON DB PASSWORD 'pass'", &sess, None).await.unwrap();
// Signin with the user
let mut sess = Session {
ns: Some("test".to_string()),
db: Some("test".to_string()),
..Default::default()
};
let res = db(
&ds,
&mut sess,
"test".to_string(),
"test".to_string(),
"user".to_string(),
"pass".to_string(),
)
.await;
assert!(res.is_ok(), "Failed to signin with credentials: {:?}", res);
assert_eq!(sess.ns, Some("test".to_string()));
assert_eq!(sess.db, Some("test".to_string()));
assert_eq!(sess.au.id(), "user");
assert!(sess.au.is_db());
assert_eq!(sess.au.level().ns(), Some("test"));
assert_eq!(sess.au.level().db(), Some("test"));
assert!(sess.au.has_role(&Role::Viewer), "Auth user expected to have Viewer role");
assert!(!sess.au.has_role(&Role::Editor), "Auth user expected to not have Editor role");
assert!(!sess.au.has_role(&Role::Owner), "Auth user expected to not have Owner role");
assert_eq!(sess.exp, None, "Default system user expiration is expected to be None");
}
//
// Test with roles defined
//
{
let ds = Datastore::new("memory").await.unwrap();
let sess = Session::owner().with_ns("test").with_db("test");
ds.execute("DEFINE USER user ON DB PASSWORD 'pass' ROLES EDITOR, OWNER", &sess, None)
.await
.unwrap();
// Signin with the user
let mut sess = Session {
ns: Some("test".to_string()),
db: Some("test".to_string()),
..Default::default()
};
let res = db(
&ds,
&mut sess,
"test".to_string(),
"test".to_string(),
"user".to_string(),
"pass".to_string(),
)
.await;
assert!(res.is_ok(), "Failed to signin with credentials: {:?}", res);
assert_eq!(sess.ns, Some("test".to_string()));
assert_eq!(sess.db, Some("test".to_string()));
assert_eq!(sess.au.id(), "user");
assert!(sess.au.is_db());
assert_eq!(sess.au.level().ns(), Some("test"));
assert_eq!(sess.au.level().db(), Some("test"));
assert!(!sess.au.has_role(&Role::Viewer), "Auth user expected to not have Viewer role");
assert!(sess.au.has_role(&Role::Editor), "Auth user expected to have Editor role");
assert!(sess.au.has_role(&Role::Owner), "Auth user expected to have Owner role");
assert_eq!(sess.exp, None, "Default system user expiration is expected to be None");
}
// Test invalid password
{
let ds = Datastore::new("memory").await.unwrap();
let sess = Session::owner().with_ns("test").with_db("test");
ds.execute("DEFINE USER user ON DB PASSWORD 'pass'", &sess, None).await.unwrap();
let mut sess = Session {
..Default::default()
};
let res = db(
&ds,
&mut sess,
"test".to_string(),
"test".to_string(),
"user".to_string(),
"invalid".to_string(),
)
.await;
assert!(res.is_err(), "Unexpected successful signin: {:?}", res);
}
}
#[tokio::test]
async fn test_signin_ns() {
//
// Test without roles defined
//
{
let ds = Datastore::new("memory").await.unwrap();
let sess = Session::owner().with_ns("test");
ds.execute("DEFINE USER user ON NS PASSWORD 'pass'", &sess, None).await.unwrap();
// Signin with the user
let mut sess = Session {
ns: Some("test".to_string()),
..Default::default()
};
let res =
ns(&ds, &mut sess, "test".to_string(), "user".to_string(), "pass".to_string())
.await;
assert!(res.is_ok(), "Failed to signin with credentials: {:?}", res);
assert_eq!(sess.ns, Some("test".to_string()));
assert_eq!(sess.au.id(), "user");
assert!(sess.au.is_ns());
assert_eq!(sess.au.level().ns(), Some("test"));
assert!(sess.au.has_role(&Role::Viewer), "Auth user expected to have Viewer role");
assert!(!sess.au.has_role(&Role::Editor), "Auth user expected to not have Editor role");
assert!(!sess.au.has_role(&Role::Owner), "Auth user expected to not have Owner role");
assert_eq!(sess.exp, None, "Default system user expiration is expected to be None");
}
//
// Test with roles defined
//
{
let ds = Datastore::new("memory").await.unwrap();
let sess = Session::owner().with_ns("test");
ds.execute("DEFINE USER user ON NS PASSWORD 'pass' ROLES EDITOR, OWNER", &sess, None)
.await
.unwrap();
// Signin with the user
let mut sess = Session {
ns: Some("test".to_string()),
..Default::default()
};
let res =
ns(&ds, &mut sess, "test".to_string(), "user".to_string(), "pass".to_string())
.await;
assert!(res.is_ok(), "Failed to signin with credentials: {:?}", res);
assert_eq!(sess.ns, Some("test".to_string()));
assert_eq!(sess.au.id(), "user");
assert!(sess.au.is_ns());
assert_eq!(sess.au.level().ns(), Some("test"));
assert!(!sess.au.has_role(&Role::Viewer), "Auth user expected to not have Viewer role");
assert!(sess.au.has_role(&Role::Editor), "Auth user expected to have Editor role");
assert!(sess.au.has_role(&Role::Owner), "Auth user expected to have Owner role");
assert_eq!(sess.exp, None, "Default system user expiration is expected to be None");
}
// Test invalid password
{
let ds = Datastore::new("memory").await.unwrap();
let sess = Session::owner().with_ns("test");
ds.execute("DEFINE USER user ON NS PASSWORD 'pass'", &sess, None).await.unwrap();
let mut sess = Session {
..Default::default()
};
let res =
ns(&ds, &mut sess, "test".to_string(), "user".to_string(), "invalid".to_string())
.await;
assert!(res.is_err(), "Unexpected successful signin: {:?}", res);
}
}
#[tokio::test]
async fn test_signin_root() {
//
// Test without roles defined
//
{
let ds = Datastore::new("memory").await.unwrap();
let sess = Session::owner();
ds.execute("DEFINE USER user ON ROOT PASSWORD 'pass'", &sess, None).await.unwrap();
// Signin with the user
let mut sess = Session {
..Default::default()
};
let res = root(&ds, &mut sess, "user".to_string(), "pass".to_string()).await;
assert!(res.is_ok(), "Failed to signin with credentials: {:?}", res);
assert_eq!(sess.au.id(), "user");
assert!(sess.au.is_root());
assert!(sess.au.has_role(&Role::Viewer), "Auth user expected to have Viewer role");
assert!(!sess.au.has_role(&Role::Editor), "Auth user expected to not have Editor role");
assert!(!sess.au.has_role(&Role::Owner), "Auth user expected to not have Owner role");
assert_eq!(sess.exp, None, "Default system user expiration is expected to be None");
}
//
// Test with roles defined
//
{
let ds = Datastore::new("memory").await.unwrap();
let sess = Session::owner();
ds.execute("DEFINE USER user ON ROOT PASSWORD 'pass' ROLES EDITOR, OWNER", &sess, None)
.await
.unwrap();
// Signin with the user
let mut sess = Session {
..Default::default()
};
let res = root(&ds, &mut sess, "user".to_string(), "pass".to_string()).await;
assert!(res.is_ok(), "Failed to signin with credentials: {:?}", res);
assert_eq!(sess.au.id(), "user");
assert!(sess.au.is_root());
assert!(!sess.au.has_role(&Role::Viewer), "Auth user expected to not have Viewer role");
assert!(sess.au.has_role(&Role::Editor), "Auth user expected to have Editor role");
assert!(sess.au.has_role(&Role::Owner), "Auth user expected to have Owner role");
assert_eq!(sess.exp, None, "Default system user expiration is expected to be None");
}
// Test invalid password
{
let ds = Datastore::new("memory").await.unwrap();
let sess = Session::owner().with_ns("test");
ds.execute("DEFINE USER user ON ROOT PASSWORD 'pass'", &sess, None).await.unwrap();
let mut sess = Session {
..Default::default()
};
let res = root(&ds, &mut sess, "user".to_string(), "invalid".to_string()).await;
assert!(res.is_err(), "Unexpected successful signin: {:?}", res);
}
}
}

View file

@ -141,3 +141,122 @@ pub async fn sc(
_ => Err(Error::NoScopeFound),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::iam::Role;
use std::collections::HashMap;
#[tokio::test]
async fn test_scope_signup() {
// Test with valid parameters
{
let ds = Datastore::new("memory").await.unwrap();
let sess = Session::owner().with_ns("test").with_db("test");
ds.execute(
r#"
DEFINE SCOPE user SESSION 1h
SIGNIN (
SELECT * FROM user WHERE name = $user AND crypto::argon2::compare(pass, $pass)
)
SIGNUP (
CREATE user CONTENT {
name: $user,
pass: crypto::argon2::generate($pass)
}
);
"#,
&sess,
None,
)
.await
.unwrap();
// Signin with the user
let mut sess = Session {
ns: Some("test".to_string()),
db: Some("test".to_string()),
..Default::default()
};
let mut vars: HashMap<&str, Value> = HashMap::new();
vars.insert("user", "user".into());
vars.insert("pass", "pass".into());
let res = sc(
&ds,
&mut sess,
"test".to_string(),
"test".to_string(),
"user".to_string(),
vars.into(),
)
.await;
assert!(res.is_ok(), "Failed to signup: {:?}", res);
assert_eq!(sess.ns, Some("test".to_string()));
assert_eq!(sess.db, Some("test".to_string()));
assert!(sess.au.id().starts_with("user:"));
assert!(sess.au.is_scope());
assert_eq!(sess.au.level().ns(), Some("test"));
assert_eq!(sess.au.level().db(), Some("test"));
// Scope users should not have roles.
assert!(!sess.au.has_role(&Role::Viewer), "Auth user expected to not have Viewer role");
assert!(!sess.au.has_role(&Role::Editor), "Auth user expected to not have Editor role");
assert!(!sess.au.has_role(&Role::Owner), "Auth user expected to not have Owner role");
// Expiration should always be set for tokens issued by SurrealDB
let exp = sess.exp.unwrap();
// Expiration should match the current time plus session duration with some margin
let min_exp = (Utc::now() + Duration::hours(1) - Duration::seconds(10)).timestamp();
let max_exp = (Utc::now() + Duration::hours(1) + Duration::seconds(10)).timestamp();
assert!(
exp > min_exp && exp < max_exp,
"Session expiration is expected to follow scope duration"
);
}
// Test with invalid parameters
{
let ds = Datastore::new("memory").await.unwrap();
let sess = Session::owner().with_ns("test").with_db("test");
ds.execute(
r#"
DEFINE SCOPE user SESSION 1h
SIGNIN (
SELECT * FROM user WHERE name = $user AND crypto::argon2::compare(pass, $pass)
)
SIGNUP (
CREATE user CONTENT {
name: $user,
pass: crypto::argon2::generate($pass)
}
);
"#,
&sess,
None,
)
.await
.unwrap();
// Signin with the user
let mut sess = Session {
ns: Some("test".to_string()),
db: Some("test".to_string()),
..Default::default()
};
let mut vars: HashMap<&str, Value> = HashMap::new();
// Password is missing
vars.insert("user", "user".into());
let res = sc(
&ds,
&mut sess,
"test".to_string(),
"test".to_string(),
"user".to_string(),
vars.into(),
)
.await;
assert!(res.is_err(), "Unexpected successful signup: {:?}", res);
}
}
}