Overview
SHA1 Hash: | 916b6e4b3b701915f2670ce40e776abbd05f71d0 |
---|---|
Date: | 2007-07-21 19:32:06 |
User: | drh |
Comment: | Improvements to web-based user management. |
Timelines: | ancestors | descendants | both | trunk |
Other Links: | files | ZIP archive | manifest |
Tags And Properties
- branch=trunk inherited from [a28c83647d]
- sym-trunk inherited from [a28c83647d]
Changes
[hide diffs]Modified src/db.c from [2bb8e10f4d] to [e56e457646].
@@ -572,23 +572,34 @@ ** fall back to the -R or --repository option. ** ** Error out if the repository cannot be opened. */ void db_find_and_open_repository(void){ - db_open_repository(find_option("repository", "R", 1)); - if( g.repositoryOpen==0 ){ - fossil_fatal("use --repository or -R to specific the repository database"); + char *zRep = find_option("repository", "R", 1); + if( zRep==0 ){ + if( db_open_local()==0 ){ + goto rep_not_found; + } + zRep = db_lget("repository", 0); + if( zRep==0 ){ + goto rep_not_found; + } + } + db_open_repository(zRep); + if( g.repositoryOpen ){ + return; } +rep_not_found: + fossil_fatal("use --repository or -R to specific the repository database"); } /* ** Open the local database. If unable, exit with an error. */ void db_must_be_within_tree(void){ if( db_open_local()==0 ){ - fprintf(stderr,"%s: not within an open checkout\n", g.argv[0]); - exit(1); + fossil_fatal("not within an open checkout"); } db_open_repository(0); } /*
Modified src/login.c from [ed7e76839e] to [eed2c5dedd].
@@ -81,11 +81,11 @@ } } if( zUsername!=0 && zPasswd!=0 && strcmp(zUsername,"anonymous")!=0 ){ int uid = db_int(0, "SELECT uid FROM user" - " WHERE login=%Q AND pw=%B", zUsername, zPasswd); + " WHERE login=%Q AND pw=%Q", zUsername, zPasswd); if( uid<=0 ){ sleep(1); zErrMsg = @ <p><font color="red"> @ You entered an unknown user or an incorrect password. @@ -92,11 +92,11 @@ @ </font></p> ; }else{ char *zCookie; const char *zCookieName = login_cookie_name(); - const char *zIpAddr = PD("REMOTE_ADDR","x"); + const char *zIpAddr = PD("REMOTE_ADDR","nil"); const char *zExpire = db_get("cookie-expire","8766"); int expires; zCookie = db_text(0, "SELECT '%d/' || hex(randomblob(25))", uid); expires = atoi(zExpire)*3600; @@ -194,14 +194,15 @@ ** user credentials. */ zRemoteAddr = PD("REMOTE_ADDR","nil"); if( strcmp(zRemoteAddr, "127.0.0.1")==0 && db_get_int("authenticate-localhost",1)==0 ){ - uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%s%'"); + uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'"); g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid); zCap = "s"; g.noPswd = 1; + g.isAnon = 0; } /* Check the login cookie to see if it matches a known valid user. */ if( uid==0 && (zCookie = P(login_cookie_name()))!=0 ){ @@ -214,18 +215,20 @@ atoi(zCookie), zCookie, zRemoteAddr ); } if( uid==0 ){ + g.isAnon = 1; g.zLogin = ""; zCap = db_get("nologin-cap","onrj"); }else if( zCap==0 ){ Stmt s; db_prepare(&s, "SELECT login, cap FROM user WHERE uid=%d", uid); db_step(&s); g.zLogin = db_column_malloc(&s, 0); zCap = db_column_malloc(&s, 1); + g.isAnon = 0; db_finalize(&s); } g.userUid = uid; login_set_capabilities(zCap);
Modified src/setup.c from [cb7dcac5a1] to [570fc89ab7].
@@ -77,50 +77,312 @@ ** Show a list of users. Clicking on any user jumps to the edit ** screen for that user. */ void setup_ulist(void){ Stmt s; + + style_footer(); login_check_credentials(); - if( !g.okSetup ){ + if( !g.okWrite || g.isAnon ){ login_needed(); - } - + return; + } + + style_submenu_element("Add", "Add User", "setup_uedit"); style_header(); - - @ <table border="0" cellpadding="0" cellspacing="0"> - db_prepare(&s, "SELECT uid, login, cap FROM repuser ORDER BY login"); + @ <h2>List Of Users</h2> + @ <table cellspacing=0 cellpadding=0 border=0> + @ <tr> + @ <th align="right"><nobr>User ID</nobr></th> + @ <th> Capabilities </th> + @ <th><nobr>Contact Info</nobr></th> + @ </tr> + db_prepare(&s, "SELECT uid, login, cap, info FROM user ORDER BY login"); while( db_step(&s)==SQLITE_ROW ){ - @ <tr><td><a href="%s(g.zBaseURL)/setup_uedit?uid=%d(db_column_int(&s,0))"> - @ %h(db_column_text(&s,1))</a></td><td width="10"></td> - @ <td>%h(db_column_text(&s,2))</td></tr> - } - db_finalize(&s); + @ <tr> + @ <td align="right"> + if( g.okAdmin ){ + @ <a href="setup_uedit?id=%d(db_column_int(&s,0))"> + } + @ <nobr>%h(db_column_text(&s,1))</nobr> + if( g.okAdmin ){ + @ </a> + } + @ </td> + @ <td align="center">%s(db_column_text(&s,2))</td> + @ <td align="center">%s(db_column_text(&s,3))</td> + @ </tr> + } + @ </table> + @ <p><hr> + @ <b>Notes:</b> + @ <ol> + @ <li><p>The permission flags are as follows:</p> + @ <table> + @ <tr><td>a</td><td width="10"></td> + @ <td>Admin: Create or delete users and ticket report formats</td></tr> + @ <tr><td>d</td><td></td> + @ <td>Delete: Erase anonymous wiki, tickets, and attachments</td></tr> + @ <tr><td>i</td><td></td> + @ <td>Check-in: Add new code to the repository</td></tr> + @ <tr><td>j</td><td></td><td>Read-Wiki: View wiki pages</td></tr> + @ <tr><td>k</td><td></td><td>Wiki: Create or modify wiki pages</td></tr> + @ <tr><td>n</td><td></td><td>New: Create new tickets</td></tr> + @ <tr><td>o</td><td></td> + @ <td>Check-out: Read code out of the repository</td></tr> + @ <tr><td>p</td><td></td><td>Password: Change password</td></tr> + @ <tr><td>q</td><td></td><td>Query: Create or edit report formats</td></tr> + @ <tr><td>r</td><td></td><td>Read: View tickets and change histories</td></tr> + @ <tr><td>s</td><td></td><td>Setup: Change CVSTrac options</td></tr> + @ <tr><td>w</td><td></td><td>Write: Edit tickets</td></tr> @ </table> - + @ </p></li> + @ + @ <li><p> + @ If a user named "<b>anonymous</b>" exists, then anyone can access + @ the server without having to log in. The permissions on the + @ anonymous user determine the access rights for anyone who is not + @ logged in. + @ </p></li> + @ + @ </ol> style_footer(); } /* -** WEBPAGE: setup_uedit -** -** Edit the user with REPUSER.UID equal to the "u" query parameter. +** WEBPAGE: /setup_uedit */ -void setup_uedit(void){ +void user_edit(void){ + const char *zId, *zLogin, *zInfo, *zCap; + char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap ; + char *oak, *oad, *oaq; + int doWrite; int uid; - + int higherUser = 0; /* True if user being edited is SETUP and the */ + /* user doing the editing is ADMIN. Disallow editing */ + + /* Must have ADMIN privleges to access this page + */ login_check_credentials(); - if( !g.okSetup ){ - login_needed(); - } - uid = atoi(PD("u","0")); - if( uid<=0 ){ + if( !g.okAdmin ){ login_needed(); return; } + + /* Check to see if an ADMIN user is trying to edit a SETUP account. + ** Don't allow that. + */ + zId = PD("id", "0"); + uid = atoi(zId); + if( zId && !g.okSetup && uid>0 ){ + char *zOldCaps; + zOldCaps = db_text(0, "SELECT caps FROM user WHERE uid=%d",uid); + higherUser = zOldCaps && strchr(zOldCaps,'s'); + } + + if( P("can") ){ + cgi_redirect("setup_ulist"); + return; + } + + /* If we have all the necessary information, write the new or + ** modified user record. After writing the user record, redirect + ** to the page that displays a list of users. + */ + doWrite = cgi_all("login","info","pw") && !higherUser; + if( doWrite ){ + const char *zPw; + const char *zLogin; + char zCap[20]; + int i = 0; + int aa = P("aa")!=0; + int ad = P("ad")!=0; + int ai = P("ai")!=0; + int aj = P("aj")!=0; + int ak = P("ak")!=0; + int an = P("an")!=0; + int ao = P("ao")!=0; + int ap = P("ap")!=0; + int aq = P("aq")!=0; + int ar = P("ar")!=0; + int as = g.okSetup && P("as")!=0; + int aw = P("aw")!=0; + if( as ) aa = 1; + if( aa ) ai = aw = ap = 1; + if( aw ) an = ar = 1; + if( ai ) ao = 1; + if( ak ) aj = 1; + if( aa ){ zCap[i++] = 'a'; } + if( ad ){ zCap[i++] = 'd'; } + if( ai ){ zCap[i++] = 'i'; } + if( aj ){ zCap[i++] = 'j'; } + if( ak ){ zCap[i++] = 'k'; } + if( an ){ zCap[i++] = 'n'; } + if( ao ){ zCap[i++] = 'o'; } + if( ap ){ zCap[i++] = 'p'; } + if( aq ){ zCap[i++] = 'q'; } + if( ar ){ zCap[i++] = 'r'; } + if( as ){ zCap[i++] = 's'; } + if( aw ){ zCap[i++] = 'w'; } + + zCap[i] = 0; + zPw = P("pw"); + if( zPw==0 || zPw[0]==0 ){ + zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid); + } + zLogin = P("login"); + if( uid>0 && + db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid) + ){ + style_header(); + @ <font color="red">Login "%h(zLogin)" is already used by a different + @ user.</font> + @ + @ <p><a href="setup_uedit?id=%d(uid))>[Bummer]</a></p> + style_footer(); + return; + } + db_multi_exec( + "REPLACE INTO user(uid,login,info,pw,cap) " + "VALUES(nullif(%d,0),%Q,%Q,%Q,'%s')", + uid, P("login"), P("info"), zPw, zCap + ); cgi_redirect("setup_ulist"); - assert(0); - } + return; + } + + /* Load the existing information about the user, if any + */ + zLogin = ""; + zInfo = ""; + zCap = ""; + oaa = oad = oai = oaj = oak = oan = oao = oap = oaq = oar = oas = oaw = ""; + if( uid ){ + zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid); + zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid); + zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid); + if( strchr(zCap, 'a') ) oaa = " checked"; + if( strchr(zCap, 'd') ) oad = " checked"; + if( strchr(zCap, 'i') ) oai = " checked"; + if( strchr(zCap, 'j') ) oaj = " checked"; + if( strchr(zCap, 'k') ) oak = " checked"; + if( strchr(zCap, 'n') ) oan = " checked"; + if( strchr(zCap, 'o') ) oao = " checked"; + if( strchr(zCap, 'p') ) oap = " checked"; + if( strchr(zCap, 'q') ) oaq = " checked"; + if( strchr(zCap, 'r') ) oar = " checked"; + if( strchr(zCap, 's') ) oas = " checked"; + if( strchr(zCap, 'w') ) oaw = " checked"; + } + + /* Begin generating the page + */ + style_submenu_element("Cancel", "Cancel", "setup_ulist"); style_header(); + if( uid ){ + @ <h2>Edit User %h(zLogin)</h2> + }else{ + @ <h2>Add A New User</h2> + } + @ <table align="left" hspace="20" vspace="10"><tr><td> + @ <form action="%s(g.zPath)" method="POST"> + @ <table> + @ <tr> + @ <td align="right"><nobr>User ID:</nobr></td> + if( uid ){ + @ <td>%d(uid) <input type="hidden" name="id" value="%d(uid)"></td> + }else{ + @ <td>(new user)<input type="hidden" name="id" value=0></td> + } + @ </tr> + @ <tr> + @ <td align="right"><nobr>Login:</nobr></td> + @ <td><input type="text" name="login" value="%h(zLogin)"></td> + @ </tr> + @ <tr> + @ <td align="right"><nobr>Contact Info:</nobr></td> + @ <td><input type="text" name="info" size=40 value="%h(zInfo)"></td> + @ </tr> + @ <tr> + @ <td align="right" valign="top">Capabilities:</td> + @ <td> + @ <input type="checkbox" name="aa"%s(oaa)>Admin</input><br> + @ <input type="checkbox" name="ad"%s(oad)>Delete</input><br> + @ <input type="checkbox" name="ai"%s(oai)>Check-In</input><br> + @ <input type="checkbox" name="aj"%s(oaj)>Read Wiki</input><br> + @ <input type="checkbox" name="ak"%s(oak)>Write Wiki</input><br> + @ <input type="checkbox" name="an"%s(oan)>New Tkt</input><br> + @ <input type="checkbox" name="ao"%s(oao)>Check-Out</input><br> + @ <input type="checkbox" name="ap"%s(oap)>Password</input><br> + @ <input type="checkbox" name="aq"%s(oaq)>Query</input><br> + @ <input type="checkbox" name="ar"%s(oar)>Read</input><br> + if( g.okSetup ){ + @ <input type="checkbox" name="as"%s(oas)>Setup</input><br> + } + @ <input type="checkbox" name="aw"%s(oaw)>Write</input> + @ </td> + @ </tr> + @ <tr> + @ <td align="right">Password:</td> + @ <td><input type="password" name="pw" value=""></td> + @ </tr> + if( !higherUser ){ + @ <tr> + @ <td> </td> + @ <td><input type="submit" name="submit" value="Apply Changes"> + @ </tr> + } + @ </table></td></tr></table> + @ <p><b>Notes:</b></p> + @ <ol> + if( higherUser ){ + @ <li><p> + @ User %h(zId) has Setup privileges and you only have Admin privileges + @ so you are not permitted to make changes to %h(zId). + @ </p></li> + @ + } + @ <li><p> + @ The <b>Read</b> and <b>Write</b> privileges give the user the ability + @ to read and write tickets. The <b>New Tkt</b> capability means that + @ the user is able to create new tickets. + @ </p></li> + @ + @ <li><p> + @ The <b>Delete</b> privilege give the user the ability to erase + @ wiki, tickets, and atttachments that have been added by anonymous + @ users. This capability is intended for deletion of spam. + @ </p></li> + @ + @ <li><p> + @ The <b>Query</b> privilege allows the user to create or edit + @ report formats by specifying appropriate SQL. Users can run + @ existing reports without the Query privilege. + @ </p></li> + @ + @ <li><p> + @ An <b>Admin</b> user can add other users, create new ticket report + @ formats, and change system defaults. But only the <b>Setup</b> user + @ is able to change the repository to + @ which this program is linked. + @ </p></li> + @ + if( zId==0 || strcmp(zId,"anonymous")==0 ){ + @ <li><p> + @ No login is required for user "<b>anonymous</b>". The capabilities + @ of this user are available to anyone without supplying a username or + @ password. To disable anonymous access, make sure there is no user + @ with an ID of <b>anonymous</b>. + @ </p></li> + @ + @ <li><p> + @ The password for the "<b>anonymous</b>" user is used for anonymous + @ access. The recommended value for the anonymous password + @ is "anonymous". + @ </p></li> + } + @ </form> style_footer(); } + /* ** Generate a checkbox for an attribute. */ static void onoff_attribute(
Modified src/style.c from [73ef49d142] to [b4958eee41].
@@ -69,11 +69,11 @@ /* ** Draw the header. */ void style_header(void){ const char *zLogInOut = "Logout"; - /* login_check_credentials(); */ + login_check_credentials(); @ <html> @ <body bgcolor="white"> @ <hr size="1"> @ <table border="0" cellpadding="0" cellspacing="0" width="100%%"> @ <tr><td valign="top" align="left"> @@ -104,11 +104,11 @@ struct Submenu *p = &aSubmenu[i]; char *zTail = i<nSubmenu-1 ? " | " : ""; if( p->zLink==0 ){ @ <font color="#888888">%h(p->zLabel)</font> %s(zTail) }else{ - @ <a href="p->zLink">%h(p->zLabel)</a> %s(zTail) + @ <a href="%T(p->zLink)">%h(p->zLabel)</a> %s(zTail) } } } @ </td></tr></table> @ <hr size="1">
Modified src/user.c from [0a8a17727c] to [f41abe0610].
@@ -106,15 +106,17 @@ ** COMMAND: user ** ** Dispatcher for various user subcommands. */ void user_cmd(void){ + int n; db_find_and_open_repository(); if( g.argc<3 ){ - usage("create|default|list|password ..."); + usage("capabilities|default|list|new|password ..."); } - if( strcmp(g.argv[2],"create")==0 ){ + n = strlen(g.argv[2]); + if( n>=2 && strncmp(g.argv[2],"new",n)==0 ){ Blob passwd, login, contact; prompt_user("login: ", &login); prompt_user("contact-info: ", &contact); get_passphrase("password: ", &passwd, 1); @@ -121,27 +123,27 @@ db_multi_exec( "INSERT INTO user(login,pw,cap,info)" "VALUES(%B,%B,'jnor',%B)", &login, &passwd, &contact ); - }else if( strcmp(g.argv[2],"default")==0 ){ + }else if( n>=2 && strncmp(g.argv[2],"default",n)==0 ){ user_select(); if( g.argc==3 ){ printf("%s\n", g.zLogin); }else if( g.localOpen ){ db_lset("default-user", g.zLogin); }else{ db_set("default-user", g.zLogin); } - }else if( strcmp(g.argv[2],"list")==0 ){ + }else if( n>=2 && strncmp(g.argv[2],"list",n)==0 ){ Stmt q; db_prepare(&q, "SELECT login, info FROM user ORDER BY login"); while( db_step(&q)==SQLITE_ROW ){ printf("%-12s %s\n", db_column_text(&q, 0), db_column_text(&q, 1)); } db_finalize(&q); - }else if( strcmp(g.argv[2],"password")==0 ){ + }else if( n>=2 && strncmp(g.argv[2],"password",2)==0 ){ char *zPrompt; int uid; Blob pw; if( g.argc!=4 ) usage("user password USERNAME"); uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", g.argv[3]); @@ -153,13 +155,29 @@ if( blob_size(&pw)==0 ){ printf("password unchanged\n"); }else{ db_multi_exec("UPDATE user SET pw=%B WHERE uid=%d", &pw, uid); } + }else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){ + int uid; + if( g.argc!=4 && g.argc!=5 ){ + usage("user capabilities USERNAME ?PERMISSIONS?"); + } + uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", g.argv[3]); + if( uid==0 ){ + fossil_fatal("no such user: %s", g.argv[3]); + } + if( g.argc==5 ){ + db_multi_exec( + "UPDATE user SET cap=%Q WHERE uid=%d", g.argv[4], + uid + ); + } + printf("%s\n", db_text(0, "SELECT cap FROM user WHERE uid=%d", uid)); }else{ fossil_panic("user subcommand should be one of: " - "create default list password"); + "capabilities default list new password"); } } /* ** Attempt to set the user to zLogin
Modified src/xfer.c from [03c0de5a4e] to [00d4a363f4].
@@ -222,11 +222,11 @@ blob_append(&combined, blob_buffer(&pw), blob_size(&pw)); sha1sum_blob(&combined, &hash); rc = blob_compare(&hash, pSig); blob_reset(&hash); blob_reset(&combined); - if( rc ){ + if( rc==0 ){ const char *zCap; zCap = db_column_text(&q, 1); login_set_capabilities(zCap); g.userUid = db_column_int(&q, 2); g.zLogin = mprintf("%b", pLogin); @@ -394,11 +394,11 @@ @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x")) db_multi_exec( "INSERT OR IGNORE INTO pending(rid) " "SELECT rid FROM blob WHERE size>=0" ); - } + }else /* login USER NONCE SIGNATURE ** ** Check for a valid login. This has to happen before anything else. */