# 2013 May 8 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # This file contains tests for the sqlite4_authorizer_push() and # sqlite4_authorizer_pop() API functions. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl set testprefix auth4 ifcapable !auth { finish_test ; return } #-------------------------------------------------------------------- # Test cases auth4-1.* test that when there are multiple authorizers # on the stack, they are invoked in order from most to least recently # added until all have been invoked or one of them returns other than # SQLITE4_OK. # do_execsql_test 1.0 { CREATE TABLE t1(x, y); INSERT INTO t1 VALUES(1, 'one'); INSERT INTO t1 VALUES(2, 'two'); } proc auth_callback {id code z1 z2 z3 z4} { if {$code == "SQLITE4_READ" && $z1=="t1" && $z2=="y"} { incr ::NAUTH return [lindex $::AUTH $id] } return SQLITE4_OK } sqlite4_authorizer_push db {auth_callback 2} sqlite4_authorizer_push db {auth_callback 1} sqlite4_authorizer_push db {auth_callback 0} foreach {tn codes ncall res} { 1 {SQLITE4_OK SQLITE4_OK SQLITE4_OK} 3 {0 {1 one 2 two}} 2 {SQLITE4_OK SQLITE4_OK SQLITE4_DENY} 3 {1 {access to t1.y is prohibited}} 3 {SQLITE4_DENY SQLITE4_OK SQLITE4_OK} 1 {1 {access to t1.y is prohibited}} 4 {SQLITE4_OK SQLITE4_DENY SQLITE4_OK} 2 {1 {access to t1.y is prohibited}} 5 {SQLITE4_OK SQLITE4_OK SQLITE4_IGNORE} 3 {0 {1 {} 2 {}}} 6 {SQLITE4_IGNORE SQLITE4_OK SQLITE4_OK} 1 {0 {1 {} 2 {}}} 7 {SQLITE4_OK SQLITE4_IGNORE SQLITE4_OK} 2 {0 {1 {} 2 {}}} 8 {SQLITE4_OK SQLITE4_OK SQLITE4_ALLOW} 3 {0 {1 one 2 two}} 9 {SQLITE4_ALLOW SQLITE4_OK SQLITE4_OK} 1 {0 {1 one 2 two}} 10 {SQLITE4_OK SQLITE4_ALLOW SQLITE4_OK} 2 {0 {1 one 2 two}} } { db cache flush set ::AUTH $codes set ::NAUTH 0 do_catchsql_test 1.$tn.1 { SELECT * FROM t1; } $res do_test 1.$tn.2 { set ::NAUTH } $ncall } sqlite4_authorizer_pop db sqlite4_authorizer_pop db sqlite4_authorizer_pop db #-------------------------------------------------------------------- # Test cases auth4-2.* test that the push and pop operations seem to # work correctly. # set ::STACK [list] proc auth_callback {id code z1 z2 z3 z4} { if {$code == "SQLITE4_READ" && $z1=="t1" && $z2=="y"} { lappend ::AUTH $id } return SQLITE4_OK } proc test_stack {} { set ::AUTH [list] db eval { SELECT * FROM t1 } set ::AUTH } proc push {id} { set ::STACK [concat $id $::STACK] sqlite4_authorizer_push db [list auth_callback $id] } proc pop {} { set ::STACK [lrange $::STACK 1 end] catch { sqlite4_authorizer_pop db } } do_execsql_test 2.0 { DROP TABLE IF EXISTS t1; CREATE TABLE t1(x, y); INSERT INTO t1 VALUES(1, 'one'); } for {set i 1} {$i <= 100} {incr i} { if { int(rand()*2.0) } { pop } else { push [expr int(rand() * 500.0)] } do_test 2.$i { test_stack } $::STACK } #-------------------------------------------------------------------- # Test that sqlite4_authorizer_pop() returns an error if the stack is # empty when it is called. # reset_db do_test 3.1 { list [catch { sqlite4_authorizer_pop db } msg] $msg } {1 SQLITE4_ERROR} do_test 3.2 { sqlite4_authorizer_push db xyz sqlite4_authorizer_pop db } {} #-------------------------------------------------------------------- # Test that authorizer destructors are invoked under the following # circumstances: # # 1. When an authorizer is popped from the stack, # 2. When the database connection is closed, and # 3. When an error occurs within the sqlite4_authorizer_push() call. # reset_db do_test 4.1.1 { set ::xyz_destroyed 0 sqlite4_authorizer_push db xyz {incr ::xyz_destroyed} set ::xyz_destroyed } {0} do_test 4.1.2 { sqlite4_authorizer_pop db set ::xyz_destroyed } {1} do_test 4.1.3 { set ::abc_destroyed 0 set ::xyz_destroyed 0 sqlite4_authorizer_push db abc {incr ::abc_destroyed} sqlite4_authorizer_push db xyz {incr ::xyz_destroyed} list $::abc_destroyed $::xyz_destroyed } {0 0} do_test 4.1.4 { sqlite4_authorizer_pop db list $::abc_destroyed $::xyz_destroyed } {0 1} do_test 4.1.5 { sqlite4_authorizer_pop db list $::abc_destroyed $::xyz_destroyed } {1 1} do_test 4.1.6 { db close list $::abc_destroyed $::xyz_destroyed } {1 1} reset_db do_test 4.2.1 { set ::abc_destroyed 0 set ::xyz_destroyed 0 sqlite4_authorizer_push db abc {incr ::abc_destroyed} sqlite4_authorizer_push db xyz {incr ::xyz_destroyed} list $::abc_destroyed $::xyz_destroyed } {0 0} do_test 4.2.2 { db close list $::abc_destroyed $::xyz_destroyed } {1 1} # Normally we don't like to mix [do_faultsim_test] and friends with # ordinary (non fault-injection) tests. But this one is OK because # it runs very fast - sqlite4_authorizer_push() only calls malloc() once. # do_faultsim_test 4.3 -faults oom* -prep { reset_db set ::abc_destroyed 0 } -body { sqlite4_authorizer_push db abc {incr ::abc_destroyed} } -test { faultsim_test_result {0 {}} {1 SQLITE4_NOMEM} if {$testrc && $::abc_destroyed!=1} {error "destructor not invoked"} if {$testrc==0 && $::abc_destroyed!=0} {error "destructor was invoked"} } finish_test