diff --git a/error_test.go b/error_test.go index 0ff14c18..fc6353d1 100644 --- a/error_test.go +++ b/error_test.go @@ -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") } diff --git a/sqlite3-binding.c b/sqlite3-binding.c index 44d91d9d..78b6f5b8 100644 --- a/sqlite3-binding.c +++ b/sqlite3-binding.c @@ -1,7 +1,7 @@ #ifndef USE_LIBSQLITE3 /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.50.3. By combining all the individual C code files into this +** version 3.50.4. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -19,7 +19,7 @@ ** separate file. This file contains only code for the core SQLite library. ** ** The content in this amalgamation comes from Fossil check-in -** 3ce993b8657d6d9deda380a93cdd6404a8c8 with changes in files: +** 4d8adfb30e03f9cf27f800a2c1ba3c48fb4c with changes in files: ** ** */ @@ -466,9 +466,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.50.3" -#define SQLITE_VERSION_NUMBER 3050003 -#define SQLITE_SOURCE_ID "2025-07-17 13:25:10 3ce993b8657d6d9deda380a93cdd6404a8c8ba1b185b2bc423703e41ae5f2543" +#define SQLITE_VERSION "3.50.4" +#define SQLITE_VERSION_NUMBER 3050004 +#define SQLITE_SOURCE_ID "2025-07-30 19:33:53 4d8adfb30e03f9cf27f800a2c1ba3c48fb4ca1b08b0f5ed59a4d5ecbf45e20a3" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -19441,6 +19441,7 @@ struct Expr { Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL ** for a column of an index on an expression */ Window *pWin; /* EP_WinFunc: Window/Filter defn for a function */ + int nReg; /* TK_NULLS: Number of registers to NULL out */ struct { /* TK_IN, TK_SELECT, and TK_EXISTS */ int iAddr; /* Subroutine entry address */ int regReturn; /* Register used to hold return address */ @@ -21475,6 +21476,7 @@ SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn(Parse*, Table*, Column*, int) SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse*, Expr*, int); SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse*, Expr*, int); SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce(Parse*, Expr*, int); +SQLITE_PRIVATE void sqlite3ExprNullRegisterRange(Parse*, int, int); SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse*, Expr*, int*); SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse*, Expr*, int); SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8); @@ -115242,6 +115244,12 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target) sqlite3VdbeLoadString(v, target, pExpr->u.zToken); return target; } + case TK_NULLS: { + /* Set a range of registers to NULL. pExpr->y.nReg registers starting + ** with target */ + sqlite3VdbeAddOp3(v, OP_Null, 0, target, target + pExpr->y.nReg - 1); + return target; + } default: { /* Make NULL the default case so that if a bug causes an illegal ** Expr node to be passed into this function, it will be handled @@ -115926,6 +115934,25 @@ SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce( return regDest; } +/* +** Make arrangements to invoke OP_Null on a range of registers +** during initialization. +*/ +SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3ExprNullRegisterRange( + Parse *pParse, /* Parsing context */ + int iReg, /* First register to set to NULL */ + int nReg /* Number of sequential registers to NULL out */ +){ + u8 okConstFactor = pParse->okConstFactor; + Expr t; + memset(&t, 0, sizeof(t)); + t.op = TK_NULLS; + t.y.nReg = nReg; + pParse->okConstFactor = 1; + sqlite3ExprCodeRunJustOnce(pParse, &t, iReg); + pParse->okConstFactor = okConstFactor; +} + /* ** Generate code to evaluate an expression and store the results ** into a register. Return the register number where the results @@ -153176,6 +153203,7 @@ SQLITE_PRIVATE int sqlite3Select( sqlite3VdbeAddOp2(v, OP_Integer, 0, iAbortFlag); VdbeComment((v, "clear abort flag")); sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1); + sqlite3ExprNullRegisterRange(pParse, iAMem, pGroupBy->nExpr); /* Begin a loop that will extract all source rows in GROUP BY order. ** This might involve two separate loops with an OP_Sort in between, or @@ -168471,6 +168499,7 @@ static int whereLoopAddBtree( pNew->u.btree.nEq = 0; pNew->u.btree.nBtm = 0; pNew->u.btree.nTop = 0; + pNew->u.btree.nDistinctCol = 0; pNew->nSkip = 0; pNew->nLTerm = 0; pNew->iSortIdx = 0; @@ -169539,8 +169568,6 @@ static i8 wherePathSatisfiesOrderBy( obSat = obDone; } break; - }else if( wctrlFlags & WHERE_DISTINCTBY ){ - pLoop->u.btree.nDistinctCol = 0; } iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor; @@ -257281,7 +257308,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2025-07-17 13:25:10 3ce993b8657d6d9deda380a93cdd6404a8c8ba1b185b2bc423703e41ae5f2543", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2025-07-30 19:33:53 4d8adfb30e03f9cf27f800a2c1ba3c48fb4ca1b08b0f5ed59a4d5ecbf45e20a3", -1, SQLITE_TRANSIENT); } /* diff --git a/sqlite3-binding.h b/sqlite3-binding.h index d65d949a..c34235d8 100644 --- a/sqlite3-binding.h +++ b/sqlite3-binding.h @@ -147,9 +147,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.50.3" -#define SQLITE_VERSION_NUMBER 3050003 -#define SQLITE_SOURCE_ID "2025-07-17 13:25:10 3ce993b8657d6d9deda380a93cdd6404a8c8ba1b185b2bc423703e41ae5f2543" +#define SQLITE_VERSION "3.50.4" +#define SQLITE_VERSION_NUMBER 3050004 +#define SQLITE_SOURCE_ID "2025-07-30 19:33:53 4d8adfb30e03f9cf27f800a2c1ba3c48fb4ca1b08b0f5ed59a4d5ecbf45e20a3" /* ** CAPI3REF: Run-Time Library Version Numbers diff --git a/sqlite3_opt_userauth.go b/sqlite3_opt_userauth.go index 76d84016..5a492766 100644 --- a/sqlite3_opt_userauth.go +++ b/sqlite3_opt_userauth.go @@ -16,53 +16,10 @@ package sqlite3 #else #include #endif -#include - -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 ( @@ -70,8 +27,9 @@ const ( ) var ( - ErrUnauthorized = errors.New("SQLITE_AUTH: Unauthorized") - ErrAdminRequired = errors.New("SQLITE_AUTH: Unauthorized; Admin Privileges Required") + ErrUnauthorized = errors.New("SQLITE_AUTH: Unauthorized") + ErrAdminRequired = errors.New("SQLITE_AUTH: Unauthorized; Admin Privileges Required") + errUserAuthNoLongerSupported = errors.New("sqlite3: the sqlite_userauth tag is no longer supported as the userauth extension is no longer supported by the SQLite authors, see https://github.com/mattn/go-sqlite3/issues/1341") ) // Authenticate will perform an authentication of the provided username @@ -88,15 +46,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 errUserAuthNoLongerSupported } // authenticate provides the actual authentication to SQLite. @@ -109,17 +59,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) @@ -131,20 +71,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 errUserAuthNoLongerSupported } // authUserAdd enables the User Authentication if not enabled. @@ -162,17 +89,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 @@ -181,20 +98,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 errUserAuthNoLongerSupported } // authUserChange allows to modify a user. @@ -215,17 +119,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) @@ -234,15 +128,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 errUserAuthNoLongerSupported } // authUserDelete can be used to delete a user. @@ -258,25 +144,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. @@ -289,7 +162,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 diff --git a/sqlite3_opt_userauth_test.go b/sqlite3_opt_userauth_test.go index 12e11510..218aecdf 100644 --- a/sqlite3_opt_userauth_test.go +++ b/sqlite3_opt_userauth_test.go @@ -10,23 +10,15 @@ package sqlite3 import ( "database/sql" + "errors" "fmt" "os" "testing" ) var ( - conn *SQLiteConn - create func(t *testing.T, username, password string) (file string, err error) - createWithCrypt func(t *testing.T, username, password, crypt, salt string) (file string, err error) - connect func(t *testing.T, f string, username, password string) (file string, db *sql.DB, c *SQLiteConn, err error) - connectWithCrypt func(t *testing.T, f string, username, password string, crypt string, salt string) (file string, db *sql.DB, c *SQLiteConn, err error) - authEnabled func(db *sql.DB) (exists bool, err error) - addUser func(db *sql.DB, username, password string, admin int) (rv int, err error) - userExists func(db *sql.DB, username string) (rv int, err error) - isAdmin func(db *sql.DB, username string) (rv bool, err error) - modifyUser func(db *sql.DB, username, password string, admin int) (rv int, err error) - deleteUser func(db *sql.DB, username string) (rv int, err error) + conn *SQLiteConn + connect func(t *testing.T, f string, username, password string) (file string, db *sql.DB, c *SQLiteConn, err error) ) func init() { @@ -39,20 +31,6 @@ func init() { }, }) - create = func(t *testing.T, username, password string) (file string, err error) { - var db *sql.DB - file, db, _, err = connect(t, "", username, password) - db.Close() - return - } - - createWithCrypt = func(t *testing.T, username, password, crypt, salt string) (file string, err error) { - var db *sql.DB - file, db, _, err = connectWithCrypt(t, "", "admin", "admin", crypt, salt) - db.Close() - return - } - connect = func(t *testing.T, f string, username, password string) (file string, db *sql.DB, c *SQLiteConn, err error) { conn = nil // Clear connection file = f // Copy provided file (f) => file @@ -75,7 +53,7 @@ func init() { } // Dummy query to force connection and database creation - // Will return ErrUnauthorized (SQLITE_AUTH) if user authentication fails + // Will return errUserAuthNoLongerSupported if user authentication fails if _, err = db.Exec("SELECT 1;"); err != nil { defer os.Remove(file) defer db.Close() @@ -85,560 +63,14 @@ func init() { return } - - connectWithCrypt = func(t *testing.T, f string, username, password string, crypt string, salt string) (file string, db *sql.DB, c *SQLiteConn, err error) { - conn = nil // Clear connection - file = f // Copy provided file (f) => file - if file == "" { - // Create dummy file - file = TempFilename(t) - } - - db, err = sql.Open("sqlite3_with_conn", "file:"+file+fmt.Sprintf("?_auth&_auth_user=%s&_auth_pass=%s&_auth_crypt=%s&_auth_salt=%s", username, password, crypt, salt)) - if err != nil { - defer os.Remove(file) - return file, nil, nil, err - } - - // Dummy query to force connection and database creation - // Will return ErrUnauthorized (SQLITE_AUTH) if user authentication fails - if _, err = db.Exec("SELECT 1;"); err != nil { - defer os.Remove(file) - defer db.Close() - return file, nil, nil, err - } - c = conn - - return - } - - authEnabled = func(db *sql.DB) (exists bool, err error) { - err = db.QueryRow("select count(type) from sqlite_master WHERE type='table' and name='sqlite_user';").Scan(&exists) - return - } - - addUser = func(db *sql.DB, username, password string, admin int) (rv int, err error) { - err = db.QueryRow("select auth_user_add(?, ?, ?);", username, password, admin).Scan(&rv) - return - } - - userExists = func(db *sql.DB, username string) (rv int, err error) { - err = db.QueryRow("select count(uname) from sqlite_user where uname=?", username).Scan(&rv) - return - } - - isAdmin = func(db *sql.DB, username string) (rv bool, err error) { - err = db.QueryRow("select isAdmin from sqlite_user where uname=?", username).Scan(&rv) - return - } - - modifyUser = func(db *sql.DB, username, password string, admin int) (rv int, err error) { - err = db.QueryRow("select auth_user_change(?, ?, ?);", username, password, admin).Scan(&rv) - return - } - - deleteUser = func(db *sql.DB, username string) (rv int, err error) { - err = db.QueryRow("select auth_user_delete(?);", username).Scan(&rv) - return - } } -func TestUserAuthCreateDatabase(t *testing.T) { - f, db, c, err := connect(t, "", "admin", "admin") - if err != nil && c == nil && db == nil { - t.Fatal(err) - } - defer db.Close() - defer os.Remove(f) - - enabled, err := authEnabled(db) - if err != nil || !enabled { - t.Fatalf("UserAuth not enabled: %s", err) - } - - e, err := userExists(db, "admin") - if err != nil { - t.Fatal(err) - } - if e != 1 { - t.Fatal("UserAuth: admin does not exists") - } - a, err := isAdmin(db, "admin") - if err != nil { - t.Fatal(err) - } - if !a { - t.Fatal("UserAuth: User is not administrator") - } -} - -func TestUserAuthCreateDatabaseWithoutArgs(t *testing.T) { - _, db, c, err := connect(t, "", "", "") - if err == nil && c == nil && db == nil { - t.Fatal("Should have failed due to missing _auth_* parameters") - } - - _, db, c, err = connect(t, "", "", "admin") - if err == nil && c == nil && db == nil { - t.Fatal("Should have failed due to missing _auth_user parameter") - } - - _, db, c, err = connect(t, "", "admin", "") - if err == nil && c == nil && db == nil { - t.Fatal("Should have failed due to missing _auth_pass parameter") - } -} - -func TestUserAuthLogin(t *testing.T) { - f1, err := create(t, "admin", "admin") - if err != nil { - t.Fatal(err) - } - defer os.Remove(f1) - - f2, db2, c2, err := connect(t, f1, "admin", "admin") - if err != nil { - t.Fatal(err) - } - defer db2.Close() - if f1 != f2 { - t.Fatal("UserAuth: Database file mismatch") - } - - // Test lower level authentication - err = c2.Authenticate("admin", "admin") - if err != nil { - t.Fatalf("UserAuth: *SQLiteConn.Authenticate() Failed: %s", err) - } - - // Test Login Failed - _, _, _, err = connect(t, f1, "admin", "invalid") - if err == nil { - t.Fatal("Login successful while expecting to fail") - } - if err != ErrUnauthorized { - t.Fatal(err) - } - err = c2.Authenticate("admin", "invalid") +func TestUserAuth(t *testing.T) { + _, _, _, err := connect(t, "", "admin", "admin") if err == nil { - t.Fatal("Login successful while expecting to fail") - } - if err != ErrUnauthorized { - t.Fatal(err) - } -} - -func TestUserAuthAddAdmin(t *testing.T) { - f, db, c, err := connect(t, "", "admin", "admin") - if err != nil && c == nil && db == nil { - t.Fatal(err) - } - defer db.Close() - defer os.Remove(f) - - // Add Admin User through SQL call - rv, err := addUser(db, "admin2", "admin2", 1) - if err != nil { - t.Fatal(err) - } - if rv != 0 { - t.Fatal("Failed to add user") - } - - // Check if user was created - exists, err := userExists(db, "admin2") - if err != nil { - t.Fatal(err) - } - if exists != 1 { - t.Fatal("UserAuth: 'admin2' does not exists") - } - - // Check if user was created as an Administrator - admin, err := isAdmin(db, "admin2") - if err != nil { - t.Fatal(err) - } - if !admin { - t.Fatal("UserAuth: 'admin2' is not administrator") - } - - // Test *SQLiteConn - err = c.AuthUserAdd("admin3", "admin3", true) - if err != nil { - t.Fatal(err) - } - - // Check if user was created - exists, err = userExists(db, "admin2") - if err != nil { - t.Fatal(err) - } - if exists != 1 { - t.Fatal("UserAuth: 'admin3' does not exists") - } - - // Check if the user was created as an Administrator - admin, err = isAdmin(db, "admin3") - if err != nil { - t.Fatal(err) - } - if !admin { - t.Fatal("UserAuth: 'admin3' is not administrator") - } -} - -func TestUserAuthAddUser(t *testing.T) { - f1, db1, c, err := connect(t, "", "admin", "admin") - if err != nil && c == nil && db == nil { - t.Fatal(err) - } - defer os.Remove(f1) - - // Add user through SQL call - rv, err := addUser(db1, "user", "user", 0) - if err != nil { - t.Fatal(err) - } - if rv != 0 { - t.Fatal("Failed to add user") - } - - // Check if user was created - exists, err := userExists(db1, "user") - if err != nil { - t.Fatal(err) - } - if exists != 1 { - t.Fatal("UserAuth: 'user' does not exists") - } - - // Check if user was created as an Administrator - admin, err := isAdmin(db1, "user") - if err != nil { - t.Fatal(err) - } - if admin { - t.Fatal("UserAuth: 'user' is administrator") - } - - // Test *SQLiteConn - err = c.AuthUserAdd("user2", "user2", false) - if err != nil { - t.Fatal(err) - } - - // Check if user was created - exists, err = userExists(db1, "user2") - if err != nil { - t.Fatal(err) - } - if exists != 1 { - t.Fatal("UserAuth: 'user2' does not exists") - } - - // Check if the user was created as an Administrator - admin, err = isAdmin(db1, "user2") - if err != nil { - t.Fatal(err) - } - if admin { - t.Fatal("UserAuth: 'user2' is administrator") - } - - // Reconnect as normal user - db1.Close() - _, db2, c2, err := connect(t, f1, "user", "user") - if err != nil { - t.Fatal(err) - } - defer db2.Close() - - // Try to create admin user while logged in as normal user - rv, err = addUser(db2, "admin2", "admin2", 1) - if err != nil { - t.Fatal(err) - } - if rv != SQLITE_AUTH { - t.Fatal("Created admin user while not allowed") - } - - err = c2.AuthUserAdd("admin3", "admin3", true) - if err != ErrAdminRequired { - t.Fatal("Created admin user while not allowed") - } - - // Try to create normal user while logged in as normal user - rv, err = addUser(db2, "user3", "user3", 0) - if err != nil { - t.Fatal(err) - } - if rv != SQLITE_AUTH { - t.Fatal("Created user while not allowed") - } - - err = c2.AuthUserAdd("user4", "user4", false) - if err != ErrAdminRequired { - t.Fatal("Created user while not allowed") - } -} - -func TestUserAuthModifyUser(t *testing.T) { - f1, db1, c1, err := connect(t, "", "admin", "admin") - if err != nil && c1 == nil && db == nil { - t.Fatal(err) - } - defer os.Remove(f1) - - // Modify Password for current logged in admin - // through SQL - rv, err := modifyUser(db1, "admin", "admin2", 1) - if err != nil { - t.Fatal(err) - } - if rv != 0 { - t.Fatal("Failed to modify password for admin") - } - - // Modify password for current logged in admin - // through *SQLiteConn - err = c1.AuthUserChange("admin", "admin3", true) - if err != nil { - t.Fatal(err) - } - - // Modify Administrator Flag - // Because we are current logged in as 'admin' - // Changing our own admin flag should fail. - rv, err = modifyUser(db1, "admin", "admin3", 0) - if err != nil { - t.Fatal(err) - } - if rv != SQLITE_AUTH { - t.Fatal("Successfully changed admin flag while not allowed") - } - - // Modify admin flag through (*SQLiteConn) - // Because we are current logged in as 'admin' - // Changing our own admin flag should fail. - err = c1.AuthUserChange("admin", "admin3", false) - if err != ErrAdminRequired { - t.Fatal("Successfully changed admin flag while not allowed") - } - - // Add normal user - rv, err = addUser(db1, "user", "password", 0) - if err != nil { - t.Fatal(err) - } - if rv != 0 { - t.Fatal("Failed to add user") - } - - rv, err = addUser(db1, "user2", "user2", 0) - if err != nil { - t.Fatal(err) - } - if rv != 0 { - t.Fatal("Failed to add user") - } - - // Modify other user password and flag through SQL - rv, err = modifyUser(db1, "user", "pass", 1) - if err != nil { - t.Fatal(err) - } - if rv != 0 { - t.Fatal("Failed to modify password for user") - } - - // Modify other user password and flag through *SQLiteConn - err = c1.AuthUserChange("user", "newpass", false) - if err != nil { - t.Fatal(err) - } - - // Disconnect database for reconnect - db1.Close() - _, db2, c2, err := connect(t, f1, "user", "newpass") - if err != nil { - t.Fatal(err) - } - defer db2.Close() - - // Modify other user password through SQL - rv, err = modifyUser(db2, "user2", "newpass", 0) - if err != nil { - t.Fatal(err) - } - if rv != SQLITE_AUTH { - t.Fatal("Password change successful while not allowed") - } - - // Modify other user password and flag through *SQLiteConn - err = c2.AuthUserChange("user2", "invalid", false) - if err != ErrAdminRequired { - t.Fatal("Password change successful while not allowed") - } -} - -func TestUserAuthDeleteUser(t *testing.T) { - f1, db1, c, err := connect(t, "", "admin", "admin") - if err != nil && c == nil && db == nil { - t.Fatal(err) - } - defer os.Remove(f1) - - // Add Admin User 2 - rv, err := addUser(db1, "admin2", "admin2", 1) - if err != nil { - t.Fatal(err) - } - if rv != 0 { - t.Fatal("Failed to add user") - } - - rv, err = addUser(db1, "admin3", "admin3", 1) - if err != nil { - t.Fatal(err) - } - if rv != 0 { - t.Fatal("Failed to add user") - } - - // Check if user was created - exists, err := userExists(db1, "admin2") - if err != nil { - t.Fatal(err) - } - if exists != 1 { - t.Fatal("UserAuth: 'admin2' does not exists") - } - - exists, err = userExists(db1, "admin3") - if err != nil { - t.Fatal(err) - } - if exists != 1 { - t.Fatal("UserAuth: 'admin2' does not exists") - } - - // Delete user through SQL - rv, err = deleteUser(db1, "admin2") - if err != nil { - t.Fatal(err) - } - if rv != 0 { - t.Fatal("Failed to delete admin2") - } - - // Verify user admin2 deleted - exists, err = userExists(db1, "admin2") - if err != nil { - t.Fatal(err) - } - if exists != 0 { - t.Fatal("UserAuth: 'admin2' still exists") - } - - // Delete user through *SQLiteConn - rv, err = deleteUser(db1, "admin3") - if err != nil { - t.Fatal(err) - } - if rv != 0 { - t.Fatal("Failed to delete admin3") - } - - // Verify user admin3 deleted - exists, err = userExists(db1, "admin3") - if err != nil { - t.Fatal(err) - } - if exists != 0 { - t.Fatal("UserAuth: 'admin3' still exists") - } - - // Add normal user for reconnect and privileges check - rv, err = addUser(db1, "reconnect", "reconnect", 0) - if err != nil { - t.Fatal(err) - } - if rv != 0 { - t.Fatal("Failed to add user") - } - - // Add normal user for deletion through SQL - rv, err = addUser(db1, "user", "user", 0) - if err != nil { - t.Fatal(err) - } - if rv != 0 { - t.Fatal("Failed to add user") - } - - rv, err = addUser(db1, "user2", "user2", 0) - if err != nil { - t.Fatal(err) - } - if rv != 0 { - t.Fatal("Failed to add user") - } - - // Close database for reconnect - db1.Close() - - // Reconnect as normal user - _, db2, c2, err := connect(t, f1, "reconnect", "reconnect") - if err != nil { - t.Fatal(err) + t.Fatalf("UserAuth not enabled: %s", err) } - defer db2.Close() - - // Delete user while logged in as normal user - // through SQL - rv, err = deleteUser(db2, "user") - if err != nil { + if !errors.Is(err, errUserAuthNoLongerSupported) { t.Fatal(err) } - if rv != SQLITE_AUTH { - t.Fatal("Successfully deleted user wthout proper privileges") - } - - // Delete user while logged in as normal user - // through *SQLiteConn - err = c2.AuthUserDelete("user2") - if err != ErrAdminRequired { - t.Fatal("Successfully deleted user wthout proper privileges") - } -} - -func TestUserAuthEncoders(t *testing.T) { - cases := map[string]string{ - "sha1": "", - "ssha1": "salted", - "sha256": "", - "ssha256": "salted", - "sha384": "", - "ssha384": "salted", - "sha512": "", - "ssha512": "salted", - } - - for enc, salt := range cases { - f, err := createWithCrypt(t, "admin", "admin", enc, salt) - if err != nil { - t.Fatal(err) - } - defer os.Remove(f) - - _, db, _, err := connectWithCrypt(t, f, "admin", "admin", enc, salt) - if err != nil { - t.Fatal(err) - } - defer db.Close() - if e, err := authEnabled(db); err != nil && !e { - t.Fatalf("UserAuth (%s) not enabled %s", enc, err) - } - } }