distribution/vendor/github.com/go-llsqlite/crawshaw/auth.go

360 lines
8.9 KiB
Go

package sqlite
// #include <stdint.h>
// #include <sqlite3.h>
// extern int go_sqlite_auth_tramp(uintptr_t, int, char*, char*, char*, char*);
// static int c_auth_tramp(void *userData, int action, const char* arg1, const char* arg2, const char* db, const char* trigger) {
// return go_sqlite_auth_tramp((uintptr_t)userData, action, (char*)arg1, (char*)arg2, (char*)db, (char*)trigger);
// }
// static int sqlite3_go_set_authorizer(sqlite3* conn, uintptr_t id) {
// return sqlite3_set_authorizer(conn, c_auth_tramp, (void*)id);
// }
import "C"
import (
"errors"
"fmt"
"strings"
"sync"
)
// An Authorizer is called during statement preparation to see whether an action
// is allowed by the application. See https://sqlite.org/c3ref/set_authorizer.html
type Authorizer interface {
Authorize(info ActionInfo) AuthResult
}
// SetAuthorizer registers an authorizer for the database connection.
// SetAuthorizer(nil) clears any authorizer previously set.
func (conn *Conn) SetAuthorizer(auth Authorizer) error {
if auth == nil {
if conn.authorizer == -1 {
return nil
}
conn.releaseAuthorizer()
res := C.sqlite3_set_authorizer(conn.conn, nil, nil)
return reserr("SetAuthorizer", "", "", res)
}
authFuncs.mu.Lock()
id := authFuncs.next
next := authFuncs.next + 1
if next < 0 {
authFuncs.mu.Unlock()
return errors.New("sqlite: authorizer function id overflow")
}
authFuncs.next = next
authFuncs.m[id] = auth
authFuncs.mu.Unlock()
res := C.sqlite3_go_set_authorizer(conn.conn, C.uintptr_t(id))
return reserr("SetAuthorizer", "", "", res)
}
func (conn *Conn) releaseAuthorizer() {
if conn.authorizer == -1 {
return
}
authFuncs.mu.Lock()
delete(authFuncs.m, conn.authorizer)
authFuncs.mu.Unlock()
conn.authorizer = -1
}
var authFuncs = struct {
mu sync.RWMutex
m map[int]Authorizer
next int
}{
m: make(map[int]Authorizer),
}
//export go_sqlite_auth_tramp
func go_sqlite_auth_tramp(id uintptr, action C.int, cArg1, cArg2 *C.char, cDB *C.char, cTrigger *C.char) C.int {
authFuncs.mu.RLock()
auth := authFuncs.m[int(id)]
authFuncs.mu.RUnlock()
var arg1, arg2, database, trigger string
if cArg1 != nil {
arg1 = C.GoString(cArg1)
}
if cArg2 != nil {
arg2 = C.GoString(cArg2)
}
if cDB != nil {
database = C.GoString(cDB)
}
if cTrigger != nil {
trigger = C.GoString(cTrigger)
}
return C.int(auth.Authorize(newActionInfo(OpType(action), arg1, arg2, database, trigger)))
}
// AuthorizeFunc is a function that implements Authorizer.
type AuthorizeFunc func(info ActionInfo) AuthResult
// Authorize calls f.
func (f AuthorizeFunc) Authorize(info ActionInfo) AuthResult {
return f(info)
}
// AuthResult is the result of a call to an Authorizer. The zero value is
// SQLITE_OK.
type AuthResult int
// Possible return values of an Authorizer.
const (
// Cause the entire SQL statement to be rejected with an error.
SQLITE_DENY = AuthResult(C.SQLITE_DENY)
// Disallow the specific action but allow the SQL statement to continue to
// be compiled.
SQLITE_IGNORE = AuthResult(C.SQLITE_IGNORE)
)
// String returns the C constant name of the result.
func (result AuthResult) String() string {
switch result {
default:
var buf [20]byte
return "SQLITE_UNKNOWN_AUTH_RESULT(" + string(itoa(buf[:], int64(result))) + ")"
case AuthResult(C.SQLITE_OK):
return "SQLITE_OK"
case SQLITE_DENY:
return "SQLITE_DENY"
case SQLITE_IGNORE:
return "SQLITE_IGNORE"
}
}
// ActionInfo holds information about an action to be authorized.
//
// Only the fields relevant to the Action are populated when this is passed to
// an Authorizer.
//
// https://sqlite.org/c3ref/c_alter_table.html
type ActionInfo struct {
Action OpType
Index string
Table string
Column string
Trigger string
View string
Function string
Pragma string
PragmaArg string
Operation string
Filename string
Module string
Database string
Savepoint string
InnerMostTrigger string
}
// newActionInfo returns an ActionInfo with the correct fields relevant to the
// action.
func newActionInfo(action OpType, arg1, arg2, database, trigger string) ActionInfo {
// We use the blank identifier with unused args below simply for visual
// consistency between the cases.
a := ActionInfo{Action: action, Database: database, InnerMostTrigger: trigger}
switch action {
case SQLITE_DROP_INDEX,
SQLITE_DROP_TEMP_INDEX,
SQLITE_CREATE_INDEX,
SQLITE_CREATE_TEMP_INDEX:
/* Index Name Table Name */
a.Index = arg1
a.Table = arg2
case SQLITE_DELETE,
SQLITE_DROP_TABLE,
SQLITE_DROP_TEMP_TABLE,
SQLITE_INSERT,
SQLITE_ANALYZE,
SQLITE_CREATE_TABLE,
SQLITE_CREATE_TEMP_TABLE:
/* Table Name NULL */
a.Table = arg1
_ = arg2
case SQLITE_CREATE_TEMP_TRIGGER,
SQLITE_CREATE_TRIGGER,
SQLITE_DROP_TEMP_TRIGGER,
SQLITE_DROP_TRIGGER:
/* Trigger Name Table Name */
a.Trigger = arg1
a.Table = arg2
case SQLITE_CREATE_TEMP_VIEW,
SQLITE_CREATE_VIEW,
SQLITE_DROP_TEMP_VIEW,
SQLITE_DROP_VIEW:
/* View Name NULL */
a.View = arg1
_ = arg2
case SQLITE_PRAGMA:
/* Pragma Name 1st arg or NULL */
a.Pragma = arg1
a.PragmaArg = arg2
case SQLITE_READ,
SQLITE_UPDATE:
/* Table Name Column Name */
a.Table = arg1
a.Column = arg2
case SQLITE_TRANSACTION:
/* Operation NULL */
a.Operation = arg1
_ = arg2
case SQLITE_ATTACH:
/* Filename NULL */
a.Filename = arg1
_ = arg2
case SQLITE_DETACH:
/* Database Name NULL */
a.Database = arg1
_ = arg2
case SQLITE_ALTER_TABLE:
/* Database Name Table Name */
a.Database = arg1
a.Table = arg2
case SQLITE_REINDEX:
/* Index Name NULL */
a.Index = arg1
_ = arg2
case SQLITE_CREATE_VTABLE,
SQLITE_DROP_VTABLE:
/* Table Name Module Name */
a.Table = arg1
a.Module = arg2
case SQLITE_FUNCTION:
/* NULL Function Name */
_ = arg1
a.Function = arg2
case SQLITE_SAVEPOINT:
/* Operation Savepoint Name */
a.Operation = arg1
a.Savepoint = arg2
case SQLITE_RECURSIVE,
SQLITE_SELECT:
/* NULL NULL */
_ = arg1
_ = arg2
case SQLITE_COPY:
/* No longer used */
default:
panic(fmt.Errorf("unknown action: %v", action))
}
return a
}
// String returns a string describing only the fields relevant to `a.Action`.
func (a ActionInfo) String() string {
switch a.Action {
case SQLITE_DROP_INDEX,
SQLITE_DROP_TEMP_INDEX,
SQLITE_CREATE_INDEX,
SQLITE_CREATE_TEMP_INDEX:
/* Index Name Table Name */
return fmt.Sprintf("%v: Index: %q Table: %q",
a.Action, a.Index, a.Table)
case SQLITE_DELETE,
SQLITE_DROP_TABLE,
SQLITE_DROP_TEMP_TABLE,
SQLITE_INSERT,
SQLITE_ANALYZE,
SQLITE_CREATE_TABLE,
SQLITE_CREATE_TEMP_TABLE:
/* Table Name NULL */
return fmt.Sprintf("%v: Table: %q", a.Action, a.Table)
case SQLITE_CREATE_TEMP_TRIGGER,
SQLITE_CREATE_TRIGGER,
SQLITE_DROP_TEMP_TRIGGER,
SQLITE_DROP_TRIGGER:
/* Trigger Name Table Name */
return fmt.Sprintf("%v: Trigger: %q Table: %q",
a.Action, a.Trigger, a.Table)
case SQLITE_CREATE_TEMP_VIEW,
SQLITE_CREATE_VIEW,
SQLITE_DROP_TEMP_VIEW,
SQLITE_DROP_VIEW:
/* View Name NULL */
return fmt.Sprintf("%v: View: %q", a.Action, a.View)
case SQLITE_PRAGMA:
/* Pragma Name 1st arg or NULL */
return fmt.Sprintf("%v: Pragma: %q",
a.Action, strings.TrimSpace(a.Pragma+" "+a.PragmaArg))
case SQLITE_READ,
SQLITE_UPDATE:
/* Table Name Column Name */
return fmt.Sprintf("%v: Table: %q Column: %q",
a.Action, a.Table, a.Column)
case SQLITE_TRANSACTION:
/* Operation NULL */
return fmt.Sprintf("%v: Operation: %q", a.Action, a.Operation)
case SQLITE_ATTACH:
/* Filename NULL */
return fmt.Sprintf("%v: Filename: %q", a.Action, a.Filename)
case SQLITE_DETACH:
/* Database Name NULL */
return fmt.Sprintf("%v: Database: %q", a.Action, a.Database)
case SQLITE_ALTER_TABLE:
/* Database Name Table Name */
return fmt.Sprintf("%v: Database: %q Table: %q",
a.Action, a.Database, a.Table)
case SQLITE_REINDEX:
/* Index Name NULL */
return fmt.Sprintf("%v: Index: %q", a.Action, a.Index)
case SQLITE_CREATE_VTABLE,
SQLITE_DROP_VTABLE:
/* Table Name Module Name */
return fmt.Sprintf("%v: Table: %q Module: %q",
a.Action, a.Table, a.Module)
case SQLITE_FUNCTION:
/* NULL Function Name */
return fmt.Sprintf("%v: Function: %q", a.Action, a.Function)
case SQLITE_SAVEPOINT:
/* Operation Savepoint Name */
return fmt.Sprintf("%v: Operation: %q Savepoint: %q",
a.Action, a.Operation, a.Savepoint)
case SQLITE_RECURSIVE,
SQLITE_SELECT:
/* NULL NULL */
return fmt.Sprintf("%v:", a.Action)
case SQLITE_COPY:
/* No longer used */
return fmt.Sprintf("%v:", a.Action)
default:
return fmt.Sprintf("unknown action: %v", a.Action)
}
}