Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Reorder parameters on the sqlite3_user_*() interfaces for consistency. Add the first TCL test cases. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | user-auth |
Files: | files | file ages | folders |
SHA1: |
2f6d8f32eef526b5912f42ab467e3c78 |
User & Date: | drh 2014-09-11 00:27:53.371 |
Context
2014-09-11
| ||
13:44 | Add support for the extra parameter on the sqlite3_set_authorizer() callback and support for failing an ATTACH with an authentication-required database using bad credentials. The extension is now feature complete, but much testing and bug-fixing remains. (check-in: 596e728b0e user: drh tags: user-auth) | |
00:27 | Reorder parameters on the sqlite3_user_*() interfaces for consistency. Add the first TCL test cases. (check-in: 2f6d8f32ee user: drh tags: user-auth) | |
2014-09-10
| ||
22:46 | Complete the implementation of the various APIs. Fix several problems. This is another incremental check-in that does not completely work. (check-in: 4eaaa7fa87 user: drh tags: user-auth) | |
Changes
Changes to ext/userauth/sqlite3userauth.h.
︙ | ︙ | |||
32 33 34 35 36 37 38 | ** ** If the SQLITE_USER table is not present in the database file, then ** this interface is a harmless no-op returnning SQLITE_OK. */ int sqlite3_user_authenticate( sqlite3 *db, /* The database connection */ const char *zUsername, /* Username */ | > | < | | | | | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | ** ** If the SQLITE_USER table is not present in the database file, then ** this interface is a harmless no-op returnning SQLITE_OK. */ int sqlite3_user_authenticate( sqlite3 *db, /* The database connection */ const char *zUsername, /* Username */ const char *aPW, /* Password or credentials */ int nPW /* Number of bytes in aPW[] */ ); /* ** The sqlite3_user_add() interface can be used (by an admin user only) ** to create a new user. When called on a no-authentication-required ** database, this routine converts the database into an authentication- ** required database, automatically makes the added user an ** administrator, and logs in the current connection as that user. ** The sqlite3_user_add() interface only works for the "main" database, not ** for any ATTACH-ed databases. Any call to sqlite3_user_add() by a ** non-admin user results in an error. */ int sqlite3_user_add( sqlite3 *db, /* Database connection */ const char *zUsername, /* Username to be added */ const char *aPW, /* Password or credentials */ int nPW, /* Number of bytes in aPW[] */ int isAdmin /* True to give new user admin privilege */ ); /* ** The sqlite3_user_change() interface can be used to change a users ** login credentials or admin privilege. Any user can change their own ** login credentials. Only an admin user can change another users login ** credentials or admin privilege setting. No user may change their own ** admin privilege setting. */ int sqlite3_user_change( sqlite3 *db, /* Database connection */ const char *zUsername, /* Username to change */ const char *aPW, /* New password or credentials */ int nPW, /* Number of bytes in aPW[] */ int isAdmin /* Modified admin privilege for the user */ ); /* ** The sqlite3_user_delete() interface can be used (by an admin user only) ** to delete a user. The currently logged-in user cannot be deleted, ** which guarantees that there is always an admin user and hence that ** the database cannot be converted into a no-authentication-required |
︙ | ︙ |
Changes to ext/userauth/user-auth.txt.
1 2 3 4 5 6 7 8 9 | Activate the user authentication logic by compiling SQLite with the -DSQLITE_USER_AUTHENTICATION compile-time option. The following new APIs are available when user authentication is activated: int sqlite3_user_authenticate( sqlite3 *db, /* The database connection */ const char *zUsername, /* Username */ | > | < | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | Activate the user authentication logic by compiling SQLite with the -DSQLITE_USER_AUTHENTICATION compile-time option. The following new APIs are available when user authentication is activated: int sqlite3_user_authenticate( sqlite3 *db, /* The database connection */ const char *zUsername, /* Username */ const char *aPW, /* Password or credentials */ int nPW /* Number of bytes in aPW[] */ ); int sqlite3_user_add( sqlite3 *db, /* Database connection */ const char *zUsername, /* Username to be added */ const char *aPW, /* Password or credentials */ int nPW, /* Number of bytes in aPW[] */ int isAdmin /* True to give new user admin privilege */ ); int sqlite3_user_change( sqlite3 *db, /* Database connection */ const char *zUsername, /* Username to change */ const void *aPW, /* Modified password or credentials */ int nPW, /* Number of bytes in aPW[] */ int isAdmin /* Modified admin privilege for the user */ ); int sqlite3_user_delete( sqlite3 *db, /* Database connection */ const char *zUsername /* Username to remove */ ); |
︙ | ︙ | |||
67 68 69 70 71 72 73 | When ATTACH-ing new database files to a connection, each newly attached database that is an authentication-required database is checked using the same username and password as supplied to the main database. If that check fails, then the ATTACH-ed database is unreadable [1g]. The sqlite3_user_add() interface can be used (by an admin user only) to create a new user. When called on a no-authentication-required | > | > > | < | > | | | 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | When ATTACH-ing new database files to a connection, each newly attached database that is an authentication-required database is checked using the same username and password as supplied to the main database. If that check fails, then the ATTACH-ed database is unreadable [1g]. The sqlite3_user_add() interface can be used (by an admin user only) to create a new user. When called on a no-authentication-required database and when A is true, the sqlite3_user_add(D,U,P,N,A) routine converts the database into an authentication-required database and logs the database connection D in using user U with password P,N. To convert a no-authentication-required database into an authentication- required database, the isAdmin parameter must be true. If sqlite3_user_add(D,U,P,N,A) is called on a no-authentication-required database and A is false, then the call fails with an SQLITE_AUTH error. Any call to sqlite3_user_add() by a non-admin user results in an error. Hence, to create a new, unencrypted, authentication-required database, the call sequence is: sqlite3_open_v2(); sqlite3_user_add(); |
︙ | ︙ |
Changes to ext/userauth/userauth.c.
︙ | ︙ | |||
173 174 175 176 177 178 179 | ** ** If the SQLITE_USER table is not present in the database file, then ** this interface is a harmless no-op returnning SQLITE_OK. */ int sqlite3_user_authenticate( sqlite3 *db, /* The database connection */ const char *zUsername, /* Username */ | > | < | 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | ** ** If the SQLITE_USER table is not present in the database file, then ** this interface is a harmless no-op returnning SQLITE_OK. */ int sqlite3_user_authenticate( sqlite3 *db, /* The database connection */ const char *zUsername, /* Username */ const char *zPW, /* Password or credentials */ int nPW /* Number of bytes in aPW[] */ ){ int rc; u8 authLevel = UAUTH_Fail; db->auth.authLevel = UAUTH_Unknown; sqlite3_free(db->auth.zAuthUser); sqlite3_free(db->auth.zAuthPW); memset(&db->auth, 0, sizeof(db->auth)); |
︙ | ︙ | |||
213 214 215 216 217 218 219 | ** The sqlite3_user_add() interface only works for the "main" database, not ** for any ATTACH-ed databases. Any call to sqlite3_user_add() by a ** non-admin user results in an error. */ int sqlite3_user_add( sqlite3 *db, /* Database connection */ const char *zUsername, /* Username to be added */ | | | | 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | ** The sqlite3_user_add() interface only works for the "main" database, not ** for any ATTACH-ed databases. Any call to sqlite3_user_add() by a ** non-admin user results in an error. */ int sqlite3_user_add( sqlite3 *db, /* Database connection */ const char *zUsername, /* Username to be added */ const char *aPW, /* Password or credentials */ int nPW, /* Number of bytes in aPW[] */ int isAdmin /* True to give new user admin privilege */ ){ sqlite3_stmt *pStmt; int rc; if( db->auth.authLevel<UAUTH_Admin ) return SQLITE_AUTH; if( !userTableExists(db, "main") ){ if( !isAdmin ) return SQLITE_AUTH; pStmt = sqlite3UserAuthPrepare(db, |
︙ | ︙ | |||
244 245 246 247 248 249 250 | if( pStmt==0 ) return SQLITE_NOMEM; sqlite3_bind_blob(pStmt, 1, aPW, nPW, SQLITE_STATIC); sqlite3_step(pStmt); rc = sqlite3_finalize(pStmt); if( rc ) return rc; if( db->auth.zAuthUser==0 ){ assert( isAdmin!=0 ); | | | | | 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 | if( pStmt==0 ) return SQLITE_NOMEM; sqlite3_bind_blob(pStmt, 1, aPW, nPW, SQLITE_STATIC); sqlite3_step(pStmt); rc = sqlite3_finalize(pStmt); if( rc ) return rc; if( db->auth.zAuthUser==0 ){ assert( isAdmin!=0 ); sqlite3_user_authenticate(db, zUsername, aPW, nPW); } return SQLITE_OK; } /* ** The sqlite3_user_change() interface can be used to change a users ** login credentials or admin privilege. Any user can change their own ** login credentials. Only an admin user can change another users login ** credentials or admin privilege setting. No user may change their own ** admin privilege setting. */ int sqlite3_user_change( sqlite3 *db, /* Database connection */ const char *zUsername, /* Username to change */ const char *aPW, /* Modified password or credentials */ int nPW, /* Number of bytes in aPW[] */ int isAdmin /* Modified admin privilege for the user */ ){ sqlite3_stmt *pStmt; if( db->auth.authLevel<UAUTH_User ){ /* Must be logged in to make a change */ return SQLITE_AUTH; } if( strcmp(db->auth.zAuthUser, zUsername)!=0 ){ |
︙ | ︙ |
Changes to src/shell.c.
︙ | ︙ | |||
3447 3448 3449 3450 3451 3452 3453 | } if( strcmp(azArg[1],"login")==0 ){ if( nArg!=4 ){ fprintf(stderr, "Usage: .user login USER PASSWORD\n"); rc = 1; goto meta_command_exit; } | | > | | | > | | | > | 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 | } if( strcmp(azArg[1],"login")==0 ){ if( nArg!=4 ){ fprintf(stderr, "Usage: .user login USER PASSWORD\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_user_authenticate(p->db, azArg[2], azArg[3], (int)strlen(azArg[3])); if( rc ){ fprintf(stderr, "Authentication failed for user %s\n", azArg[2]); rc = 1; } }else if( strcmp(azArg[1],"add")==0 ){ if( nArg!=5 ){ fprintf(stderr, "Usage: .user add USER PASSWORD ISADMIN\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_user_add(p->db, azArg[2], azArg[3], (int)strlen(azArg[3]), booleanValue(azArg[4])); if( rc ){ fprintf(stderr, "User-Add failed: %d\n", rc); rc = 1; } }else if( strcmp(azArg[1],"edit")==0 ){ if( nArg!=5 ){ fprintf(stderr, "Usage: .user edit USER PASSWORD ISADMIN\n"); rc = 1; goto meta_command_exit; } rc = sqlite3_user_change(p->db, azArg[2], azArg[3], (int)strlen(azArg[3]), booleanValue(azArg[4])); if( rc ){ fprintf(stderr, "User-Edit failed: %d\n", rc); rc = 1; } }else if( strcmp(azArg[1],"delete")==0 ){ if( nArg!=3 ){ fprintf(stderr, "Usage: .user delete USER\n"); |
︙ | ︙ |
Changes to src/test1.c.
︙ | ︙ | |||
6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 | return TCL_OK; sql_error: Tcl_AppendResult(interp, "sql error: ", sqlite3_errmsg(db), 0); return TCL_ERROR; } /* ** Register commands with the TCL interpreter. */ int Sqlitetest1_Init(Tcl_Interp *interp){ extern int sqlite3_search_count; extern int sqlite3_found_count; extern int sqlite3_interrupt_count; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 6493 6494 6495 6496 6497 6498 6499 6500 6501 6502 6503 6504 6505 6506 6507 6508 6509 6510 6511 6512 6513 6514 6515 6516 6517 6518 6519 6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551 6552 6553 6554 6555 6556 6557 6558 6559 6560 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 6576 6577 6578 6579 6580 6581 6582 6583 6584 6585 6586 6587 6588 6589 6590 6591 6592 6593 6594 6595 6596 6597 6598 6599 6600 6601 6602 6603 6604 6605 6606 6607 6608 6609 6610 6611 6612 6613 6614 6615 6616 6617 6618 6619 6620 6621 6622 6623 6624 6625 6626 6627 6628 6629 6630 6631 6632 | return TCL_OK; sql_error: Tcl_AppendResult(interp, "sql error: ", sqlite3_errmsg(db), 0); return TCL_ERROR; } #ifdef SQLITE_USER_AUTHENTICATION #include "sqlite3userauth.h" /* ** tclcmd: sqlite3_user_authenticate DB USERNAME PASSWORD */ static int test_user_authenticate( ClientData clientData, /* Unused */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ char *zUser = 0; char *zPasswd = 0; int nPasswd = 0; sqlite3 *db; int rc; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB USERNAME PASSWORD"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){ return TCL_ERROR; } zUser = Tcl_GetString(objv[2]); zPasswd = Tcl_GetStringFromObj(objv[3], &nPasswd); rc = sqlite3_user_authenticate(db, zUser, zPasswd, nPasswd); Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC); return TCL_OK; } #endif /* SQLITE_USER_AUTHENTICATION */ #ifdef SQLITE_USER_AUTHENTICATION /* ** tclcmd: sqlite3_user_add DB USERNAME PASSWORD ISADMIN */ static int test_user_add( ClientData clientData, /* Unused */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ char *zUser = 0; char *zPasswd = 0; int nPasswd = 0; int isAdmin = 0; sqlite3 *db; int rc; if( objc!=5 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB USERNAME PASSWORD ISADMIN"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){ return TCL_ERROR; } zUser = Tcl_GetString(objv[2]); zPasswd = Tcl_GetStringFromObj(objv[3], &nPasswd); Tcl_GetBooleanFromObj(interp, objv[4], &isAdmin); rc = sqlite3_user_add(db, zUser, zPasswd, nPasswd, isAdmin); Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC); return TCL_OK; } #endif /* SQLITE_USER_AUTHENTICATION */ #ifdef SQLITE_USER_AUTHENTICATION /* ** tclcmd: sqlite3_user_change DB USERNAME PASSWORD ISADMIN */ static int test_user_change( ClientData clientData, /* Unused */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ char *zUser = 0; char *zPasswd = 0; int nPasswd = 0; int isAdmin = 0; sqlite3 *db; int rc; if( objc!=5 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB USERNAME PASSWORD ISADMIN"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){ return TCL_ERROR; } zUser = Tcl_GetString(objv[2]); zPasswd = Tcl_GetStringFromObj(objv[3], &nPasswd); Tcl_GetBooleanFromObj(interp, objv[4], &isAdmin); rc = sqlite3_user_change(db, zUser, zPasswd, nPasswd, isAdmin); Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC); return TCL_OK; } #endif /* SQLITE_USER_AUTHENTICATION */ #ifdef SQLITE_USER_AUTHENTICATION /* ** tclcmd: sqlite3_user_delete DB USERNAME */ static int test_user_delete( ClientData clientData, /* Unused */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ char *zUser = 0; sqlite3 *db; int rc; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB USERNAME"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){ return TCL_ERROR; } zUser = Tcl_GetString(objv[2]); rc = sqlite3_user_delete(db, zUser); Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC); return TCL_OK; } #endif /* SQLITE_USER_AUTHENTICATION */ /* ** Register commands with the TCL interpreter. */ int Sqlitetest1_Init(Tcl_Interp *interp){ extern int sqlite3_search_count; extern int sqlite3_found_count; extern int sqlite3_interrupt_count; |
︙ | ︙ | |||
6730 6731 6732 6733 6734 6735 6736 6737 6738 6739 6740 6741 6742 6743 | { "sqlite3_test_control", test_test_control }, #if SQLITE_OS_UNIX { "getrusage", test_getrusage }, #endif { "load_static_extension", tclLoadStaticExtensionCmd }, { "sorter_test_fakeheap", sorter_test_fakeheap }, { "sorter_test_sort4_helper", sorter_test_sort4_helper }, }; static int bitmask_size = sizeof(Bitmask)*8; int i; extern int sqlite3_sync_count, sqlite3_fullsync_count; extern int sqlite3_opentemp_count; extern int sqlite3_like_count; extern int sqlite3_xferopt_count; | > > > > > > > | 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 | { "sqlite3_test_control", test_test_control }, #if SQLITE_OS_UNIX { "getrusage", test_getrusage }, #endif { "load_static_extension", tclLoadStaticExtensionCmd }, { "sorter_test_fakeheap", sorter_test_fakeheap }, { "sorter_test_sort4_helper", sorter_test_sort4_helper }, #ifdef SQLITE_USER_AUTHENTICATION { "sqlite3_user_authenticate", test_user_authenticate, 0 }, { "sqlite3_user_add", test_user_add, 0 }, { "sqlite3_user_change", test_user_change, 0 }, { "sqlite3_user_delete", test_user_delete, 0 }, #endif }; static int bitmask_size = sizeof(Bitmask)*8; int i; extern int sqlite3_sync_count, sqlite3_fullsync_count; extern int sqlite3_opentemp_count; extern int sqlite3_like_count; extern int sqlite3_xferopt_count; |
︙ | ︙ |
Added test/userauth01.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | # 2014-09-10 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. # May you find forgiveness for yourself and forgive others. # May you share freely, never taking more than you give. # #*********************************************************************** # # This file implements tests of the SQLITE_USER_AUTHENTICATION extension. # set testdir [file dirname $argv0] source $testdir/tester.tcl set testprefix userauth01 ifcapable !userauth { finish_test return } # Create a no-authentication-required database # do_execsql_test userauth01-1.0 { CREATE TABLE t1(x); INSERT INTO t1 VALUES(1),(2.5),('three'),(x'4444'),(NULL); SELECT quote(x) FROM t1 ORDER BY x; SELECT name FROM sqlite_master; } {NULL 1 2.5 'three' X'4444' t1} # Calling sqlite3_user_authenticate() on a no-authentication-required # database connection is a harmless no-op. # do_test userauth01-1.1 { sqlite3_user_authenticate db alice pw-4-alice execsql { SELECT quote(x) FROM t1 ORDER BY x; SELECT name FROM sqlite_master; } } {NULL 1 2.5 'three' X'4444' t1} # If sqlite3_user_add(D,U,P,N,A) is called on a no-authentication-required # database and A is false, then the call fails with an SQLITE_AUTH error. # do_test userauth01-1.2 { sqlite3_user_add db bob pw-4-bob 0 } {SQLITE_AUTH} do_test userauth01-1.3 { execsql { SELECT quote(x) FROM t1 ORDER BY x; SELECT name FROM sqlite_master; } } {NULL 1 2.5 'three' X'4444' t1} # When called on a no-authentication-required # database and when A is true, the sqlite3_user_add(D,U,P,N,A) routine # converts the database into an authentication-required database and # logs the database connection D in using user U with password P,N. # do_test userauth01-1.4 { sqlite3_user_add db alice pw-4-alice 1 } {SQLITE_OK} do_test userauth01-1.5 { execsql { SELECT quote(x) FROM t1 ORDER BY x; SELECT uname, isadmin FROM sqlite_user ORDER BY uname; SELECT name FROM sqlite_master ORDER BY name; } } {NULL 1 2.5 'three' X'4444' alice 1 sqlite_user t1} finish_test |