/ Check-in [2f6d8f32]
Login

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 | SQL archive
Timelines: family | ancestors | descendants | both | user-auth
Files: files | file ages | folders
SHA1: 2f6d8f32eef526b5912f42ab467e3c7812480d8b
User & Date: drh 2014-09-11 00:27:53
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: 596e728b user: drh tags: user-auth
00:27
Reorder parameters on the sqlite3_user_*() interfaces for consistency. Add the first TCL test cases. check-in: 2f6d8f32 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: 4eaaa7fa user: drh tags: user-auth
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to ext/userauth/sqlite3userauth.h.

    32     32   **
    33     33   ** If the SQLITE_USER table is not present in the database file, then
    34     34   ** this interface is a harmless no-op returnning SQLITE_OK.
    35     35   */
    36     36   int sqlite3_user_authenticate(
    37     37     sqlite3 *db,           /* The database connection */
    38     38     const char *zUsername, /* Username */
    39         -  int nPW,               /* Number of bytes in aPW[] */
    40         -  const char *aPW        /* Password or credentials */
           39  +  const char *aPW,       /* Password or credentials */
           40  +  int nPW                /* Number of bytes in aPW[] */
    41     41   );
    42     42   
    43     43   /*
    44     44   ** The sqlite3_user_add() interface can be used (by an admin user only)
    45     45   ** to create a new user.  When called on a no-authentication-required
    46     46   ** database, this routine converts the database into an authentication-
    47     47   ** required database, automatically makes the added user an
................................................................................
    49     49   ** The sqlite3_user_add() interface only works for the "main" database, not
    50     50   ** for any ATTACH-ed databases.  Any call to sqlite3_user_add() by a
    51     51   ** non-admin user results in an error.
    52     52   */
    53     53   int sqlite3_user_add(
    54     54     sqlite3 *db,           /* Database connection */
    55     55     const char *zUsername, /* Username to be added */
    56         -  int isAdmin,           /* True to give new user admin privilege */
           56  +  const char *aPW,       /* Password or credentials */
    57     57     int nPW,               /* Number of bytes in aPW[] */
    58         -  const char *aPW        /* Password or credentials */
           58  +  int isAdmin            /* True to give new user admin privilege */
    59     59   );
    60     60   
    61     61   /*
    62     62   ** The sqlite3_user_change() interface can be used to change a users
    63     63   ** login credentials or admin privilege.  Any user can change their own
    64     64   ** login credentials.  Only an admin user can change another users login
    65     65   ** credentials or admin privilege setting.  No user may change their own 
    66     66   ** admin privilege setting.
    67     67   */
    68     68   int sqlite3_user_change(
    69     69     sqlite3 *db,           /* Database connection */
    70     70     const char *zUsername, /* Username to change */
    71         -  int isAdmin,           /* Modified admin privilege for the user */
           71  +  const char *aPW,       /* New password or credentials */
    72     72     int nPW,               /* Number of bytes in aPW[] */
    73         -  const char *aPW        /* Modified password or credentials */
           73  +  int isAdmin            /* Modified admin privilege for the user */
    74     74   );
    75     75   
    76     76   /*
    77     77   ** The sqlite3_user_delete() interface can be used (by an admin user only)
    78     78   ** to delete a user.  The currently logged-in user cannot be deleted,
    79     79   ** which guarantees that there is always an admin user and hence that
    80     80   ** the database cannot be converted into a no-authentication-required

Changes to ext/userauth/user-auth.txt.

     3      3   
     4      4   The following new APIs are available when user authentication is
     5      5   activated:
     6      6   
     7      7      int sqlite3_user_authenticate(
     8      8        sqlite3 *db,           /* The database connection */
     9      9        const char *zUsername, /* Username */
    10         -     int nPW,               /* Number of bytes in aPW[] */
    11         -     const void *aPW        /* Password or credentials */
           10  +     const char *aPW,       /* Password or credentials */
           11  +     int nPW                /* Number of bytes in aPW[] */
    12     12      );
    13     13      
    14     14      int sqlite3_user_add(
    15     15        sqlite3 *db,           /* Database connection */
    16     16        const char *zUsername, /* Username to be added */
    17         -     int isAdmin,           /* True to give new user admin privilege */
           17  +     const char *aPW,       /* Password or credentials */
    18     18        int nPW,               /* Number of bytes in aPW[] */
    19         -     const void *aPW        /* Password or credentials */
           19  +     int isAdmin            /* True to give new user admin privilege */
    20     20      );
    21     21      
    22     22      int sqlite3_user_change(
    23     23        sqlite3 *db,           /* Database connection */
    24     24        const char *zUsername, /* Username to change */
    25         -     int isAdmin,           /* Modified admin privilege for the user */
           25  +     const void *aPW,       /* Modified password or credentials */
    26     26        int nPW,               /* Number of bytes in aPW[] */
    27         -     const void *aPW        /* Modified password or credentials */
           27  +     int isAdmin            /* Modified admin privilege for the user */
    28     28      );
    29     29      
    30     30      int sqlite3_user_delete(
    31     31        sqlite3 *db,           /* Database connection */
    32     32        const char *zUsername  /* Username to remove */
    33     33      );
    34     34      
................................................................................
    67     67   When ATTACH-ing new database files to a connection, each newly attached
    68     68   database that is an authentication-required database is checked using
    69     69   the same username and password as supplied to the main database.  If that
    70     70   check fails, then the ATTACH-ed database is unreadable [1g].
    71     71   
    72     72   The sqlite3_user_add() interface can be used (by an admin user only)
    73     73   to create a new user.  When called on a no-authentication-required
    74         -database, this routine converts the database into an authentication-
    75         -required database [3], automatically makes the added user an
    76         -administrator [1b], and logs in the current connection as that user [1a].
    77         -The sqlite3_user_add() interface only works for the "main" database, not
    78         -for any ATTACH-ed databases.  Any call to sqlite3_user_add() by a
    79         -non-admin user results in an error.
           74  +database and when A is true, the sqlite3_user_add(D,U,P,N,A) routine
           75  +converts the database into an authentication-required database and
           76  +logs the database connection D in using user U with password P,N.
           77  +To convert a no-authentication-required database into an authentication-
           78  +required database, the isAdmin parameter must be true.  If
           79  +sqlite3_user_add(D,U,P,N,A) is called on a no-authentication-required
           80  +database and A is false, then the call fails with an SQLITE_AUTH error.
           81  +
           82  +Any call to sqlite3_user_add() by a non-admin user results in an error.
    80     83   
    81     84   Hence, to create a new, unencrypted, authentication-required database,
    82     85   the call sequence is:
    83     86   
    84     87       sqlite3_open_v2();
    85     88       sqlite3_user_add();
    86     89   

Changes to ext/userauth/userauth.c.

   173    173   **
   174    174   ** If the SQLITE_USER table is not present in the database file, then
   175    175   ** this interface is a harmless no-op returnning SQLITE_OK.
   176    176   */
   177    177   int sqlite3_user_authenticate(
   178    178     sqlite3 *db,           /* The database connection */
   179    179     const char *zUsername, /* Username */
   180         -  int nPW,               /* Number of bytes in aPW[] */
   181         -  const char *zPW        /* Password or credentials */
          180  +  const char *zPW,       /* Password or credentials */
          181  +  int nPW                /* Number of bytes in aPW[] */
   182    182   ){
   183    183     int rc;
   184    184     u8 authLevel = UAUTH_Fail;
   185    185     db->auth.authLevel = UAUTH_Unknown;
   186    186     sqlite3_free(db->auth.zAuthUser);
   187    187     sqlite3_free(db->auth.zAuthPW);
   188    188     memset(&db->auth, 0, sizeof(db->auth));
................................................................................
   213    213   ** The sqlite3_user_add() interface only works for the "main" database, not
   214    214   ** for any ATTACH-ed databases.  Any call to sqlite3_user_add() by a
   215    215   ** non-admin user results in an error.
   216    216   */
   217    217   int sqlite3_user_add(
   218    218     sqlite3 *db,           /* Database connection */
   219    219     const char *zUsername, /* Username to be added */
   220         -  int isAdmin,           /* True to give new user admin privilege */
          220  +  const char *aPW,       /* Password or credentials */
   221    221     int nPW,               /* Number of bytes in aPW[] */
   222         -  const char *aPW        /* Password or credentials */
          222  +  int isAdmin            /* True to give new user admin privilege */
   223    223   ){
   224    224     sqlite3_stmt *pStmt;
   225    225     int rc;
   226    226     if( db->auth.authLevel<UAUTH_Admin ) return SQLITE_AUTH;
   227    227     if( !userTableExists(db, "main") ){
   228    228       if( !isAdmin ) return SQLITE_AUTH;
   229    229       pStmt = sqlite3UserAuthPrepare(db, 
................................................................................
   244    244     if( pStmt==0 ) return SQLITE_NOMEM;
   245    245     sqlite3_bind_blob(pStmt, 1, aPW, nPW, SQLITE_STATIC);
   246    246     sqlite3_step(pStmt);
   247    247     rc = sqlite3_finalize(pStmt);
   248    248     if( rc ) return rc;
   249    249     if( db->auth.zAuthUser==0 ){
   250    250       assert( isAdmin!=0 );
   251         -    sqlite3_user_authenticate(db, zUsername, nPW, aPW);
          251  +    sqlite3_user_authenticate(db, zUsername, aPW, nPW);
   252    252     }
   253    253     return SQLITE_OK;
   254    254   }
   255    255   
   256    256   /*
   257    257   ** The sqlite3_user_change() interface can be used to change a users
   258    258   ** login credentials or admin privilege.  Any user can change their own
................................................................................
   259    259   ** login credentials.  Only an admin user can change another users login
   260    260   ** credentials or admin privilege setting.  No user may change their own 
   261    261   ** admin privilege setting.
   262    262   */
   263    263   int sqlite3_user_change(
   264    264     sqlite3 *db,           /* Database connection */
   265    265     const char *zUsername, /* Username to change */
   266         -  int isAdmin,           /* Modified admin privilege for the user */
          266  +  const char *aPW,       /* Modified password or credentials */
   267    267     int nPW,               /* Number of bytes in aPW[] */
   268         -  const char *aPW        /* Modified password or credentials */
          268  +  int isAdmin            /* Modified admin privilege for the user */
   269    269   ){
   270    270     sqlite3_stmt *pStmt;
   271    271     if( db->auth.authLevel<UAUTH_User ){
   272    272       /* Must be logged in to make a change */
   273    273       return SQLITE_AUTH;
   274    274     }
   275    275     if( strcmp(db->auth.zAuthUser, zUsername)!=0 ){

Changes to src/shell.c.

  3447   3447       }
  3448   3448       if( strcmp(azArg[1],"login")==0 ){
  3449   3449         if( nArg!=4 ){
  3450   3450           fprintf(stderr, "Usage: .user login USER PASSWORD\n");
  3451   3451           rc = 1;
  3452   3452           goto meta_command_exit;
  3453   3453         }
  3454         -      rc = sqlite3_user_authenticate(p->db, azArg[2], (int)strlen(azArg[3]), azArg[3]);
         3454  +      rc = sqlite3_user_authenticate(p->db, azArg[2], azArg[3],
         3455  +                                    (int)strlen(azArg[3]));
  3455   3456         if( rc ){
  3456   3457           fprintf(stderr, "Authentication failed for user %s\n", azArg[2]);
  3457   3458           rc = 1;
  3458   3459         }
  3459   3460       }else if( strcmp(azArg[1],"add")==0 ){
  3460   3461         if( nArg!=5 ){
  3461         -        fprintf(stderr, "Usage: .user add USER ISADMIN PASSWORD\n");
         3462  +        fprintf(stderr, "Usage: .user add USER PASSWORD ISADMIN\n");
  3462   3463           rc = 1;
  3463   3464           goto meta_command_exit;
  3464   3465         }
  3465         -      rc = sqlite3_user_add(p->db, azArg[2], booleanValue(azArg[3]),
  3466         -                            (int)strlen(azArg[4]), azArg[4]);
         3466  +      rc = sqlite3_user_add(p->db, azArg[2],
         3467  +                            azArg[3], (int)strlen(azArg[3]),
         3468  +                            booleanValue(azArg[4]));
  3467   3469         if( rc ){
  3468   3470           fprintf(stderr, "User-Add failed: %d\n", rc);
  3469   3471           rc = 1;
  3470   3472         }
  3471   3473       }else if( strcmp(azArg[1],"edit")==0 ){
  3472   3474         if( nArg!=5 ){
  3473         -        fprintf(stderr, "Usage: .user edit USER ISADMIN PASSWORD\n");
         3475  +        fprintf(stderr, "Usage: .user edit USER PASSWORD ISADMIN\n");
  3474   3476           rc = 1;
  3475   3477           goto meta_command_exit;
  3476   3478         }
  3477         -      rc = sqlite3_user_change(p->db, azArg[2], booleanValue(azArg[3]),
  3478         -                              (int)strlen(azArg[4]), azArg[4]);
         3479  +      rc = sqlite3_user_change(p->db, azArg[2],
         3480  +                              azArg[3], (int)strlen(azArg[3]),
         3481  +                              booleanValue(azArg[4]));
  3479   3482         if( rc ){
  3480   3483           fprintf(stderr, "User-Edit failed: %d\n", rc);
  3481   3484           rc = 1;
  3482   3485         }
  3483   3486       }else if( strcmp(azArg[1],"delete")==0 ){
  3484   3487         if( nArg!=3 ){
  3485   3488           fprintf(stderr, "Usage: .user delete USER\n");

Changes to src/test1.c.

  6493   6493     return TCL_OK;
  6494   6494    sql_error:
  6495   6495     Tcl_AppendResult(interp, "sql error: ", sqlite3_errmsg(db), 0);
  6496   6496     return TCL_ERROR;
  6497   6497   }
  6498   6498   
  6499   6499   
         6500  +#ifdef SQLITE_USER_AUTHENTICATION
         6501  +#include "sqlite3userauth.h"
         6502  +/*
         6503  +** tclcmd:  sqlite3_user_authenticate DB USERNAME PASSWORD
         6504  +*/
         6505  +static int test_user_authenticate(
         6506  +  ClientData clientData, /* Unused */
         6507  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
         6508  +  int objc,              /* Number of arguments */
         6509  +  Tcl_Obj *CONST objv[]  /* Command arguments */
         6510  +){
         6511  +  char *zUser = 0;
         6512  +  char *zPasswd = 0;
         6513  +  int nPasswd = 0;
         6514  +  sqlite3 *db;
         6515  +  int rc;
         6516  +
         6517  +  if( objc!=4 ){
         6518  +    Tcl_WrongNumArgs(interp, 1, objv, "DB USERNAME PASSWORD");
         6519  +    return TCL_ERROR;
         6520  +  }
         6521  +  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
         6522  +    return TCL_ERROR;
         6523  +  }
         6524  +  zUser = Tcl_GetString(objv[2]);
         6525  +  zPasswd = Tcl_GetStringFromObj(objv[3], &nPasswd);
         6526  +  rc = sqlite3_user_authenticate(db, zUser, zPasswd, nPasswd);
         6527  +  Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC);
         6528  +  return TCL_OK;
         6529  +}
         6530  +#endif /* SQLITE_USER_AUTHENTICATION */
         6531  +
         6532  +#ifdef SQLITE_USER_AUTHENTICATION
         6533  +/*
         6534  +** tclcmd:  sqlite3_user_add DB USERNAME PASSWORD ISADMIN
         6535  +*/
         6536  +static int test_user_add(
         6537  +  ClientData clientData, /* Unused */
         6538  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
         6539  +  int objc,              /* Number of arguments */
         6540  +  Tcl_Obj *CONST objv[]  /* Command arguments */
         6541  +){
         6542  +  char *zUser = 0;
         6543  +  char *zPasswd = 0;
         6544  +  int nPasswd = 0;
         6545  +  int isAdmin = 0;
         6546  +  sqlite3 *db;
         6547  +  int rc;
         6548  +
         6549  +  if( objc!=5 ){
         6550  +    Tcl_WrongNumArgs(interp, 1, objv, "DB USERNAME PASSWORD ISADMIN");
         6551  +    return TCL_ERROR;
         6552  +  }
         6553  +  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
         6554  +    return TCL_ERROR;
         6555  +  }
         6556  +  zUser = Tcl_GetString(objv[2]);
         6557  +  zPasswd = Tcl_GetStringFromObj(objv[3], &nPasswd);
         6558  +  Tcl_GetBooleanFromObj(interp, objv[4], &isAdmin);
         6559  +  rc = sqlite3_user_add(db, zUser, zPasswd, nPasswd, isAdmin);
         6560  +  Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC);
         6561  +  return TCL_OK;
         6562  +}
         6563  +#endif /* SQLITE_USER_AUTHENTICATION */
         6564  +
         6565  +#ifdef SQLITE_USER_AUTHENTICATION
         6566  +/*
         6567  +** tclcmd:  sqlite3_user_change DB USERNAME PASSWORD ISADMIN
         6568  +*/
         6569  +static int test_user_change(
         6570  +  ClientData clientData, /* Unused */
         6571  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
         6572  +  int objc,              /* Number of arguments */
         6573  +  Tcl_Obj *CONST objv[]  /* Command arguments */
         6574  +){
         6575  +  char *zUser = 0;
         6576  +  char *zPasswd = 0;
         6577  +  int nPasswd = 0;
         6578  +  int isAdmin = 0;
         6579  +  sqlite3 *db;
         6580  +  int rc;
         6581  +
         6582  +  if( objc!=5 ){
         6583  +    Tcl_WrongNumArgs(interp, 1, objv, "DB USERNAME PASSWORD ISADMIN");
         6584  +    return TCL_ERROR;
         6585  +  }
         6586  +  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
         6587  +    return TCL_ERROR;
         6588  +  }
         6589  +  zUser = Tcl_GetString(objv[2]);
         6590  +  zPasswd = Tcl_GetStringFromObj(objv[3], &nPasswd);
         6591  +  Tcl_GetBooleanFromObj(interp, objv[4], &isAdmin);
         6592  +  rc = sqlite3_user_change(db, zUser, zPasswd, nPasswd, isAdmin);
         6593  +  Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC);
         6594  +  return TCL_OK;
         6595  +}
         6596  +#endif /* SQLITE_USER_AUTHENTICATION */
         6597  +
         6598  +#ifdef SQLITE_USER_AUTHENTICATION
         6599  +/*
         6600  +** tclcmd:  sqlite3_user_delete DB USERNAME
         6601  +*/
         6602  +static int test_user_delete(
         6603  +  ClientData clientData, /* Unused */
         6604  +  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
         6605  +  int objc,              /* Number of arguments */
         6606  +  Tcl_Obj *CONST objv[]  /* Command arguments */
         6607  +){
         6608  +  char *zUser = 0;
         6609  +  sqlite3 *db;
         6610  +  int rc;
         6611  +
         6612  +  if( objc!=3 ){
         6613  +    Tcl_WrongNumArgs(interp, 1, objv, "DB USERNAME");
         6614  +    return TCL_ERROR;
         6615  +  }
         6616  +  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
         6617  +    return TCL_ERROR;
         6618  +  }
         6619  +  zUser = Tcl_GetString(objv[2]);
         6620  +  rc = sqlite3_user_delete(db, zUser);
         6621  +  Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC);
         6622  +  return TCL_OK;
         6623  +}
         6624  +#endif /* SQLITE_USER_AUTHENTICATION */
         6625  +
  6500   6626   /*
  6501   6627   ** Register commands with the TCL interpreter.
  6502   6628   */
  6503   6629   int Sqlitetest1_Init(Tcl_Interp *interp){
  6504   6630     extern int sqlite3_search_count;
  6505   6631     extern int sqlite3_found_count;
  6506   6632     extern int sqlite3_interrupt_count;
................................................................................
  6730   6856        { "sqlite3_test_control", test_test_control },
  6731   6857   #if SQLITE_OS_UNIX
  6732   6858        { "getrusage", test_getrusage },
  6733   6859   #endif
  6734   6860        { "load_static_extension", tclLoadStaticExtensionCmd },
  6735   6861        { "sorter_test_fakeheap", sorter_test_fakeheap },
  6736   6862        { "sorter_test_sort4_helper", sorter_test_sort4_helper },
         6863  +#ifdef SQLITE_USER_AUTHENTICATION
         6864  +     { "sqlite3_user_authenticate", test_user_authenticate, 0 },
         6865  +     { "sqlite3_user_add",          test_user_add,          0 },
         6866  +     { "sqlite3_user_change",       test_user_change,       0 },
         6867  +     { "sqlite3_user_delete",       test_user_delete,       0 },
         6868  +#endif
         6869  +
  6737   6870     };
  6738   6871     static int bitmask_size = sizeof(Bitmask)*8;
  6739   6872     int i;
  6740   6873     extern int sqlite3_sync_count, sqlite3_fullsync_count;
  6741   6874     extern int sqlite3_opentemp_count;
  6742   6875     extern int sqlite3_like_count;
  6743   6876     extern int sqlite3_xferopt_count;

Added test/userauth01.test.

            1  +# 2014-09-10
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +# 
           12  +# This file implements tests of the SQLITE_USER_AUTHENTICATION extension.
           13  +#
           14  +
           15  +set testdir [file dirname $argv0]
           16  +source $testdir/tester.tcl
           17  +set testprefix userauth01
           18  +
           19  +ifcapable !userauth {
           20  +  finish_test
           21  +  return
           22  +}
           23  +
           24  +# Create a no-authentication-required database
           25  +#
           26  +do_execsql_test userauth01-1.0 {
           27  +  CREATE TABLE t1(x);
           28  +  INSERT INTO t1 VALUES(1),(2.5),('three'),(x'4444'),(NULL);
           29  +  SELECT quote(x) FROM t1 ORDER BY x;
           30  +  SELECT name FROM sqlite_master;
           31  +} {NULL 1 2.5 'three' X'4444' t1}
           32  +
           33  +# Calling sqlite3_user_authenticate() on a no-authentication-required
           34  +# database connection is a harmless no-op.  
           35  +#
           36  +do_test userauth01-1.1 {
           37  +  sqlite3_user_authenticate db alice pw-4-alice
           38  +  execsql {
           39  +    SELECT quote(x) FROM t1 ORDER BY x;
           40  +    SELECT name FROM sqlite_master;
           41  +  }
           42  +} {NULL 1 2.5 'three' X'4444' t1}
           43  +
           44  +# If sqlite3_user_add(D,U,P,N,A) is called on a no-authentication-required
           45  +# database and A is false, then the call fails with an SQLITE_AUTH error.
           46  +#
           47  +do_test userauth01-1.2 {
           48  +  sqlite3_user_add db bob pw-4-bob 0
           49  +} {SQLITE_AUTH}
           50  +do_test userauth01-1.3 {
           51  +  execsql {
           52  +    SELECT quote(x) FROM t1 ORDER BY x;
           53  +    SELECT name FROM sqlite_master;
           54  +  }
           55  +} {NULL 1 2.5 'three' X'4444' t1}
           56  +
           57  +# When called on a no-authentication-required
           58  +# database and when A is true, the sqlite3_user_add(D,U,P,N,A) routine
           59  +# converts the database into an authentication-required database and
           60  +# logs the database connection D in using user U with password P,N.
           61  +#  
           62  +do_test userauth01-1.4 {
           63  +  sqlite3_user_add db alice pw-4-alice 1
           64  +} {SQLITE_OK}
           65  +do_test userauth01-1.5 {
           66  +  execsql {
           67  +    SELECT quote(x) FROM t1 ORDER BY x;
           68  +    SELECT uname, isadmin FROM sqlite_user ORDER BY uname;
           69  +    SELECT name FROM sqlite_master ORDER BY name;
           70  +  }
           71  +} {NULL 1 2.5 'three' X'4444' alice 1 sqlite_user t1}
           72  +
           73  +
           74  +finish_test