Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/go.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
go: ['1.19', '1.20', '1.21']
go: ['1.22', '1.23', '1.24']
fail-fast: false
env:
OS: ${{ matrix.os }}
Expand Down Expand Up @@ -44,7 +44,7 @@ jobs:
run: go-acc . -- -race -v -tags "libsqlite3"

- name: 'Tags: full'
run: go-acc . -- -race -v -tags "sqlite_allow_uri_authority sqlite_app_armor sqlite_column_metadata sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_math_functions sqlite_os_trace sqlite_preupdate_hook sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_unlock_notify sqlite_vacuum_incr sqlite_vtable"
run: go-acc . -- -race -v -tags "sqlite_allow_uri_authority sqlite_app_armor sqlite_column_metadata sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_math_functions sqlite_os_trace sqlite_preupdate_hook sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_unlock_notify sqlite_userauth sqlite_vacuum_incr sqlite_vtable"

- name: 'Tags: vacuum'
run: go-acc . -- -race -v -tags "sqlite_vacuum_full"
Expand All @@ -64,7 +64,7 @@ jobs:

strategy:
matrix:
go: ['1.19', '1.20', '1.21']
go: ['1.22', '1.23', '1.24']
fail-fast: false
env:
OS: windows-latest
Expand Down Expand Up @@ -99,7 +99,7 @@ jobs:
- name: 'Tags: full'
run: |
echo 'skip this test'
echo go build -race -v -tags "sqlite_allow_uri_authority sqlite_app_armor sqlite_column_metadata sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_math_functions sqlite_preupdate_hook sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_unlock_notify sqlite_vacuum_incr sqlite_vtable"
echo go build -race -v -tags "sqlite_allow_uri_authority sqlite_app_armor sqlite_column_metadata sqlite_foreign_keys sqlite_fts5 sqlite_icu sqlite_introspect sqlite_json sqlite_math_functions sqlite_preupdate_hook sqlite_secure_delete sqlite_see sqlite_stat4 sqlite_trace sqlite_unlock_notify sqlite_userauth sqlite_vacuum_incr sqlite_vtable"
shell: msys2 {0}

- name: 'Tags: vacuum'
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,8 @@ For example the TDM-GCC Toolchain can be found [here](https://jmeubank.github.io

# User Authentication

***This is deprecated***

This package supports the SQLite User Authentication module.

## Compile
Expand Down
5 changes: 4 additions & 1 deletion error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ func TestCorruptDbErrors(t *testing.T) {
_, err = db.Exec("drop table foo")
}

sqliteErr := err.(Error)
sqliteErr, ok := err.(Error)
if !ok {
t.Fatal(err)
}
if sqliteErr.Code != ErrNotADB {
t.Error("wrong error code for corrupted DB")
}
Expand Down
8 changes: 5 additions & 3 deletions sqlite3.go
Original file line number Diff line number Diff line change
Expand Up @@ -1568,9 +1568,11 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
}
}

// Preform Authentication
if err := conn.Authenticate(authUser, authPass); err != nil {
return nil, err
if conn.AuthEnabled() {
Copy link
Collaborator

@rittneje rittneje Jul 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we can add this check. Now AuthEnabled() always returns false even if you use the build tag. Therefore, anyone who had been relying on it just would skip auth instead of getting the error.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If enable sqlite_userauth tag, sqlite3 driver always fail since conn.Authenticate return error eventthough user/pass is not set. This means declaring to developers not to use sqlite_userauth. Is this what you intended?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. If the user doesn't need this extension, then the fix is simple - stop passing the build tag they don't need.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please take a look.

// Preform Authentication
if err := conn.Authenticate(authUser, authPass); err != nil {
return nil, err
}
}
Copy link
Preview

Copilot AI Jul 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This conditional check is unnecessary since AuthEnabled() now always returns false. The authentication code inside this block will never execute, making it unreachable code.

Suggested change
if conn.AuthEnabled() {
// Preform Authentication
if err := conn.Authenticate(authUser, authPass); err != nil {
return nil, err
}
}
// Authentication is disabled as AuthEnabled() always returns false.

Copilot uses AI. Check for mistakes.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, we need to remain this code for users who uses Authenticate method.


// Register: authenticate
Expand Down
148 changes: 10 additions & 138 deletions sqlite3_opt_userauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,53 +16,10 @@ package sqlite3
#else
#include <sqlite3.h>
#endif
#include <stdlib.h>

static int
_sqlite3_user_authenticate(sqlite3* db, const char* zUsername, const char* aPW, int nPW)
{
return sqlite3_user_authenticate(db, zUsername, aPW, nPW);
}

static int
_sqlite3_user_add(sqlite3* db, const char* zUsername, const char* aPW, int nPW, int isAdmin)
{
return sqlite3_user_add(db, zUsername, aPW, nPW, isAdmin);
}

static int
_sqlite3_user_change(sqlite3* db, const char* zUsername, const char* aPW, int nPW, int isAdmin)
{
return sqlite3_user_change(db, zUsername, aPW, nPW, isAdmin);
}

static int
_sqlite3_user_delete(sqlite3* db, const char* zUsername)
{
return sqlite3_user_delete(db, zUsername);
}

static int
_sqlite3_auth_enabled(sqlite3* db)
{
int exists = -1;

sqlite3_stmt *stmt;
sqlite3_prepare_v2(db, "select count(type) from sqlite_master WHERE type='table' and name='sqlite_user';", -1, &stmt, NULL);

while ( sqlite3_step(stmt) == SQLITE_ROW) {
exists = sqlite3_column_int(stmt, 0);
}

sqlite3_finalize(stmt);

return exists;
}
*/
import "C"
import (
"errors"
"unsafe"
)

const (
Expand All @@ -88,15 +45,7 @@ var (
// If the SQLITE_USER table is not present in the database file, then
// this interface is a harmless no-op returning SQLITE_OK.
func (c *SQLiteConn) Authenticate(username, password string) error {
rv := c.authenticate(username, password)
switch rv {
case C.SQLITE_ERROR, C.SQLITE_AUTH:
return ErrUnauthorized
case C.SQLITE_OK:
return nil
default:
return c.lastError()
}
return ErrUnauthorized
}

// authenticate provides the actual authentication to SQLite.
Expand All @@ -109,17 +58,7 @@ func (c *SQLiteConn) Authenticate(username, password string) error {
// C.SQLITE_ERROR (1)
// C.SQLITE_AUTH (23)
func (c *SQLiteConn) authenticate(username, password string) int {
// Allocate C Variables
cuser := C.CString(username)
cpass := C.CString(password)

// Free C Variables
defer func() {
C.free(unsafe.Pointer(cuser))
C.free(unsafe.Pointer(cpass))
}()

return int(C._sqlite3_user_authenticate(c.db, cuser, cpass, C.int(len(password))))
return 1
}

// AuthUserAdd can be used (by an admin user only)
Expand All @@ -131,20 +70,7 @@ func (c *SQLiteConn) authenticate(username, password string) int {
// for any ATTACH-ed databases. Any call to AuthUserAdd by a
// non-admin user results in an error.
func (c *SQLiteConn) AuthUserAdd(username, password string, admin bool) error {
isAdmin := 0
if admin {
isAdmin = 1
}

rv := c.authUserAdd(username, password, isAdmin)
switch rv {
case C.SQLITE_ERROR, C.SQLITE_AUTH:
return ErrAdminRequired
case C.SQLITE_OK:
return nil
default:
return c.lastError()
}
return ErrUnauthorized
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should return something like errors.New("sqlite3: the sqlite_userauth tag is no longer supported as the userauth extension is no longer supported by the SQLite authors, see <GitHub issue link>").

That way, users that have been relying on this extension will get a very clear message as to what the problem is, as opposed to a mysterious "not authorized" error.

}

// authUserAdd enables the User Authentication if not enabled.
Expand All @@ -162,17 +88,7 @@ func (c *SQLiteConn) AuthUserAdd(username, password string, admin bool) error {
// C.SQLITE_ERROR (1)
// C.SQLITE_AUTH (23)
func (c *SQLiteConn) authUserAdd(username, password string, admin int) int {
// Allocate C Variables
cuser := C.CString(username)
cpass := C.CString(password)

// Free C Variables
defer func() {
C.free(unsafe.Pointer(cuser))
C.free(unsafe.Pointer(cpass))
}()

return int(C._sqlite3_user_add(c.db, cuser, cpass, C.int(len(password)), C.int(admin)))
return 1
}

// AuthUserChange can be used to change a users
Expand All @@ -181,20 +97,7 @@ func (c *SQLiteConn) authUserAdd(username, password string, admin int) int {
// credentials or admin privilege setting. No user may change their own
// admin privilege setting.
func (c *SQLiteConn) AuthUserChange(username, password string, admin bool) error {
isAdmin := 0
if admin {
isAdmin = 1
}

rv := c.authUserChange(username, password, isAdmin)
switch rv {
case C.SQLITE_ERROR, C.SQLITE_AUTH:
return ErrAdminRequired
case C.SQLITE_OK:
return nil
default:
return c.lastError()
}
return ErrUnauthorized
}

// authUserChange allows to modify a user.
Expand All @@ -215,17 +118,7 @@ func (c *SQLiteConn) AuthUserChange(username, password string, admin bool) error
// C.SQLITE_ERROR (1)
// C.SQLITE_AUTH (23)
func (c *SQLiteConn) authUserChange(username, password string, admin int) int {
// Allocate C Variables
cuser := C.CString(username)
cpass := C.CString(password)

// Free C Variables
defer func() {
C.free(unsafe.Pointer(cuser))
C.free(unsafe.Pointer(cpass))
}()

return int(C._sqlite3_user_change(c.db, cuser, cpass, C.int(len(password)), C.int(admin)))
return 1
}

// AuthUserDelete can be used (by an admin user only)
Expand All @@ -234,15 +127,7 @@ func (c *SQLiteConn) authUserChange(username, password string, admin int) int {
// the database cannot be converted into a no-authentication-required
// database.
func (c *SQLiteConn) AuthUserDelete(username string) error {
rv := c.authUserDelete(username)
switch rv {
case C.SQLITE_ERROR, C.SQLITE_AUTH:
return ErrAdminRequired
case C.SQLITE_OK:
return nil
default:
return c.lastError()
}
return ErrUnauthorized
}

// authUserDelete can be used to delete a user.
Expand All @@ -258,25 +143,12 @@ func (c *SQLiteConn) AuthUserDelete(username string) error {
// C.SQLITE_ERROR (1)
// C.SQLITE_AUTH (23)
func (c *SQLiteConn) authUserDelete(username string) int {
// Allocate C Variables
cuser := C.CString(username)

// Free C Variables
defer func() {
C.free(unsafe.Pointer(cuser))
}()

return int(C._sqlite3_user_delete(c.db, cuser))
return 1
}

// AuthEnabled checks if the database is protected by user authentication
func (c *SQLiteConn) AuthEnabled() (exists bool) {
rv := c.authEnabled()
if rv == 1 {
exists = true
}

return
return false
}

// authEnabled perform the actual check for user authentication.
Expand All @@ -289,7 +161,7 @@ func (c *SQLiteConn) AuthEnabled() (exists bool) {
// 0 - Disabled
// 1 - Enabled
func (c *SQLiteConn) authEnabled() int {
return int(C._sqlite3_auth_enabled(c.db))
return 0
}

// EOF
Loading
Loading