dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Copyright (c) 2006 D. Richard Hipp dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** This program is free software; you can redistribute it and/or dbda8d6ce9 2007-07-21 drh: ** modify it under the terms of the GNU General Public dbda8d6ce9 2007-07-21 drh: ** License version 2 as published by the Free Software Foundation. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** This program is distributed in the hope that it will be useful, dbda8d6ce9 2007-07-21 drh: ** but WITHOUT ANY WARRANTY; without even the implied warranty of dbda8d6ce9 2007-07-21 drh: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dbda8d6ce9 2007-07-21 drh: ** General Public License for more details. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** You should have received a copy of the GNU General Public dbda8d6ce9 2007-07-21 drh: ** License along with this library; if not, write to the dbda8d6ce9 2007-07-21 drh: ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, dbda8d6ce9 2007-07-21 drh: ** Boston, MA 02111-1307, USA. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** Author contact information: dbda8d6ce9 2007-07-21 drh: ** drh@hwaci.com dbda8d6ce9 2007-07-21 drh: ** http://www.hwaci.com/drh/ dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ******************************************************************************* dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** Commands and procedures used for creating, processing, editing, and dbda8d6ce9 2007-07-21 drh: ** querying information about users. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: #include "config.h" dbda8d6ce9 2007-07-21 drh: #include "user.h" dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Strip leading and trailing space from a string and add the string dbda8d6ce9 2007-07-21 drh: ** onto the end of a blob. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static void strip_string(Blob *pBlob, char *z){ dbda8d6ce9 2007-07-21 drh: int i; dbda8d6ce9 2007-07-21 drh: blob_reset(pBlob); dbda8d6ce9 2007-07-21 drh: while( isspace(*z) ){ z++; } dbda8d6ce9 2007-07-21 drh: for(i=0; z[i]; i++){ dbda8d6ce9 2007-07-21 drh: if( z[i]=='\r' || z[i]=='\n' ){ dbda8d6ce9 2007-07-21 drh: while( i>0 && isspace(z[i-1]) ){ i--; } dbda8d6ce9 2007-07-21 drh: z[i] = 0; dbda8d6ce9 2007-07-21 drh: break; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( z[i]<' ' ) z[i] = ' '; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: blob_append(pBlob, z, -1); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: 83c876b447 2007-09-21 anonymous: #ifdef __MINGW32__ 83c876b447 2007-09-21 anonymous: /* 83c876b447 2007-09-21 anonymous: ** getpass for Windows 83c876b447 2007-09-21 anonymous: */ 83c876b447 2007-09-21 anonymous: static char *getpass(const char *prompt){ 83c876b447 2007-09-21 anonymous: static char pwd[64]; 83c876b447 2007-09-21 anonymous: size_t i; 83c876b447 2007-09-21 anonymous: 83c876b447 2007-09-21 anonymous: fputs(prompt,stderr); 83c876b447 2007-09-21 anonymous: fflush(stderr); 83c876b447 2007-09-21 anonymous: for(i=0; i<sizeof(pwd)-1; ++i){ 83c876b447 2007-09-21 anonymous: pwd[i] = _getch(); 83c876b447 2007-09-21 anonymous: if(pwd[i]=='\r' || pwd[i]=='\n'){ 83c876b447 2007-09-21 anonymous: break; 83c876b447 2007-09-21 anonymous: } 83c876b447 2007-09-21 anonymous: /* BS or DEL */ 83c876b447 2007-09-21 anonymous: else if(i>0 && (pwd[i]==8 || pwd[i]==127)){ 83c876b447 2007-09-21 anonymous: i -= 2; 83c876b447 2007-09-21 anonymous: continue; 83c876b447 2007-09-21 anonymous: } 83c876b447 2007-09-21 anonymous: /* CTRL-C */ 83c876b447 2007-09-21 anonymous: else if(pwd[i]==3) { 83c876b447 2007-09-21 anonymous: i=0; 83c876b447 2007-09-21 anonymous: break; 83c876b447 2007-09-21 anonymous: } 83c876b447 2007-09-21 anonymous: /* ESC */ 83c876b447 2007-09-21 anonymous: else if(pwd[i]==27){ 83c876b447 2007-09-21 anonymous: i=0; 83c876b447 2007-09-21 anonymous: break; 83c876b447 2007-09-21 anonymous: } 83c876b447 2007-09-21 anonymous: else{ 83c876b447 2007-09-21 anonymous: fputc('*',stderr); 83c876b447 2007-09-21 anonymous: } 83c876b447 2007-09-21 anonymous: } 83c876b447 2007-09-21 anonymous: pwd[i]='\0'; 83c876b447 2007-09-21 anonymous: fputs("\n", stderr); 83c876b447 2007-09-21 anonymous: return pwd; 83c876b447 2007-09-21 anonymous: } 83c876b447 2007-09-21 anonymous: #endif 83c876b447 2007-09-21 anonymous: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Do a single prompt for a passphrase. Store the results in the blob. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static void prompt_for_passphrase(const char *zPrompt, Blob *pPassphrase){ dbda8d6ce9 2007-07-21 drh: char *z = getpass(zPrompt); dbda8d6ce9 2007-07-21 drh: strip_string(pPassphrase, z); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* e621b6dbe3 2007-07-30 drh: ** Prompt the user for a password. Store the result in the pPassphrase e621b6dbe3 2007-07-30 drh: ** blob. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** Behavior is controlled by the verify parameter: dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** 0 Just ask once. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** 1 If the first answer is a non-empty string, ask for dbda8d6ce9 2007-07-21 drh: ** verification. Repeat if the two strings do not match. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** 2 Ask twice, repeat if the strings do not match. dbda8d6ce9 2007-07-21 drh: */ e621b6dbe3 2007-07-30 drh: void prompt_for_password( e621b6dbe3 2007-07-30 drh: const char *zPrompt, e621b6dbe3 2007-07-30 drh: Blob *pPassphrase, e621b6dbe3 2007-07-30 drh: int verify e621b6dbe3 2007-07-30 drh: ){ dbda8d6ce9 2007-07-21 drh: Blob secondTry; dbda8d6ce9 2007-07-21 drh: blob_zero(pPassphrase); dbda8d6ce9 2007-07-21 drh: blob_zero(&secondTry); dbda8d6ce9 2007-07-21 drh: while(1){ dbda8d6ce9 2007-07-21 drh: prompt_for_passphrase(zPrompt, pPassphrase); dbda8d6ce9 2007-07-21 drh: if( verify==0 ) break; dbda8d6ce9 2007-07-21 drh: if( verify==1 && blob_size(pPassphrase)==0 ) break; dbda8d6ce9 2007-07-21 drh: prompt_for_passphrase("Again: ", &secondTry); dbda8d6ce9 2007-07-21 drh: if( blob_compare(pPassphrase, &secondTry) ){ dbda8d6ce9 2007-07-21 drh: printf("Passphrases do not match. Try again...\n"); dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: break; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: blob_reset(&secondTry); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Prompt the user to enter a single line of text. dbda8d6ce9 2007-07-21 drh: */ e37451d9c2 2007-08-01 drh: void prompt_user(const char *zPrompt, Blob *pIn){ dbda8d6ce9 2007-07-21 drh: char *z; dbda8d6ce9 2007-07-21 drh: char zLine[1000]; dbda8d6ce9 2007-07-21 drh: blob_zero(pIn); dbda8d6ce9 2007-07-21 drh: printf("%s", zPrompt); dbda8d6ce9 2007-07-21 drh: fflush(stdout); dbda8d6ce9 2007-07-21 drh: z = fgets(zLine, sizeof(zLine), stdin); dbda8d6ce9 2007-07-21 drh: if( z ){ dbda8d6ce9 2007-07-21 drh: strip_string(pIn, z); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** COMMAND: user dbda8d6ce9 2007-07-21 drh: ** 6607844a01 2007-08-18 drh: ** Usage: %fossil user SUBCOMMAND ... ?-R|--repository FILE? 6607844a01 2007-08-18 drh: ** 6607844a01 2007-08-18 drh: ** Run various subcommands on users of the open repository or of 6607844a01 2007-08-18 drh: ** the repository identified by the -R or --repository option. 6607844a01 2007-08-18 drh: ** 6607844a01 2007-08-18 drh: ** %fossil user capabilities USERNAME ?STRING? 6607844a01 2007-08-18 drh: ** 6607844a01 2007-08-18 drh: ** Query or set the capabilities for user USERNAME 6607844a01 2007-08-18 drh: ** 6607844a01 2007-08-18 drh: ** %fossil user default ?USERNAME? 6607844a01 2007-08-18 drh: ** 6607844a01 2007-08-18 drh: ** Query or set the default user. The default user is the 6607844a01 2007-08-18 drh: ** user for command-line interaction. 6607844a01 2007-08-18 drh: ** 6607844a01 2007-08-18 drh: ** %fossil user list 6607844a01 2007-08-18 drh: ** 6607844a01 2007-08-18 drh: ** List all users known to the repository 6607844a01 2007-08-18 drh: ** 783df88ba8 2007-10-26 drh: ** %fossil user new ?USERNAME? 6607844a01 2007-08-18 drh: ** 6607844a01 2007-08-18 drh: ** Create a new user in the repository. Users can never be 6607844a01 2007-08-18 drh: ** deleted. They can be denied all access but they must continue 6607844a01 2007-08-18 drh: ** to exist in the database. 6607844a01 2007-08-18 drh: ** 6607844a01 2007-08-18 drh: ** %fossil user password USERNAME 6607844a01 2007-08-18 drh: ** 6607844a01 2007-08-18 drh: ** Change the web access password for a user. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: void user_cmd(void){ 916b6e4b3b 2007-07-21 drh: int n; dbda8d6ce9 2007-07-21 drh: db_find_and_open_repository(); dbda8d6ce9 2007-07-21 drh: if( g.argc<3 ){ 916b6e4b3b 2007-07-21 drh: usage("capabilities|default|list|new|password ..."); dbda8d6ce9 2007-07-21 drh: } 916b6e4b3b 2007-07-21 drh: n = strlen(g.argv[2]); 916b6e4b3b 2007-07-21 drh: if( n>=2 && strncmp(g.argv[2],"new",n)==0 ){ dbda8d6ce9 2007-07-21 drh: Blob passwd, login, contact; dbda8d6ce9 2007-07-21 drh: 783df88ba8 2007-10-26 drh: if( g.argc>=4 ){ 783df88ba8 2007-10-26 drh: blob_zero(&login); 783df88ba8 2007-10-26 drh: blob_append(&login, g.argv[3], -1); 783df88ba8 2007-10-26 drh: }else{ 783df88ba8 2007-10-26 drh: prompt_user("login: ", &login); 783df88ba8 2007-10-26 drh: } 783df88ba8 2007-10-26 drh: if( db_exists("SELECT 1 FROM user WHERE login=%B", &login) ){ 783df88ba8 2007-10-26 drh: fossil_fatal("user %b already exists", &login); 783df88ba8 2007-10-26 drh: } dbda8d6ce9 2007-07-21 drh: prompt_user("contact-info: ", &contact); e621b6dbe3 2007-07-30 drh: prompt_for_password("password: ", &passwd, 1); dbda8d6ce9 2007-07-21 drh: db_multi_exec( dbda8d6ce9 2007-07-21 drh: "INSERT INTO user(login,pw,cap,info)" dbda8d6ce9 2007-07-21 drh: "VALUES(%B,%B,'jnor',%B)", dbda8d6ce9 2007-07-21 drh: &login, &passwd, &contact dbda8d6ce9 2007-07-21 drh: ); 916b6e4b3b 2007-07-21 drh: }else if( n>=2 && strncmp(g.argv[2],"default",n)==0 ){ dbda8d6ce9 2007-07-21 drh: user_select(); dbda8d6ce9 2007-07-21 drh: if( g.argc==3 ){ dbda8d6ce9 2007-07-21 drh: printf("%s\n", g.zLogin); dbda8d6ce9 2007-07-21 drh: }else{ e2a42f7a68 2008-01-29 drh: if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.argv[3]) ){ e2a42f7a68 2008-01-29 drh: fossil_fatal("no such user: %s", g.argv[3]); e2a42f7a68 2008-01-29 drh: } e2a42f7a68 2008-01-29 drh: if( g.localOpen ){ e2a42f7a68 2008-01-29 drh: db_lset("default-user", g.argv[3]); e2a42f7a68 2008-01-29 drh: }else{ e2a42f7a68 2008-01-29 drh: db_set("default-user", g.argv[3], 0); e2a42f7a68 2008-01-29 drh: } dbda8d6ce9 2007-07-21 drh: } 916b6e4b3b 2007-07-21 drh: }else if( n>=2 && strncmp(g.argv[2],"list",n)==0 ){ dbda8d6ce9 2007-07-21 drh: Stmt q; dbda8d6ce9 2007-07-21 drh: db_prepare(&q, "SELECT login, info FROM user ORDER BY login"); dbda8d6ce9 2007-07-21 drh: while( db_step(&q)==SQLITE_ROW ){ dbda8d6ce9 2007-07-21 drh: printf("%-12s %s\n", db_column_text(&q, 0), db_column_text(&q, 1)); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: db_finalize(&q); 916b6e4b3b 2007-07-21 drh: }else if( n>=2 && strncmp(g.argv[2],"password",2)==0 ){ dbda8d6ce9 2007-07-21 drh: char *zPrompt; dbda8d6ce9 2007-07-21 drh: int uid; dbda8d6ce9 2007-07-21 drh: Blob pw; df3d6cbff5 2007-09-24 mjanssen: if( g.argc!=4 ) usage("password USERNAME"); dbda8d6ce9 2007-07-21 drh: uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", g.argv[3]); dbda8d6ce9 2007-07-21 drh: if( uid==0 ){ dbda8d6ce9 2007-07-21 drh: fossil_fatal("no such user: %s", g.argv[3]); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: zPrompt = mprintf("new passwd for %s: ", g.argv[3]); e621b6dbe3 2007-07-30 drh: prompt_for_password(zPrompt, &pw, 1); dbda8d6ce9 2007-07-21 drh: if( blob_size(&pw)==0 ){ dbda8d6ce9 2007-07-21 drh: printf("password unchanged\n"); dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: db_multi_exec("UPDATE user SET pw=%B WHERE uid=%d", &pw, uid); dbda8d6ce9 2007-07-21 drh: } 916b6e4b3b 2007-07-21 drh: }else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){ 916b6e4b3b 2007-07-21 drh: int uid; 916b6e4b3b 2007-07-21 drh: if( g.argc!=4 && g.argc!=5 ){ 916b6e4b3b 2007-07-21 drh: usage("user capabilities USERNAME ?PERMISSIONS?"); 916b6e4b3b 2007-07-21 drh: } 916b6e4b3b 2007-07-21 drh: uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", g.argv[3]); 916b6e4b3b 2007-07-21 drh: if( uid==0 ){ 916b6e4b3b 2007-07-21 drh: fossil_fatal("no such user: %s", g.argv[3]); 916b6e4b3b 2007-07-21 drh: } 916b6e4b3b 2007-07-21 drh: if( g.argc==5 ){ 916b6e4b3b 2007-07-21 drh: db_multi_exec( 916b6e4b3b 2007-07-21 drh: "UPDATE user SET cap=%Q WHERE uid=%d", g.argv[4], 916b6e4b3b 2007-07-21 drh: uid 916b6e4b3b 2007-07-21 drh: ); 916b6e4b3b 2007-07-21 drh: } 916b6e4b3b 2007-07-21 drh: printf("%s\n", db_text(0, "SELECT cap FROM user WHERE uid=%d", uid)); dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: fossil_panic("user subcommand should be one of: " 916b6e4b3b 2007-07-21 drh: "capabilities default list new password"); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Attempt to set the user to zLogin dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static int attempt_user(const char *zLogin){ dbda8d6ce9 2007-07-21 drh: int uid; dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: if( zLogin==0 ){ dbda8d6ce9 2007-07-21 drh: return 0; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin); dbda8d6ce9 2007-07-21 drh: if( uid ){ dbda8d6ce9 2007-07-21 drh: g.userUid = uid; dbda8d6ce9 2007-07-21 drh: g.zLogin = mprintf("%s", zLogin); dbda8d6ce9 2007-07-21 drh: return 1; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: return 0; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Figure out what user is at the controls. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** (1) Use the --user and -U command-line options. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** (2) If the local database is open, check in VVAR. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** (3) Check the default user in the repository dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** (4) Try the USER environment variable. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** (5) Use the first user in the USER table. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** The user name is stored in g.zLogin. The uid is in g.userUid. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: void user_select(void){ dbda8d6ce9 2007-07-21 drh: Stmt s; dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: if( g.userUid ) return; dbda8d6ce9 2007-07-21 drh: if( attempt_user(g.zLogin) ) return; dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: if( g.localOpen && attempt_user(db_lget("default-user",0)) ) return; dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: if( attempt_user(db_get("default-user", 0)) ) return; dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: if( attempt_user(getenv("USER")) ) return; dbda8d6ce9 2007-07-21 drh: 097479f99a 2007-09-26 drh: db_prepare(&s, "SELECT uid, login FROM user" 097479f99a 2007-09-26 drh: " WHERE login NOT IN ('anonymous','nobody')"); dbda8d6ce9 2007-07-21 drh: if( db_step(&s)==SQLITE_ROW ){ dbda8d6ce9 2007-07-21 drh: g.userUid = db_column_int(&s, 0); dbda8d6ce9 2007-07-21 drh: g.zLogin = mprintf("%s", db_column_text(&s, 1)); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: db_finalize(&s); dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: if( g.userUid==0 ){ dbda8d6ce9 2007-07-21 drh: db_prepare(&s, "SELECT uid, login FROM user"); dbda8d6ce9 2007-07-21 drh: if( db_step(&s)==SQLITE_ROW ){ dbda8d6ce9 2007-07-21 drh: g.userUid = db_column_int(&s, 0); dbda8d6ce9 2007-07-21 drh: g.zLogin = mprintf("%s", db_column_text(&s, 1)); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: db_finalize(&s); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: if( g.userUid==0 ){ dbda8d6ce9 2007-07-21 drh: db_multi_exec( dbda8d6ce9 2007-07-21 drh: "INSERT INTO user(login, pw, cap, info)" 097479f99a 2007-09-26 drh: "VALUES('anonymous', '', 'cfghjkmnoqw', '')" dbda8d6ce9 2007-07-21 drh: ); dbda8d6ce9 2007-07-21 drh: g.userUid = db_last_insert_rowid(); dbda8d6ce9 2007-07-21 drh: g.zLogin = "anonymous"; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: }