Hello list,
i've extended sqlite_driver to get access to the authorizer-feature of
sqlite. This is my first contact with the Zend-API and my last c skill
is more than a bit outdated.
Could someone review/cleanup my code to get it merged to the
distribution? Hint and comment welcome!
Thanks,
Mario Wolff
PS: Patch applys against 5.2.0!
Two comments about the patch:
- could you create a unified diff and post that instead?
- static local variable usage is not thread safe; state should be
stored in the pdo_dbh_t structure.
And one concern:
I deliberately left this feature unimplemented so far because the
authorization callback will happen "a lot", and frequent callbacks
into PHP script will make things slower.
I suggest that you adjust your patch to cache the function callback
information in the pdo_dbh_t to reduce some of that overhead, and run
some benchmarks for a simple authorizer function in PHP that always
returns true vs the same script with no authorizer, so that we get a
feel for what kind of impact that has for various common queries.
One other thing that is important is to ensure that the PHP
safe_mode/open_basedir checks have higher priority than the PHP script
callback. If safe_mode/open_basedir decide that the path is not
accessible, then the PHP script must not be able to override that.
However, if safe_mode/open_basedir say that access is ok, the PHP
script can optionally override that decision.
--Wez.
Hello list,
i've extended sqlite_driver to get access to the authorizer-feature of
sqlite. This is my first contact with the Zend-API and my last c skill
is more than a bit outdated.
Could someone review/cleanup my code to get it merged to the
distribution? Hint and comment welcome!
Thanks,
Mario WolffPS: Patch applys against 5.2.0!
@Wez: Whoop, just found you as maintainer in Pecl and send the same
request direct to you, sorry!
2006/11/16, Wez Furlong kingwez@gmail.com:
Two comments about the patch:
- could you create a unified diff and post that instead?
Here it is!
- static local variable usage is not thread safe; state should be
stored in the pdo_dbh_t structure.
Makes sense, even for cleanup. pdo_sqlite_request_shutdown cant free
the callback-zval right now.
And one concern:
I deliberately left this feature unimplemented so far because the
authorization callback will happen "a lot", and frequent callbacks
into PHP script will make things slower.
It is only called at compiletime of a query! I think thats not as
hard! Maybe it could be an optional feature?
I suggest that you adjust your patch to cache the function callback
information in the pdo_dbh_t to reduce some of that overhead, and run
some benchmarks for a simple authorizer function in PHP that always
returns true vs the same script with no authorizer, so that we get a
feel for what kind of impact that has for various common queries.
The Sqlite-Api keeps the user-authorizer in the userdata-void-pointer.
The static definition is only ment for clean up!
One other thing that is important is to ensure that the PHP
safe_mode/open_basedir checks have higher priority than the PHP script
callback. If safe_mode/open_basedir decide that the path is not
accessible, then the PHP script must not be able to override that.
However, if safe_mode/open_basedir say that access is ok, the PHP
script can optionally override that decision.
Have a look on my authorizer, the user-authorizer is only called if
your rules gives SQLITE_OK. The user-authorizer is called from a
default authorizer, not from the sqlite-api!
--Wez.
Regards,
Mario
Hello list,
i've extended sqlite_driver to get access to the authorizer-feature of
sqlite. This is my first contact with the Zend-API and my last c skill
is more than a bit outdated.
Could someone review/cleanup my code to get it merged to the
distribution? Hint and comment welcome!
Thanks,
Mario WolffPS: Patch applys against 5.2.0!
2006/11/16, Wez Furlong kingwez@gmail.com:
I suggest that you adjust your patch to cache the function callback
information in the pdo_dbh_t to reduce some of that overhead, and run
some benchmarks for a simple authorizer function in PHP that always
returns true vs the same script with no authorizer, so that we get a
feel for what kind of impact that has for various common queries.
Is the pdo_sqlite_db_handle not the better place to store? I'm not so
deep in the architecture to decide!
--Wez.
2006/11/16, Mario Wolff wolfshoehle@googlemail.com:
2006/11/16, Wez Furlong kingwez@gmail.com:
I suggest that you adjust your patch to cache the function callback
information in the pdo_dbh_t to reduce some of that overhead, and run
some benchmarks for a simple authorizer function in PHP that always
returns true vs the same script with no authorizer, so that we get a
feel for what kind of impact that has for various common queries.Is the pdo_sqlite_db_handle not the better place to store? I'm not so
deep in the architecture to decide!
New patches attached! static-stuff removed. user_authorizer cleanup done.
--Wez.
Short test script:
<?php
$data = array( 'one', 'two', 'three', 'four', 'five', 'six');
$db = new PDO( 'sqlite::memory:');
echo "register authorizer\n";
$db->sqliteSetAuthorizer('auth');
$db->exec( "CREATE TABLE strings( a)");
$insert = $db->prepare( 'INSERT INTO strings VALUES ( ?)');
foreach ( $data as $str) {
$insert->execute( array( $str));
}
$insert = null;
echo "unregister authorizer\n";
$db->sqliteSetAuthorizer();
function auth($type,$arga,$argb,$argc,$argd ){
echo "$type\t$arga\t$argb\t$argc\t$argd\n";
return true;
}
print_r( $db->query( 'SELECT sqlite_version( *);')->fetchAll( ));
?>
gives:
register authorizer
SQLITE_INSERT sqlite_master main
SQLITE_CREATE_TABLE strings main
SQLITE_UPDATE sqlite_master type main
SQLITE_UPDATE sqlite_master name main
SQLITE_UPDATE sqlite_master tbl_name main
SQLITE_UPDATE sqlite_master rootpage main
SQLITE_UPDATE sqlite_master sql main
SQLITE_READ sqlite_master ROWID main
SQLITE_READ sqlite_master name main
SQLITE_READ sqlite_master rootpage main
SQLITE_READ sqlite_master sql main
SQLITE_READ sqlite_master tbl_name main
SQLITE_INSERT strings main
unregister authorizer
Array
(
[0] => Array
(
[sqlite_version( *)] => 3.3.7
[0] => 3.3.7
)
)
I think it would be better to pass in the pdo_dbh_t as the autharg to
the C level callback and then use that to determine if any of the
expensive work needs to be done in the callback.
static int authorizer(....)
{
pdo_dbh_t *db;
/* keep the current safemode / basedir checks "cheap" and fast */
if (existing safe_mode and open_base dir return SQLITE_DENY) {
return SQLITE_DENY;
}
db = (pdo_dbh_t*)autharg;
if (db->user_authorizer) {
TSRMLS_FETCH();
...
free stuff
}
}
You should take a look at how the pdo_sqlite_fci stuff works and adopt
that for the authorizer callback, as it will help improve runtime
performance there.
It would also be best to register constants for the various SQLITE_XXX codes
rather than creating strings and having the PHP code check against strings.
This will also improve performance at runtime.
--Wez.
Short test script:
<?php
$data = array( 'one', 'two', 'three', 'four', 'five', 'six');
$db = new PDO( 'sqlite::memory:');
echo "register authorizer\n";
$db->sqliteSetAuthorizer('auth');
$db->exec( "CREATE TABLE strings( a)");
$insert = $db->prepare( 'INSERT INTO strings VALUES ( ?)');
foreach ( $data as $str) {$insert->execute( array( $str));
}
$insert = null;
echo "unregister authorizer\n";
$db->sqliteSetAuthorizer();function auth($type,$arga,$argb,$argc,$argd ){
echo "$type\t$arga\t$argb\t$argc\t$argd\n";
return true;
}
print_r( $db->query( 'SELECT sqlite_version( *);')->fetchAll( ));?>
gives:
register authorizer
SQLITE_INSERT sqlite_master main
SQLITE_CREATE_TABLE strings main
SQLITE_UPDATE sqlite_master type main
SQLITE_UPDATE sqlite_master name main
SQLITE_UPDATE sqlite_master tbl_name main
SQLITE_UPDATE sqlite_master rootpage main
SQLITE_UPDATE sqlite_master sql main
SQLITE_READ sqlite_master ROWID main
SQLITE_READ sqlite_master name main
SQLITE_READ sqlite_master rootpage main
SQLITE_READ sqlite_master sql main
SQLITE_READ sqlite_master tbl_name main
SQLITE_INSERT strings main
unregister authorizer
Array
(
[0] => Array
(
[sqlite_version( *)] => 3.3.7
[0] => 3.3.7
))
2006/11/16, Wez Furlong kingwez@gmail.com:
I think it would be better to pass in the pdo_dbh_t as the autharg to
the C level callback and then use that to determine if any of the
expensive work needs to be done in the callback.
Saves the sqlite_set_authorizer recall in SetAuthorizer, Looks simpler
and cleaner. -> done
static int authorizer(....)
{
pdo_dbh_t *db;/* keep the current safemode / basedir checks "cheap" and fast */
if (existing safe_mode and open_base dir return SQLITE_DENY) {
return SQLITE_DENY;
}db = (pdo_dbh_t*)autharg;
if (db->user_authorizer) {
TSRMLS_FETCH();
What is this? I'm not familiar with the hole php-stuff, sorry!
Done by:
-
pdo_dbh_t *dbh;
-
pdo_sqlite_db_handle *H;
...
-
dbh=(pdo_dbh_t*)autharg;
-
PDO_CONSTRUCT_CHECK;
-
H = (pdo_sqlite_db_handle *)dbh->driver_data;
-
if( H->user_authorizer ){
OK?
...
free stuff
}
}You should take a look at how the pdo_sqlite_fci stuff works and adopt
that for the authorizer callback, as it will help improve runtime
performance there.
Can't find usefull stuff to adopt. Can you hint me further?
It would also be best to register constants for the various SQLITE_XXX codes
rather than creating strings and having the PHP code check against strings.
This will also improve performance at runtime.
I like talking parameters! But you are right, performance comes first! -> done
Short test script:
Modified:
<?php
$data = array( 'one', 'two', 'three', 'four', 'five', 'six');
$db = new PDO( 'sqlite::memory:');
echo "register authorizer\n";
$db->sqliteSetAuthorizer('auth');
$db->exec( "CREATE TABLE strings( a)");
$insert = $db->prepare( 'INSERT INTO strings VALUES ( ?)');
foreach ( $data as $str) {
$insert->execute( array( $str));
}
$insert = null;
if( $delete = $db->prepare( 'DELETE FROM strings where a=?')){
if( $delete->execute(array( "six" ))){
echo "delete done!\n";
}
$delete = null;
}else{
echo "delete not prepared\n";
}
echo "unregister authorizer\n";
$db->sqliteSetAuthorizer();
if( $delete = $db->prepare( 'DELETE FROM strings where a=?')){
if( $delete->execute(array( "six" ))){
echo "delete done!\n";
}
$delete = null;
}else{
echo "delete not prepared\n";
}
function auth($type,$arga,$argb,$argc,$argd ){
echo "$type\t$arga\t$argb\t$argc\t$argd\n";
if( $type==SQLITE_DELETE ){
return SQLITE_DENY;
}
return SQLITE_OK;
}
print_r( $db->query( 'SELECT sqlite_version( *);')->fetchAll( ));
print_r( $db->query( 'SELECT * from strings;')->fetchAll( ));
?>
gives:
now:
register authorizer
18 sqlite_master main
2 strings main
23 sqlite_master type main
23 sqlite_master name main
23 sqlite_master tbl_name main
23 sqlite_master rootpage main
23 sqlite_master sql main
20 sqlite_master ROWID main
20 sqlite_master name main
20 sqlite_master rootpage main
20 sqlite_master sql main
20 sqlite_master tbl_name main
18 strings main
9 strings main
delete not prepared
unregister authorizer
delete done!
Array
(
[0] => Array
(
[sqlite_version( *)] => 3.3.7
[0] => 3.3.7
)
)
Array
(
[0] => Array
(
[a] => one
[0] => one
)
[1] => Array
(
[a] => two
[0] => two
)
[2] => Array
(
[a] => three
[0] => three
)
[3] => Array
(
[a] => four
[0] => four
)
[4] => Array
(
[a] => five
[0] => five
)
)
Regards,
Mario
Any suggestions or meanings on it?
Thanks,
Mario
2006/11/17, Mario Wolff wolfshoehle@googlemail.com:
2006/11/16, Wez Furlong kingwez@gmail.com:
I think it would be better to pass in the pdo_dbh_t as the autharg to
the C level callback and then use that to determine if any of the
expensive work needs to be done in the callback.Saves the sqlite_set_authorizer recall in SetAuthorizer, Looks simpler
and cleaner. -> donestatic int authorizer(....)
{
pdo_dbh_t *db;/* keep the current safemode / basedir checks "cheap" and fast */
if (existing safe_mode and open_base dir return SQLITE_DENY) {
return SQLITE_DENY;
}db = (pdo_dbh_t*)autharg;
if (db->user_authorizer) {
TSRMLS_FETCH();What is this? I'm not familiar with the hole php-stuff, sorry!
Done by:
pdo_dbh_t *dbh;
pdo_sqlite_db_handle *H;
...
dbh=(pdo_dbh_t*)autharg;
PDO_CONSTRUCT_CHECK;
H = (pdo_sqlite_db_handle *)dbh->driver_data;
if( H->user_authorizer ){
OK?
...
free stuff
}
}You should take a look at how the pdo_sqlite_fci stuff works and adopt
that for the authorizer callback, as it will help improve runtime
performance there.Can't find usefull stuff to adopt. Can you hint me further?
It would also be best to register constants for the various SQLITE_XXX codes
rather than creating strings and having the PHP code check against strings.
This will also improve performance at runtime.I like talking parameters! But you are right, performance comes first! -> done
Short test script:
Modified:
<?php
$data = array( 'one', 'two', 'three', 'four', 'five', 'six');
$db = new PDO( 'sqlite::memory:');
echo "register authorizer\n";
$db->sqliteSetAuthorizer('auth');
$db->exec( "CREATE TABLE strings( a)");
$insert = $db->prepare( 'INSERT INTO strings VALUES ( ?)');
foreach ( $data as $str) {$insert->execute( array( $str));
}
$insert = null;if( $delete = $db->prepare( 'DELETE FROM strings where a=?')){
if( $delete->execute(array( "six" ))){
echo "delete done!\n";
}
$delete = null;
}else{
echo "delete not prepared\n";
}echo "unregister authorizer\n";
$db->sqliteSetAuthorizer();if( $delete = $db->prepare( 'DELETE FROM strings where a=?')){
if( $delete->execute(array( "six" ))){
echo "delete done!\n";
}
$delete = null;
}else{
echo "delete not prepared\n";
}function auth($type,$arga,$argb,$argc,$argd ){
echo "$type\t$arga\t$argb\t$argc\t$argd\n";
if( $type==SQLITE_DELETE ){
return SQLITE_DENY;
}
return SQLITE_OK;
}
print_r( $db->query( 'SELECT sqlite_version( *);')->fetchAll( ));
print_r( $db->query( 'SELECT * from strings;')->fetchAll( ));?>
gives:
now:
register authorizer
18 sqlite_master main
2 strings main
23 sqlite_master type main
23 sqlite_master name main
23 sqlite_master tbl_name main
23 sqlite_master rootpage main
23 sqlite_master sql main
20 sqlite_master ROWID main
20 sqlite_master name main
20 sqlite_master rootpage main
20 sqlite_master sql main
20 sqlite_master tbl_name main
18 strings main
9 strings main
delete not prepared
unregister authorizer
delete done!
Array
(
[0] => Array
(
[sqlite_version( *)] => 3.3.7
[0] => 3.3.7
))
Array
(
[0] => Array
(
[a] => one
[0] => one
)[1] => Array
(
[a] => two
[0] => two
)[2] => Array
(
[a] => three
[0] => three
)[3] => Array
(
[a] => four
[0] => four
)[4] => Array
(
[a] => five
[0] => five
))
Regards,
Mario
Any suggestions or meanings on it?
Thanks,
Mario
Short test script:
<?php
$data = array( 'one', 'two', 'three', 'four', 'five', 'six');
$db = new PDO( 'sqlite::memory:');
echo "register authorizer\n";
$db->sqliteSetAuthorizer('auth');
$db->exec( "CREATE TABLE strings( a)");
$insert = $db->prepare( 'INSERT INTO strings VALUES ( ?)');
foreach ( $data as $str) {
$insert->execute( array( $str));
}
$insert = null;
if( $delete = $db->prepare( 'DELETE FROM strings where a=?')){
if( $delete->execute(array( "six" ))){
echo "delete done!\n";
}
$delete = null;
}else{
echo "delete not prepared\n";
}
echo "unregister authorizer\n";
$db->sqliteSetAuthorizer();
if( $delete = $db->prepare( 'DELETE FROM strings where a=?')){
if( $delete->execute(array( "six" ))){
echo "delete done!\n";
}
$delete = null;
}else{
echo "delete not prepared\n";
}
function auth($type,$arga,$argb,$argc,$argd ){
echo "$type\t$arga\t$argb\t$argc\t$argd\n";
if( $type==SQLITE_DELETE ){
return SQLITE_DENY;
}
return SQLITE_OK;
}
print_r( $db->query( 'SELECT sqlite_version( *);')->fetchAll( ));
print_r( $db->query( 'SELECT * from strings;')->fetchAll( ));
?>
gives:
register authorizer
18 sqlite_master main
2 strings main
23 sqlite_master type main
23 sqlite_master name main
23 sqlite_master tbl_name main
23 sqlite_master rootpage main
23 sqlite_master sql main
20 sqlite_master ROWID main
20 sqlite_master name main
20 sqlite_master rootpage main
20 sqlite_master sql main
20 sqlite_master tbl_name main
18 strings main
9 strings main
delete not prepared
unregister authorizer
delete done!
Array
(
[0] => Array
(
[sqlite_version( *)] => 3.3.7
[0] => 3.3.7
)
)
Array
(
[0] => Array
(
[a] => one
[0] => one
)
[1] => Array
(
[a] => two
[0] => two
)
[2] => Array
(
[a] => three
[0] => three
)
[3] => Array
(
[a] => four
[0] => four
)
[4] => Array
(
[a] => five
[0] => five
)
)
Hi Mario,
Sorry that I didn't get back to you--I often get too busy to follow up
on PHP related mail so you need to poke.
I'm currently doing some work on PDO and so I'll look at integrating
your patch this week; if I don't spot any problems, I'll commit it,
otherwise I'll get back to you to discuss it.
Thanks for putting in the effort!
--Wez.
Any suggestions or meanings on it?
Thanks,
MarioShort test script:
<?php
$data = array( 'one', 'two', 'three', 'four', 'five', 'six');
$db = new PDO( 'sqlite::memory:');
echo "register authorizer\n";
$db->sqliteSetAuthorizer('auth');
$db->exec( "CREATE TABLE strings( a)");
$insert = $db->prepare( 'INSERT INTO strings VALUES ( ?)');
foreach ( $data as $str) {
$insert->execute( array( $str));
}
$insert = null;if( $delete = $db->prepare( 'DELETE FROM strings where a=?')){
if( $delete->execute(array( "six" ))){
echo "delete done!\n";
}
$delete = null;
}else{
echo "delete not prepared\n";
}echo "unregister authorizer\n";
$db->sqliteSetAuthorizer();if( $delete = $db->prepare( 'DELETE FROM strings where a=?')){
if( $delete->execute(array( "six" ))){
echo "delete done!\n";
}
$delete = null;
}else{
echo "delete not prepared\n";
}function auth($type,$arga,$argb,$argc,$argd ){
echo "$type\t$arga\t$argb\t$argc\t$argd\n";
if( $type==SQLITE_DELETE ){
return SQLITE_DENY;
}
return SQLITE_OK;
}
print_r( $db->query( 'SELECT sqlite_version( *);')->fetchAll( ));
print_r( $db->query( 'SELECT * from strings;')->fetchAll( ));?>
gives:
register authorizer
18 sqlite_master main
2 strings main
23 sqlite_master type main
23 sqlite_master name main
23 sqlite_master tbl_name main
23 sqlite_master rootpage main
23 sqlite_master sql main
20 sqlite_master ROWID main
20 sqlite_master name main
20 sqlite_master rootpage main
20 sqlite_master sql main
20 sqlite_master tbl_name main
18 strings main
9 strings main
delete not prepared
unregister authorizer
delete done!
Array
(
[0] => Array
(
[sqlite_version( *)] => 3.3.7
[0] => 3.3.7
))
Array
(
[0] => Array
(
[a] => one
[0] => one
)[1] => Array
(
[a] => two
[0] => two
)[2] => Array
(
[a] => three
[0] => three
)[3] => Array
(
[a] => four
[0] => four
)[4] => Array
(
[a] => five
[0] => five
))