Fossil

Check-in [916b6e4b]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Improvements to web-based user management.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:916b6e4b3b701915f2670ce40e776abbd05f71d0
User & Date: drh 2007-07-21 19:32:06
Context
2007-07-22
12:00
Setup webpag updates. check-in: c4ec179b user: drh tags: trunk
2007-07-21
19:32
Improvements to web-based user management. check-in: 916b6e4b user: drh tags: trunk
14:10
Initial check-in of m1 sources. check-in: dbda8d6c user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/db.c.

570
571
572
573
574
575
576
577










578
579

580


581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
** Try to find the repository and open it.  If we are in a local
** tree, then use the repository of the local tree.  Otherwise,
** 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");

  }


}

/*
** 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);
  }
  db_open_repository(0);
}

/*
** Close the database connection.
*/







|
>
>
>
>
>
>
>
>
>
>
|
<
>

>
>







|
<







570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588

589
590
591
592
593
594
595
596
597
598
599
600

601
602
603
604
605
606
607
** Try to find the repository and open it.  If we are in a local
** tree, then use the repository of the local tree.  Otherwise,
** fall back to the -R or --repository option.
**
** Error out if the repository cannot be opened.
*/
void db_find_and_open_repository(void){
  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 ){
    fossil_fatal("not within an open checkout");

  }
  db_open_repository(0);
}

/*
** Close the database connection.
*/

Changes to src/login.c.

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
...
192
193
194
195
196
197
198
199
200
201
202

203
204
205
206
207
208
209
...
212
213
214
215
216
217
218

219
220
221
222
223
224
225
226

227
228
229
230
231
232
233
      cgi_redirect("index");
      return;
    }
  }
  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);
    if( uid<=0 ){
      sleep(1);
      zErrMsg = 
         @ <p><font color="red">
         @ You entered an unknown user or an incorrect password.
         @ </font></p>
      ;
    }else{
      char *zCookie;
      const char *zCookieName = login_cookie_name();
      const char *zIpAddr = PD("REMOTE_ADDR","x");
      const char *zExpire = db_get("cookie-expire","8766");
      int expires;

      zCookie = db_text(0, "SELECT '%d/' || hex(randomblob(25))", uid);
      expires = atoi(zExpire)*3600;
      cgi_set_cookie(zCookieName, zCookie, 0, expires);
      db_multi_exec(
................................................................................
  /* If the HTTP connection is coming over 127.0.0.1 and if
  ** local login is disabled, then there is no need to check
  ** 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%'");
    g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
    zCap = "s";
    g.noPswd = 1;

  }

  /* Check the login cookie to see if it matches a known valid user.
  */
  if( uid==0 && (zCookie = P(login_cookie_name()))!=0 ){
    uid = db_int(0, 
            "SELECT 1 FROM user"
................................................................................
            "   AND ipaddr=%Q"
            "   AND cexpire>julianday('now')",
            atoi(zCookie), zCookie, zRemoteAddr
         );
  }

  if( uid==0 ){

    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);

    db_finalize(&s);
  }
  g.userUid = uid;

  login_set_capabilities(zCap);
}








|










|







 







|



>







 







>








>







79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
...
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
...
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
      cgi_redirect("index");
      return;
    }
  }
  if( zUsername!=0 && zPasswd!=0 && strcmp(zUsername,"anonymous")!=0 ){
    int uid = db_int(0,
        "SELECT uid FROM user"
        " 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.
         @ </font></p>
      ;
    }else{
      char *zCookie;
      const char *zCookieName = login_cookie_name();
      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;
      cgi_set_cookie(zCookieName, zCookie, 0, expires);
      db_multi_exec(
................................................................................
  /* If the HTTP connection is coming over 127.0.0.1 and if
  ** local login is disabled, then there is no need to check
  ** 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%%'");
    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 ){
    uid = db_int(0, 
            "SELECT 1 FROM user"
................................................................................
            "   AND ipaddr=%Q"
            "   AND cexpire>julianday('now')",
            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);
}

Changes to src/setup.c.

75
76
77
78
79
80
81


82
83
84

85
86

87
88
89







90
91



92

93




94


95
96
97























98








99
100
101
102
103
104
105
106
107




108


109


110
111
112
113




114

























































115















116
117

118




























119







































































































120
121

122
123
124
125
126
127
128
** WEBPAGE: setup_ulist
**
** Show a list of users.  Clicking on any user jumps to the edit
** screen for that user.
*/
void setup_ulist(void){
  Stmt s;


  login_check_credentials();
  if( !g.okSetup ){
    login_needed();

  }


  style_header();

  @ <table border="0" cellpadding="0" cellspacing="0">







  db_prepare(&s, "SELECT uid, login, cap FROM repuser 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);
  @ </table>























  








  style_footer();
}

/*
** WEBPAGE: setup_uedit
**
** Edit the user with REPUSER.UID equal to the "u" query parameter.
*/
void setup_uedit(void){




  int uid;





  login_check_credentials();
  if( !g.okSetup ){
    login_needed();
  }




  uid = atoi(PD("u","0"));

























































  if( uid<=0 ){















    cgi_redirect("setup_ulist");
    assert(0);

  }




























  style_header();







































































































  style_footer(); 
}


/*
** Generate a checkbox for an attribute.
*/
static void onoff_attribute(
  const char *zLabel,   /* The text label on the checkbox */
  const char *zVar,     /* The corresponding row in the VAR table */







>
>

|

>


>

<
<
>
>
>
>
>
>
>
|

>
>
>
|
>
|
>
>
>
>
|
>
>

<

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>




|
<
<

|
>
>
>
>

>
>

>
>

<
|
|
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

<
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|

>







75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91


92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152


153
154
155
156
157
158
159
160
161
162
163
164
165

166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
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
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
** WEBPAGE: setup_ulist
**
** 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.okWrite || g.isAnon ){
    login_needed();
    return;
  }

  style_submenu_element("Add", "Add User", "setup_uedit");
  style_header();


  @ <h2>List Of Users</h2>
  @ <table cellspacing=0 cellpadding=0 border=0>
  @ <tr>
  @   <th align="right"><nobr>User ID</nobr></th>
  @   <th>&nbsp;&nbsp;&nbsp;Capabilities&nbsp;&nbsp;&nbsp;</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 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


*/
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.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");

    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&nbsp;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>&nbsp</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(
  const char *zLabel,   /* The text label on the checkbox */
  const char *zVar,     /* The corresponding row in the VAR table */

Changes to src/style.c.

67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
...
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
}

/*
** Draw the header.
*/
void style_header(void){
  const char *zLogInOut = "Logout";
  /* 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">
  @ <big><big><b>Fossil SCM</b></big></big><br>
  if( g.zLogin==0 || g.zLogin[0]==0 ){
................................................................................
    qsort(aSubmenu, nSubmenu, sizeof(aSubmenu[0]), submenuCompare);
    for(i=0; i<nSubmenu; i++){
      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)
      }
    }
  }
  @ </td></tr></table>
  @ <hr size="1">
  g.cgiPanic = 1;
}







|







 







|







67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
...
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
}

/*
** Draw the header.
*/
void style_header(void){
  const char *zLogInOut = "Logout";
  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">
  @ <big><big><b>Fossil SCM</b></big></big><br>
  if( g.zLogin==0 || g.zLogin[0]==0 ){
................................................................................
    qsort(aSubmenu, nSubmenu, sizeof(aSubmenu[0]), submenuCompare);
    for(i=0; i<nSubmenu; i++){
      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="%T(p->zLink)">%h(p->zLabel)</a> %s(zTail)
      }
    }
  }
  @ </td></tr></table>
  @ <hr size="1">
  g.cgiPanic = 1;
}

Changes to src/user.c.

104
105
106
107
108
109
110

111
112
113

114

115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
...
151
152
153
154
155
156
157
















158
159
160
161
162
163
164
165
166
167

/*
** COMMAND:  user
**
** Dispatcher for various user subcommands.
*/
void user_cmd(void){

  db_find_and_open_repository();
  if( g.argc<3 ){
    usage("create|default|list|password ...");

  }

  if( strcmp(g.argv[2],"create")==0 ){
    Blob passwd, login, contact;

    prompt_user("login: ", &login);
    prompt_user("contact-info: ", &contact);
    get_passphrase("password: ", &passwd, 1);
    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 ){
    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 ){
    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 ){
    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]);
    if( uid==0 ){
      fossil_fatal("no such user: %s", g.argv[3]);
................................................................................
    zPrompt = mprintf("new passwd for %s: ", g.argv[3]);
    get_passphrase(zPrompt, &pw, 1);
    if( blob_size(&pw)==0 ){
      printf("password unchanged\n");
    }else{
      db_multi_exec("UPDATE user SET pw=%B WHERE uid=%d", &pw, uid);
    }
















  }else{
    fossil_panic("user subcommand should be one of: "
                 "create default list password");
  }
}

/*
** Attempt to set the user to zLogin
*/
static int attempt_user(const char *zLogin){







>


<
>

>
|










|








|






|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


|







104
105
106
107
108
109
110
111
112
113

114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
...
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185

/*
** COMMAND:  user
**
** Dispatcher for various user subcommands.
*/
void user_cmd(void){
  int n;
  db_find_and_open_repository();
  if( g.argc<3 ){

    usage("capabilities|default|list|new|password ...");
  }
  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);
    db_multi_exec(
      "INSERT INTO user(login,pw,cap,info)"
      "VALUES(%B,%B,'jnor',%B)",
      &login, &passwd, &contact
    );
  }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( 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( 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]);
    if( uid==0 ){
      fossil_fatal("no such user: %s", g.argv[3]);
................................................................................
    zPrompt = mprintf("new passwd for %s: ", g.argv[3]);
    get_passphrase(zPrompt, &pw, 1);
    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: "
                 "capabilities default list new password");
  }
}

/*
** Attempt to set the user to zLogin
*/
static int attempt_user(const char *zLogin){

Changes to src/xfer.c.

220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
...
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
    blob_zero(&combined);
    blob_copy(&combined, pNonce);
    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 ){
      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);
      g.zNonce = mprintf("%b", pNonce);
    }
................................................................................
      }
      isPull = 1;
      @ 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"
      );
    }

    /*    login  USER  NONCE  SIGNATURE
    **
    ** Check for a valid login.  This has to happen before anything else.
    */
    if( blob_eq(&aToken[0], "login") && nToken==4 ){
      if( disableLogin ){







|







 







|







220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
...
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
    blob_zero(&combined);
    blob_copy(&combined, pNonce);
    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==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);
      g.zNonce = mprintf("%b", pNonce);
    }
................................................................................
      }
      isPull = 1;
      @ 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.
    */
    if( blob_eq(&aToken[0], "login") && nToken==4 ){
      if( disableLogin ){