318
vendor/zombiezen.com/go/sqlite/auth.go
generated
vendored
Normal file
318
vendor/zombiezen.com/go/sqlite/auth.go
generated
vendored
Normal file
@@ -0,0 +1,318 @@
|
||||
// Copyright 2021 Ross Light
|
||||
// SPDX-License-Identifier: ISC
|
||||
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"modernc.org/libc"
|
||||
lib "modernc.org/sqlite/lib"
|
||||
)
|
||||
|
||||
// An Authorizer is called during statement preparation to see whether an action
|
||||
// is allowed by the application. An Authorizer must not modify the database
|
||||
// connection, including by preparing statements.
|
||||
//
|
||||
// See https://sqlite.org/c3ref/set_authorizer.html for a longer explanation.
|
||||
type Authorizer interface {
|
||||
Authorize(Action) AuthResult
|
||||
}
|
||||
|
||||
// SetAuthorizer registers an authorizer for the database connection.
|
||||
// SetAuthorizer(nil) clears any authorizer previously set.
|
||||
func (c *Conn) SetAuthorizer(auth Authorizer) error {
|
||||
if c == nil {
|
||||
return fmt.Errorf("sqlite: set authorizer: nil connection")
|
||||
}
|
||||
if auth == nil {
|
||||
c.releaseAuthorizer()
|
||||
res := ResultCode(lib.Xsqlite3_set_authorizer(c.tls, c.conn, 0, 0))
|
||||
if err := reserr(res); err != nil {
|
||||
return fmt.Errorf("sqlite: set authorizer: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
authorizers.mu.Lock()
|
||||
if authorizers.m == nil {
|
||||
authorizers.m = make(map[uintptr]Authorizer)
|
||||
}
|
||||
authorizers.m[c.conn] = auth
|
||||
authorizers.mu.Unlock()
|
||||
|
||||
// The following is a conversion from function value to uintptr. It assumes
|
||||
// the memory representation described in https://golang.org/s/go11func.
|
||||
//
|
||||
// It does this by doing the following in order:
|
||||
// 1) Create a Go struct containing a pointer to a pointer to
|
||||
// authTrampoline. It is assumed that the pointer to authTrampoline will be
|
||||
// stored in the read-only data section and thus will not move.
|
||||
// 2) Convert the pointer to the Go struct to a pointer to uintptr through
|
||||
// unsafe.Pointer. This is permitted via Rule #1 of unsafe.Pointer.
|
||||
// 3) Dereference the pointer to uintptr to obtain the function value as a
|
||||
// uintptr. This is safe as long as function values are passed as pointers.
|
||||
xAuth := *(*uintptr)(unsafe.Pointer(&struct {
|
||||
f func(*libc.TLS, uintptr, int32, uintptr, uintptr, uintptr, uintptr) int32
|
||||
}{authTrampoline}))
|
||||
|
||||
res := ResultCode(lib.Xsqlite3_set_authorizer(c.tls, c.conn, xAuth, c.conn))
|
||||
if err := reserr(res); err != nil {
|
||||
return fmt.Errorf("sqlite: set authorizer: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) releaseAuthorizer() {
|
||||
authorizers.mu.Lock()
|
||||
delete(authorizers.m, c.conn)
|
||||
authorizers.mu.Unlock()
|
||||
}
|
||||
|
||||
var authorizers struct {
|
||||
mu sync.RWMutex
|
||||
m map[uintptr]Authorizer // sqlite3* -> Authorizer
|
||||
}
|
||||
|
||||
func authTrampoline(tls *libc.TLS, conn uintptr, op int32, cArg1, cArg2, cDB, cTrigger uintptr) int32 {
|
||||
authorizers.mu.RLock()
|
||||
auth := authorizers.m[conn]
|
||||
authorizers.mu.RUnlock()
|
||||
return int32(auth.Authorize(Action{
|
||||
op: OpType(op),
|
||||
arg1: libc.GoString(cArg1),
|
||||
arg2: libc.GoString(cArg2),
|
||||
database: libc.GoString(cDB),
|
||||
trigger: libc.GoString(cTrigger),
|
||||
}))
|
||||
}
|
||||
|
||||
// AuthorizeFunc is a function that implements Authorizer.
|
||||
type AuthorizeFunc func(Action) AuthResult
|
||||
|
||||
// Authorize calls f.
|
||||
func (f AuthorizeFunc) Authorize(action Action) AuthResult {
|
||||
return f(action)
|
||||
}
|
||||
|
||||
// AuthResult is the result of a call to an Authorizer. The zero value is
|
||||
// AuthResultOK.
|
||||
type AuthResult int32
|
||||
|
||||
// Possible return values from Authorize.
|
||||
const (
|
||||
// AuthResultOK allows the SQL statement to be compiled.
|
||||
AuthResultOK AuthResult = lib.SQLITE_OK
|
||||
// AuthResultDeny causes the entire SQL statement to be rejected with an error.
|
||||
AuthResultDeny AuthResult = lib.SQLITE_DENY
|
||||
// AuthResultIgnore disallows the specific action but allow the SQL statement
|
||||
// to continue to be compiled. For OpRead, this substitutes a NULL for the
|
||||
// column value. For OpDelete, the DELETE operation proceeds but the truncate
|
||||
// optimization is disabled and all rows are deleted individually.
|
||||
AuthResultIgnore AuthResult = lib.SQLITE_IGNORE
|
||||
)
|
||||
|
||||
// String returns the C constant name of the result.
|
||||
func (result AuthResult) String() string {
|
||||
switch result {
|
||||
case AuthResultOK:
|
||||
return "SQLITE_OK"
|
||||
case AuthResultDeny:
|
||||
return "SQLITE_DENY"
|
||||
case AuthResultIgnore:
|
||||
return "SQLITE_IGNORE"
|
||||
default:
|
||||
return fmt.Sprintf("AuthResult(%d)", int32(result))
|
||||
}
|
||||
}
|
||||
|
||||
// Action represents an action to be authorized.
|
||||
type Action struct {
|
||||
op OpType
|
||||
arg1 string
|
||||
arg2 string
|
||||
database string
|
||||
trigger string
|
||||
}
|
||||
|
||||
// Mapping of argument position to concept at:
|
||||
// https://sqlite.org/c3ref/c_alter_table.html
|
||||
|
||||
// Type returns the type of action being authorized.
|
||||
func (action Action) Type() OpType {
|
||||
return action.op
|
||||
}
|
||||
|
||||
// Accessor returns the name of the inner-most trigger or view that is
|
||||
// responsible for the access attempt or the empty string if this access attempt
|
||||
// is directly from top-level SQL code.
|
||||
func (action Action) Accessor() string {
|
||||
return action.trigger
|
||||
}
|
||||
|
||||
// Database returns the name of the database (e.g. "main", "temp", etc.) this
|
||||
// action affects or the empty string if not applicable.
|
||||
func (action Action) Database() string {
|
||||
switch action.op {
|
||||
case OpDetach, OpAlterTable:
|
||||
return action.arg1
|
||||
default:
|
||||
return action.database
|
||||
}
|
||||
}
|
||||
|
||||
// Index returns the name of the index this action affects or the empty string
|
||||
// if not applicable.
|
||||
func (action Action) Index() string {
|
||||
switch action.op {
|
||||
case OpCreateIndex, OpCreateTempIndex, OpDropIndex, OpDropTempIndex, OpReindex:
|
||||
return action.arg1
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// Table returns the name of the table this action affects or the empty string
|
||||
// if not applicable.
|
||||
func (action Action) Table() string {
|
||||
switch action.op {
|
||||
case OpCreateTable, OpCreateTempTable, OpDelete, OpDropTable, OpDropTempTable, OpInsert, OpRead, OpUpdate, OpAnalyze, OpCreateVTable, OpDropVTable:
|
||||
return action.arg1
|
||||
case OpCreateIndex, OpCreateTempIndex, OpCreateTempTrigger, OpCreateTrigger, OpDropIndex, OpDropTempIndex, OpDropTempTrigger, OpDropTrigger, OpAlterTable:
|
||||
return action.arg2
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger returns the name of the trigger this action affects or the empty
|
||||
// string if not applicable.
|
||||
func (action Action) Trigger() string {
|
||||
switch action.op {
|
||||
case OpCreateTempTrigger, OpCreateTrigger, OpDropTempTrigger, OpDropTrigger:
|
||||
return action.arg1
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// View returns the name of the view this action affects or the empty string
|
||||
// if not applicable.
|
||||
func (action Action) View() string {
|
||||
switch action.op {
|
||||
case OpCreateTempView, OpCreateView, OpDropTempView, OpDropView:
|
||||
return action.arg1
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// Pragma returns the name of the action's PRAGMA command or the empty string
|
||||
// if the action does not represent a PRAGMA command.
|
||||
// See https://sqlite.org/pragma.html#toc for a list of possible values.
|
||||
func (action Action) Pragma() string {
|
||||
if action.op != OpPragma {
|
||||
return ""
|
||||
}
|
||||
return action.arg1
|
||||
}
|
||||
|
||||
// PragmaArg returns the argument to the PRAGMA command or the empty string if
|
||||
// the action does not represent a PRAGMA command or the PRAGMA command does not
|
||||
// take an argument.
|
||||
func (action Action) PragmaArg() string {
|
||||
if action.op != OpPragma {
|
||||
return ""
|
||||
}
|
||||
return action.arg2
|
||||
}
|
||||
|
||||
// Column returns the name of the column this action affects or the empty string
|
||||
// if not applicable. For OpRead actions, this will return the empty string if a
|
||||
// table is referenced but no column values are extracted from that table
|
||||
// (e.g. a query like "SELECT COUNT(*) FROM tab").
|
||||
func (action Action) Column() string {
|
||||
switch action.op {
|
||||
case OpRead, OpUpdate:
|
||||
return action.arg2
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// Operation returns one of "BEGIN", "COMMIT", "RELEASE", or "ROLLBACK" for a
|
||||
// transaction or savepoint statement or the empty string otherwise.
|
||||
func (action Action) Operation() string {
|
||||
switch action.op {
|
||||
case OpTransaction, OpSavepoint:
|
||||
return action.arg1
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// File returns the name of the file being ATTACHed or the empty string if the
|
||||
// action does not represent an ATTACH DATABASE statement.
|
||||
func (action Action) File() string {
|
||||
if action.op != OpAttach {
|
||||
return ""
|
||||
}
|
||||
return action.arg1
|
||||
}
|
||||
|
||||
// Module returns the module name given to the virtual table statement or the
|
||||
// empty string if the action does not represent a CREATE VIRTUAL TABLE or
|
||||
// DROP VIRTUAL TABLE statement.
|
||||
func (action Action) Module() string {
|
||||
switch action.op {
|
||||
case OpCreateVTable, OpDropVTable:
|
||||
return action.arg2
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// Savepoint returns the name given to the SAVEPOINT statement or the empty
|
||||
// string if the action does not represent a SAVEPOINT statement.
|
||||
func (action Action) Savepoint() string {
|
||||
if action.op != OpSavepoint {
|
||||
return ""
|
||||
}
|
||||
return action.arg2
|
||||
}
|
||||
|
||||
// String returns a debugging representation of the action.
|
||||
func (action Action) String() string {
|
||||
sb := new(strings.Builder)
|
||||
sb.WriteString(action.op.String())
|
||||
params := []struct {
|
||||
name, value string
|
||||
}{
|
||||
{"database", action.Database()},
|
||||
{"file", action.File()},
|
||||
{"trigger", action.Trigger()},
|
||||
{"index", action.Index()},
|
||||
{"table", action.Table()},
|
||||
{"view", action.View()},
|
||||
{"module", action.Module()},
|
||||
{"column", action.Column()},
|
||||
|
||||
{"operation", action.Operation()},
|
||||
{"savepoint", action.Savepoint()},
|
||||
|
||||
{"pragma", action.Pragma()},
|
||||
{"arg", action.PragmaArg()},
|
||||
}
|
||||
for _, p := range params {
|
||||
if p.value != "" {
|
||||
sb.WriteString(" ")
|
||||
sb.WriteString(p.name)
|
||||
sb.WriteString(":")
|
||||
sb.WriteString(p.value)
|
||||
}
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
Reference in New Issue
Block a user