From 37cb291e80bbd285e0784d4af4ef0284efc060ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Marsza=C5=82ek?= Date: Sat, 13 Apr 2019 22:46:01 +0200 Subject: [PATCH] implementing AuthorizerFunc --- sqlite3/sqlite3.go | 29 +++++++++++++++++++++++------ sqlite3/sqlite3_test.go | 30 ++++++++++++++++++++++++++++++ sqlite3/util.go | 10 ++++++++++ 3 files changed, 63 insertions(+), 6 deletions(-) diff --git a/sqlite3/sqlite3.go b/sqlite3/sqlite3.go index e1e9437..ad6b122 100644 --- a/sqlite3/sqlite3.go +++ b/sqlite3/sqlite3.go @@ -29,7 +29,6 @@ package sqlite3 #cgo CFLAGS: -DSQLITE_ENABLE_STAT4=1 #cgo CFLAGS: -DSQLITE_ENABLE_UNLOCK_NOTIFY #cgo CFLAGS: -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 -#cgo CFLAGS: -DSQLITE_OMIT_AUTHORIZATION=1 #cgo CFLAGS: -DSQLITE_OMIT_AUTOINIT=1 #cgo CFLAGS: -DSQLITE_OMIT_DEPRECATED=1 #cgo CFLAGS: -DSQLITE_OMIT_PROGRESS_CALLBACK=1 @@ -102,12 +101,13 @@ int go_busy_handler(void*,int); int go_commit_hook(void*); void go_rollback_hook(void*); void go_update_hook(void* data, int op,const char *db, const char *tbl, sqlite3_int64 row); +int go_set_authorizer(void* data, int op, const char *arg1, const char *arg2, const char *db, const char *entity); SET(busy_handler) SET(commit_hook) SET(rollback_hook) SET(update_hook) - +SET(set_authorizer) // A pointer to an instance of this structure is passed as the user-context // pointer when registering for an unlock-notify callback. @@ -259,6 +259,7 @@ var busyRegistry = newRegistry() var commitRegistry = newRegistry() var rollbackRegistry = newRegistry() var updateRegistry = newRegistry() +var authorizerRegistry = newRegistry() func init() { // Initialize SQLite (required with SQLITE_OMIT_AUTOINIT). @@ -280,10 +281,11 @@ func init() { type Conn struct { db *C.sqlite3 - busyIdx int - commitIdx int - rollbackIdx int - updateIdx int + busyIdx int + commitIdx int + rollbackIdx int + updateIdx int + authorizerIdx int } // Open creates a new connection to a SQLite database. The name can be 1) a path @@ -1352,3 +1354,18 @@ func (c *Conn) UpdateFunc(f UpdateFunc) (prev UpdateFunc) { prev, _ = updateRegistry.unregister(prevIdx).(UpdateFunc) return } + +// AuthorizerFunc registers a function that is invoked by SQLite During sql +// statement compilation. Function can return sqlite3.OK to accept statement, +// sqlite3.IGNORE to disallow specyfic action, but allow further statement +// processing, or sqlite3.DENY to deny action completly and stop processing. +// +// [https://www.sqlite.org/c3ref/set_authorizer.html] +func (c *Conn) AuthorizerFunc(f AuthorizerFunc) (prev AuthorizerFunc) { + idx := authorizerRegistry.register(f) + prevIdx := c.authorizerIdx + c.authorizerIdx = idx + C.set_set_authorizer(c.db, unsafe.Pointer(&c.authorizerIdx), cBool(f != nil)) + prev, _ = authorizerRegistry.unregister(prevIdx).(AuthorizerFunc) + return +} diff --git a/sqlite3/sqlite3_test.go b/sqlite3/sqlite3_test.go index 084dec5..9b36d94 100644 --- a/sqlite3/sqlite3_test.go +++ b/sqlite3/sqlite3_test.go @@ -1034,6 +1034,36 @@ func TestUpdateHandler(T *testing.T) { verify(&update{DELETE, "main", "x", 2}) } +func TestAuthorizerHandler(T *testing.T) { + t := begin(T) + defer t.skipRestIfFailed() + + c := t.open(":memory:") + defer t.close(c) + t.exec(c, "CREATE TABLE x(a)") + + type authorizer struct { + op int + arg1, arg2, db, entity string + } + var have *authorizer + verify := func(want *authorizer) { + if !reflect.DeepEqual(have, want) { + t.Fatalf(cl("verify() expected %v; got %v"), want, have) + } + } + c.AuthorizerFunc(func(op int, arg1, arg2, db, entity RawString) int { + have = &authorizer{op, arg1.Copy(), arg2.Copy(), db.Copy(), entity.Copy()} + return OK + }) + + t.exec(c, "INSERT INTO x VALUES(1)") + verify(&authorizer{INSERT, "x", "", "main", ""}) + + t.exec(c, "SELECT * FROM x") + verify(&authorizer{READ, "x", "a", "main", ""}) +} + func TestBusyHandler(T *testing.T) { t := begin(T) diff --git a/sqlite3/util.go b/sqlite3/util.go index 69c9f50..75fae77 100644 --- a/sqlite3/util.go +++ b/sqlite3/util.go @@ -106,6 +106,9 @@ type RollbackFunc func() // inserted, or deleted. type UpdateFunc func(op int, db, tbl RawString, row int64) +// AuthorizerFunc is a callback function invoked by SQLite when statement is compiled. +type AuthorizerFunc func(op int, arg1, arg2, db, entity RawString) int + // Error is returned for all SQLite API result codes other than OK, ROW, and // DONE. type Error struct { @@ -383,3 +386,10 @@ func go_update_hook(data unsafe.Pointer, op C.int, db, tbl *C.char, row C.sqlite fn := updateRegistry.lookup(idx).(UpdateFunc) fn(int(op), raw(goStr(db)), raw(goStr(tbl)), int64(row)) } + +//export go_set_authorizer +func go_set_authorizer(data unsafe.Pointer, op C.int, arg1, arg2, db, entity *C.char) C.int { + idx := *(*int)(data) + fn := authorizerRegistry.lookup(idx).(AuthorizerFunc) + return C.int(fn(int(op), raw(goStr(arg1)), raw(goStr(arg2)), raw(goStr(db)), raw(goStr(entity)))) +}