2023-07-15 16:26:36 +00:00
mod parse ;
2023-08-30 18:01:30 +00:00
use parse ::Parse ;
2023-08-19 12:31:51 +00:00
2023-08-30 18:01:30 +00:00
mod helpers ;
use helpers ::new_ds ;
2023-08-11 23:22:53 +00:00
use surrealdb ::dbs ::{ Action , Notification , Session } ;
2023-07-15 16:26:36 +00:00
use surrealdb ::err ::Error ;
2023-07-29 18:47:25 +00:00
use surrealdb ::iam ::Role ;
2024-02-21 10:04:45 +00:00
use surrealdb ::sql ::{ Thing , Value } ;
2023-07-15 16:26:36 +00:00
#[ tokio::test ]
2023-07-29 18:47:25 +00:00
async fn delete ( ) -> Result < ( ) , Error > {
2023-07-15 16:26:36 +00:00
let sql = "
2023-07-29 18:47:25 +00:00
CREATE person :test SET name = ' Tester ' ;
DELETE person :test ;
SELECT * FROM person ;
2023-07-15 16:26:36 +00:00
" ;
2023-08-30 18:01:30 +00:00
let dbs = new_ds ( ) . await ? ;
2023-07-29 18:47:25 +00:00
let ses = Session ::owner ( ) . with_ns ( " test " ) . with_db ( " test " ) ;
2023-07-15 16:26:36 +00:00
let res = & mut dbs . execute ( sql , & ses , None ) . await ? ;
2023-07-29 18:47:25 +00:00
assert_eq! ( res . len ( ) , 3 ) ;
//
let tmp = res . remove ( 0 ) . result ? ;
let val = Value ::parse (
" [
{
id : person :test ,
name : ' Tester '
}
] " ,
) ;
assert_eq! ( tmp , val ) ;
2023-07-15 16:26:36 +00:00
//
2023-07-29 18:47:25 +00:00
let tmp = res . remove ( 0 ) . result ? ;
let val = Value ::parse ( " [] " ) ;
assert_eq! ( tmp , val ) ;
2023-07-15 16:26:36 +00:00
//
let tmp = res . remove ( 0 ) . result ? ;
let val = Value ::parse ( " [] " ) ;
assert_eq! ( tmp , val ) ;
2023-07-29 18:47:25 +00:00
//
2023-07-15 16:26:36 +00:00
Ok ( ( ) )
}
2023-07-29 18:47:25 +00:00
//
// Permissions
//
async fn common_permissions_checks ( auth_enabled : bool ) {
let tests = vec! [
// Root level
( ( ( ) . into ( ) , Role ::Owner ) , ( " NS " , " DB " ) , true , " owner at root level should be able to delete a record " ) ,
( ( ( ) . into ( ) , Role ::Editor ) , ( " NS " , " DB " ) , true , " editor at root level should be able to delete a record " ) ,
( ( ( ) . into ( ) , Role ::Viewer ) , ( " NS " , " DB " ) , false , " viewer at root level should not be able to delete a record " ) ,
// Namespace level
( ( ( " NS " , ) . into ( ) , Role ::Owner ) , ( " NS " , " DB " ) , true , " owner at namespace level should be able to delete a record on its namespace " ) ,
( ( ( " NS " , ) . into ( ) , Role ::Owner ) , ( " OTHER_NS " , " DB " ) , false , " owner at namespace level should not be able to delete a record on another namespace " ) ,
( ( ( " NS " , ) . into ( ) , Role ::Editor ) , ( " NS " , " DB " ) , true , " editor at namespace level should be able to delete a record on its namespace " ) ,
( ( ( " NS " , ) . into ( ) , Role ::Editor ) , ( " OTHER_NS " , " DB " ) , false , " editor at namespace level should not be able to delete a record on another namespace " ) ,
( ( ( " NS " , ) . into ( ) , Role ::Viewer ) , ( " NS " , " DB " ) , false , " viewer at namespace level should not be able to delete a record on its namespace " ) ,
( ( ( " NS " , ) . into ( ) , Role ::Viewer ) , ( " OTHER_NS " , " DB " ) , false , " viewer at namespace level should not be able to delete a record on another namespace " ) ,
// Database level
( ( ( " NS " , " DB " ) . into ( ) , Role ::Owner ) , ( " NS " , " DB " ) , true , " owner at database level should be able to delete a record on its database " ) ,
( ( ( " NS " , " DB " ) . into ( ) , Role ::Owner ) , ( " NS " , " OTHER_DB " ) , false , " owner at database level should not be able to delete a record on another database " ) ,
( ( ( " NS " , " DB " ) . into ( ) , Role ::Owner ) , ( " OTHER_NS " , " DB " ) , false , " owner at database level should not be able to delete a record on another namespace even if the database name matches " ) ,
( ( ( " NS " , " DB " ) . into ( ) , Role ::Editor ) , ( " NS " , " DB " ) , true , " editor at database level should be able to delete a record on its database " ) ,
( ( ( " NS " , " DB " ) . into ( ) , Role ::Editor ) , ( " NS " , " OTHER_DB " ) , false , " editor at database level should not be able to delete a record on another database " ) ,
( ( ( " NS " , " DB " ) . into ( ) , Role ::Editor ) , ( " OTHER_NS " , " DB " ) , false , " editor at database level should not be able to delete a record on another namespace even if the database name matches " ) ,
( ( ( " NS " , " DB " ) . into ( ) , Role ::Viewer ) , ( " NS " , " DB " ) , false , " viewer at database level should not be able to delete a record on its database " ) ,
( ( ( " NS " , " DB " ) . into ( ) , Role ::Viewer ) , ( " NS " , " OTHER_DB " ) , false , " viewer at database level should not be able to delete a record on another database " ) ,
( ( ( " NS " , " DB " ) . into ( ) , Role ::Viewer ) , ( " OTHER_NS " , " DB " ) , false , " viewer at database level should not be able to delete a record on another namespace even if the database name matches " ) ,
] ;
let statement = " DELETE person:test " ;
for ( ( level , role ) , ( ns , db ) , should_succeed , msg ) in tests . into_iter ( ) {
let sess = Session ::for_level ( level , role ) . with_ns ( ns ) . with_db ( db ) ;
{
2023-08-30 18:01:30 +00:00
let ds = new_ds ( ) . await . unwrap ( ) . with_auth_enabled ( auth_enabled ) ;
2023-07-29 18:47:25 +00:00
let mut resp = ds
. execute ( " CREATE person:test " , & Session ::owner ( ) . with_ns ( " NS " ) . with_db ( " DB " ) , None )
. await
. unwrap ( ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! (
res . is_ok ( ) & & res . unwrap ( ) ! = Value ::parse ( " [] " ) ,
" unexpected error creating person record "
) ;
let mut resp = ds
. execute (
" CREATE person:test " ,
& Session ::owner ( ) . with_ns ( " OTHER_NS " ) . with_db ( " DB " ) ,
None ,
)
. await
. unwrap ( ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! (
res . is_ok ( ) & & res . unwrap ( ) ! = Value ::parse ( " [] " ) ,
" unexpected error creating person record "
) ;
let mut resp = ds
. execute (
" CREATE person:test " ,
& Session ::owner ( ) . with_ns ( " NS " ) . with_db ( " OTHER_DB " ) ,
None ,
)
. await
. unwrap ( ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! (
res . is_ok ( ) & & res . unwrap ( ) ! = Value ::parse ( " [] " ) ,
" unexpected error creating person record "
) ;
// Run the test
let mut resp = ds . execute ( statement , & sess , None ) . await . unwrap ( ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! ( res . is_ok ( ) , " delete should not fail " ) ;
if should_succeed {
// Verify the record has been deleted
let mut resp = ds
. execute (
" SELECT * FROM person:test " ,
& Session ::owner ( ) . with_ns ( " NS " ) . with_db ( " DB " ) ,
None ,
)
. await
. unwrap ( ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! ( res . is_ok ( ) & & res . unwrap ( ) = = Value ::parse ( " [] " ) , " {} " , msg ) ;
} else {
// Verify the record has not been deleted in any DB
let mut resp = ds
. execute (
" SELECT * FROM person:test " ,
& Session ::owner ( ) . with_ns ( " NS " ) . with_db ( " DB " ) ,
None ,
)
. await
. unwrap ( ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! ( res . is_ok ( ) & & res . unwrap ( ) ! = Value ::parse ( " [] " ) , " {} " , msg ) ;
let mut resp = ds
. execute (
" SELECT * FROM person:test " ,
& Session ::owner ( ) . with_ns ( " OTHER_NS " ) . with_db ( " DB " ) ,
None ,
)
. await
. unwrap ( ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! ( res . is_ok ( ) & & res . unwrap ( ) ! = Value ::parse ( " [] " ) , " {} " , msg ) ;
let mut resp = ds
. execute (
" SELECT * FROM person:test " ,
& Session ::owner ( ) . with_ns ( " NS " ) . with_db ( " OTHER_DB " ) ,
None ,
)
. await
. unwrap ( ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! ( res . is_ok ( ) & & res . unwrap ( ) ! = Value ::parse ( " [] " ) , " {} " , msg ) ;
}
}
}
}
#[ tokio::test ]
async fn check_permissions_auth_enabled ( ) {
let auth_enabled = true ;
//
// Test common scenarios
//
common_permissions_checks ( auth_enabled ) . await ;
//
// Test Anonymous user
//
let statement = " DELETE person:test " ;
// When the table exists but grants no permissions
{
2023-08-30 18:01:30 +00:00
let ds = new_ds ( ) . await . unwrap ( ) . with_auth_enabled ( auth_enabled ) ;
2023-07-29 18:47:25 +00:00
let mut resp = ds
. execute (
" DEFINE TABLE person PERMISSIONS NONE; CREATE person:test " ,
& Session ::owner ( ) . with_ns ( " NS " ) . with_db ( " DB " ) ,
None ,
)
. await
. unwrap ( ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! ( res . is_ok ( ) , " failed to create table: {:?} " , res ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! ( res . is_ok ( ) & & res . unwrap ( ) ! = Value ::parse ( " [] " ) , " {} " , " failed to create record " ) ;
let mut resp = ds
. execute ( statement , & Session ::default ( ) . with_ns ( " NS " ) . with_db ( " DB " ) , None )
. await
. unwrap ( ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! ( res . is_ok ( ) , " delete should succeed even if it doesn't really delete anything " ) ;
// Verify the record has not been deleted
let mut resp = ds
. execute (
" SELECT * FROM person:test " ,
& Session ::owner ( ) . with_ns ( " NS " ) . with_db ( " DB " ) ,
None ,
)
. await
. unwrap ( ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! (
res . is_ok ( ) & & res . unwrap ( ) ! = Value ::parse ( " [] " ) ,
" {} " ,
" anonymous user should not be able to delete a record if the table has no permissions "
) ;
}
// When the table exists and grants full permissions
{
2023-08-30 18:01:30 +00:00
let ds = new_ds ( ) . await . unwrap ( ) . with_auth_enabled ( auth_enabled ) ;
2023-07-29 18:47:25 +00:00
let mut resp = ds
. execute (
" DEFINE TABLE person PERMISSIONS FULL; CREATE person:test " ,
& Session ::owner ( ) . with_ns ( " NS " ) . with_db ( " DB " ) ,
None ,
)
. await
. unwrap ( ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! ( res . is_ok ( ) , " failed to create table: {:?} " , res ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! ( res . is_ok ( ) & & res . unwrap ( ) ! = Value ::parse ( " [] " ) , " {} " , " failed to create record " ) ;
let mut resp = ds
. execute ( statement , & Session ::default ( ) . with_ns ( " NS " ) . with_db ( " DB " ) , None )
. await
. unwrap ( ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! ( res . is_ok ( ) , " delete should succeed even if it doesn't really delete anything " ) ;
// Verify the record has been deleted
let mut resp = ds
. execute (
" SELECT * FROM person:test " ,
& Session ::owner ( ) . with_ns ( " NS " ) . with_db ( " DB " ) ,
None ,
)
. await
. unwrap ( ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! (
res . is_ok ( ) & & res . unwrap ( ) = = Value ::parse ( " [] " ) ,
" {} " ,
" anonymous user should be able to delete a record if the table has full permissions "
) ;
}
}
#[ tokio::test ]
async fn check_permissions_auth_disabled ( ) {
let auth_enabled = false ;
//
// Test common scenarios
//
common_permissions_checks ( auth_enabled ) . await ;
//
// Test Anonymous user
//
let statement = " DELETE person:test " ;
// When the table exists but grants no permissions
{
2023-08-30 18:01:30 +00:00
let ds = new_ds ( ) . await . unwrap ( ) . with_auth_enabled ( auth_enabled ) ;
2023-07-29 18:47:25 +00:00
let mut resp = ds
. execute (
" DEFINE TABLE person PERMISSIONS NONE; CREATE person:test; " ,
& Session ::owner ( ) . with_ns ( " NS " ) . with_db ( " DB " ) ,
None ,
)
. await
. unwrap ( ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! ( res . is_ok ( ) , " failed to create table: {:?} " , res ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! ( res . is_ok ( ) & & res . unwrap ( ) ! = Value ::parse ( " [] " ) , " {} " , " failed to create record " ) ;
let mut resp = ds
. execute ( statement , & Session ::default ( ) . with_ns ( " NS " ) . with_db ( " DB " ) , None )
. await
. unwrap ( ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! ( res . is_ok ( ) , " delete should succeed even if it doesn't really delete anything " ) ;
// Verify the record has been deleted
let mut resp = ds
. execute (
" SELECT * FROM person:test " ,
& Session ::owner ( ) . with_ns ( " NS " ) . with_db ( " DB " ) ,
None ,
)
. await
. unwrap ( ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! (
res . is_ok ( ) & & res . unwrap ( ) = = Value ::parse ( " [] " ) ,
" {} " ,
" anonymous user should be able to delete a record if the table has no permissions "
) ;
}
{
2023-08-30 18:01:30 +00:00
let ds = new_ds ( ) . await . unwrap ( ) . with_auth_enabled ( auth_enabled ) ;
2023-07-29 18:47:25 +00:00
// When the table exists and grants full permissions
let mut resp = ds
. execute (
" DEFINE TABLE person PERMISSIONS FULL; CREATE person:test; " ,
& Session ::owner ( ) . with_ns ( " NS " ) . with_db ( " DB " ) ,
None ,
)
. await
. unwrap ( ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! ( res . is_ok ( ) , " failed to create table: {:?} " , res ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! ( res . is_ok ( ) & & res . unwrap ( ) ! = Value ::parse ( " [] " ) , " {} " , " failed to create record " ) ;
let mut resp = ds
. execute ( statement , & Session ::default ( ) . with_ns ( " NS " ) . with_db ( " DB " ) , None )
. await
. unwrap ( ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! ( res . is_ok ( ) , " delete should succeed even if it doesn't really delete anything " ) ;
// Verify the record has been deleted
let mut resp = ds
. execute (
" SELECT * FROM person:test " ,
& Session ::owner ( ) . with_ns ( " NS " ) . with_db ( " DB " ) ,
None ,
)
. await
. unwrap ( ) ;
let res = resp . remove ( 0 ) . output ( ) ;
assert! (
res . is_ok ( ) & & res . unwrap ( ) = = Value ::parse ( " [] " ) ,
" {} " ,
" anonymous user should be able to delete a record if the table has full permissions "
) ;
}
}
2023-08-11 23:22:53 +00:00
#[ tokio::test ]
async fn delete_filtered_live_notification ( ) -> Result < ( ) , Error > {
2023-08-30 18:01:30 +00:00
let dbs = new_ds ( ) . await ? . with_notifications ( ) ;
2023-08-11 23:22:53 +00:00
let ses = Session ::owner ( ) . with_ns ( " test " ) . with_db ( " test " ) . with_rt ( true ) ;
2023-08-19 12:31:51 +00:00
let res = & mut dbs . execute ( " CREATE person:test_true SET condition = true " , & ses , None ) . await ? ;
assert_eq! ( res . len ( ) , 1 ) ;
// validate create response
2023-08-11 23:22:53 +00:00
let tmp = res . remove ( 0 ) . result ? ;
let expected_record = Value ::parse (
" [
{
2023-08-19 12:31:51 +00:00
id : person :test_true ,
condition : true ,
2023-08-11 23:22:53 +00:00
}
] " ,
) ;
assert_eq! ( tmp , expected_record ) ;
2023-08-19 12:31:51 +00:00
// Validate live query response
let res =
& mut dbs . execute ( " LIVE SELECT * FROM person WHERE condition = true " , & ses , None ) . await ? ;
assert_eq! ( res . len ( ) , 1 ) ;
2023-08-11 23:22:53 +00:00
let live_id = res . remove ( 0 ) . result ? ;
let live_id = match live_id {
Value ::Uuid ( id ) = > id ,
_ = > panic! ( " expected uuid " ) ,
} ;
2023-08-19 12:31:51 +00:00
// Validate delete response
let res = & mut dbs . execute ( " DELETE person:test_true " , & ses , None ) . await ? ;
assert_eq! ( res . len ( ) , 1 ) ;
2023-08-11 23:22:53 +00:00
let tmp = res . remove ( 0 ) . result ? ;
let val = Value ::parse ( " [] " ) ;
assert_eq! ( tmp , val ) ;
2023-08-19 12:31:51 +00:00
// Validate notification
2023-11-13 17:19:47 +00:00
let notifications = dbs . notifications ( ) . expect ( " expected notifications " ) ;
let notification = notifications . recv ( ) . await . unwrap ( ) ;
2023-08-11 23:22:53 +00:00
assert_eq! (
2023-11-13 17:19:47 +00:00
notification ,
2024-04-02 20:12:08 +00:00
Notification ::new (
live_id ,
Action ::Delete ,
2024-09-15 23:48:57 +00:00
Value ::Thing ( Thing ::from ( ( " person " , " test_true " ) ) ) ,
2024-04-02 20:12:08 +00:00
Value ::parse (
2023-11-13 17:19:47 +00:00
" {
id : person :test_true ,
condition : true ,
} "
) ,
2024-04-02 20:12:08 +00:00
)
2023-08-11 23:22:53 +00:00
) ;
Ok ( ( ) )
}
2024-02-21 10:04:45 +00:00
#[ tokio::test ]
2024-09-17 14:43:30 +00:00
async fn delete_with_permissions_no_select ( ) -> Result < ( ) , Error > {
2024-02-21 10:04:45 +00:00
let sql = "
DEFINE TABLE friends_with PERMISSIONS FOR delete WHERE in = $auth ;
CREATE user :john , user :mary ;
RELATE user :john ->friends_with :1 ->user :mary ;
RELATE user :mary ->friends_with :2 ->user :john ;
2024-09-17 14:43:30 +00:00
" ;
let dbs = new_ds ( ) . await ? . with_auth_enabled ( true ) ;
let ses = Session ::owner ( ) . with_ns ( " test " ) . with_db ( " test " ) ;
let res = & mut dbs . execute ( sql , & ses , None ) . await ? ;
assert_eq! ( res . len ( ) , 4 ) ;
//
let tmp = res . remove ( 0 ) . result ;
assert! ( tmp . is_ok ( ) ) ;
//
let tmp = res . remove ( 0 ) . result ;
assert! ( tmp . is_ok ( ) ) ;
//
let tmp = res . remove ( 0 ) . result ;
assert! ( tmp . is_ok ( ) ) ;
//
let tmp = res . remove ( 0 ) . result ;
assert! ( tmp . is_ok ( ) ) ;
//
let sql = "
DELETE friends_with :1 RETURN BEFORE ;
DELETE friends_with :2 RETURN BEFORE ;
" ;
let ses = Session ::for_record ( " test " , " test " , " test " , Thing ::from ( ( " user " , " john " ) ) . into ( ) ) ;
let res = & mut dbs . execute ( sql , & ses , None ) . await ? ;
assert_eq! ( res . len ( ) , 2 ) ;
//
let tmp = res . remove ( 0 ) . result ? ;
let val = Value ::parse ( " [] " ) ;
assert_eq! ( tmp , val ) ;
//
let tmp = res . remove ( 0 ) . result ? ;
let val = Value ::parse ( " [] " ) ;
assert_eq! ( tmp , val ) ;
//
Ok ( ( ) )
}
#[ tokio::test ]
async fn delete_with_permissions_with_select ( ) -> Result < ( ) , Error > {
let sql = "
DEFINE TABLE friends_with PERMISSIONS FOR select , delete WHERE in = $auth ;
CREATE user :john , user :mary ;
RELATE user :john ->friends_with :1 ->user :mary ;
RELATE user :mary ->friends_with :2 ->user :john ;
2024-02-21 10:04:45 +00:00
" ;
let dbs = new_ds ( ) . await ? . with_auth_enabled ( true ) ;
let ses = Session ::owner ( ) . with_ns ( " test " ) . with_db ( " test " ) ;
let res = & mut dbs . execute ( sql , & ses , None ) . await ? ;
assert_eq! ( res . len ( ) , 4 ) ;
//
let tmp = res . remove ( 0 ) . result ;
assert! ( tmp . is_ok ( ) ) ;
//
let tmp = res . remove ( 0 ) . result ;
assert! ( tmp . is_ok ( ) ) ;
//
let tmp = res . remove ( 0 ) . result ;
assert! ( tmp . is_ok ( ) ) ;
//
let tmp = res . remove ( 0 ) . result ;
assert! ( tmp . is_ok ( ) ) ;
//
let sql = "
DELETE friends_with :1 RETURN BEFORE ;
DELETE friends_with :2 RETURN BEFORE ;
" ;
2024-05-22 13:57:25 +00:00
let ses = Session ::for_record ( " test " , " test " , " test " , Thing ::from ( ( " user " , " john " ) ) . into ( ) ) ;
2024-02-21 10:04:45 +00:00
let res = & mut dbs . execute ( sql , & ses , None ) . await ? ;
assert_eq! ( res . len ( ) , 2 ) ;
//
let tmp = res . remove ( 0 ) . result ? ;
let val = Value ::parse (
" [
{
id : friends_with :1 ,
in : user :john ,
out : user :mary ,
} ,
] " ,
) ;
assert_eq! ( tmp , val ) ;
//
let tmp = res . remove ( 0 ) . result ? ;
let val = Value ::parse ( " [] " ) ;
assert_eq! ( tmp , val ) ;
//
Ok ( ( ) )
}