Fossil SCM

Initial complete implementation of self-password-reset. Just need refinement and a security audit before merging to trunk.

drh 2023-01-07 14:25 self-service-password-reset
Commit 41bb73e9ba49c121bd95d2054fe1c1b974aa6849bb99f4229433218571f6018b
3 files changed +7 +170 -2 +9
+7
--- src/db.c
+++ src/db.c
@@ -4499,10 +4499,17 @@
44994499
** displayed using unadorned HTML ("skinless").
45004500
**
45014501
** If repolist-skin has a value of 2, then the repository is omitted from
45024502
** the list in use cases 1 through 4, but not for 5 and 6.
45034503
*/
4504
+/*
4505
+** SETTING: self-pw-reset boolean default=off sensitive
4506
+** Allow users to request that an email containing a hyperlink
4507
+** to the /resetpw page be sent to their email address of record,
4508
+** thus allowing forgetful users to reset their forgotten passwords
4509
+** without administrator involvement.
4510
+*/
45044511
/*
45054512
** SETTING: self-register boolean default=off sensitive
45064513
** Allow users to register themselves through the HTTP UI.
45074514
** This is useful if you want to see other names than
45084515
** "Anonymous" in e.g. ticketing system. On the other hand
45094516
--- src/db.c
+++ src/db.c
@@ -4499,10 +4499,17 @@
4499 ** displayed using unadorned HTML ("skinless").
4500 **
4501 ** If repolist-skin has a value of 2, then the repository is omitted from
4502 ** the list in use cases 1 through 4, but not for 5 and 6.
4503 */
 
 
 
 
 
 
 
4504 /*
4505 ** SETTING: self-register boolean default=off sensitive
4506 ** Allow users to register themselves through the HTTP UI.
4507 ** This is useful if you want to see other names than
4508 ** "Anonymous" in e.g. ticketing system. On the other hand
4509
--- src/db.c
+++ src/db.c
@@ -4499,10 +4499,17 @@
4499 ** displayed using unadorned HTML ("skinless").
4500 **
4501 ** If repolist-skin has a value of 2, then the repository is omitted from
4502 ** the list in use cases 1 through 4, but not for 5 and 6.
4503 */
4504 /*
4505 ** SETTING: self-pw-reset boolean default=off sensitive
4506 ** Allow users to request that an email containing a hyperlink
4507 ** to the /resetpw page be sent to their email address of record,
4508 ** thus allowing forgetful users to reset their forgotten passwords
4509 ** without administrator involvement.
4510 */
4511 /*
4512 ** SETTING: self-register boolean default=off sensitive
4513 ** Allow users to register themselves through the HTTP UI.
4514 ** This is useful if you want to see other names than
4515 ** "Anonymous" in e.g. ticketing system. On the other hand
4516
+170 -2
--- src/login.c
+++ src/login.c
@@ -510,10 +510,19 @@
510510
zPattern = mprintf("%s/login*", g.zBaseURL);
511511
rc = sqlite3_strglob(zPattern, zReferer)==0;
512512
fossil_free(zPattern);
513513
return rc;
514514
}
515
+
516
+/*
517
+** Return true if users are allowed to reset their own passwords.
518
+*/
519
+int login_self_password_reset_available(void){
520
+ if( !db_get_boolean("self-pw-reset",0) ) return 0;
521
+ if( !alert_tables_exist() ) return 0;
522
+ return 1;
523
+}
515524
516525
/*
517526
** Return TRUE if self-registration is available. If the zNeeded
518527
** argument is not NULL, then only return true if self-registration is
519528
** available and any of the capabilities named in zNeeded are available
@@ -560,10 +569,14 @@
560569
const int noAnon = P("noanon")!=0;
561570
int rememberMe; /* If true, use persistent cookie, else
562571
session cookie. Toggled per
563572
checkbox. */
564573
574
+ if( P("pwreset")!=0 ){
575
+ login_reqpwreset_page();
576
+ return;
577
+ }
565578
login_check_credentials();
566579
fossil_redirect_to_https_if_needed(1);
567580
sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
568581
constant_time_cmp_function, 0, 0);
569582
zUsername = P("u");
@@ -780,10 +793,16 @@
780793
if( !noAnon && login_self_register_available(0) ){
781794
@ <tr>
782795
@ <td></td>
783796
@ <td><input type="submit" name="self" value="Create A New Account">
784797
@ </tr>
798
+ }
799
+ if( login_self_password_reset_available() ){
800
+ @ <tr>
801
+ @ <td></td>
802
+ @ <td><input type="submit" name="pwreset" value="Reset My Password">
803
+ @ </tr>
785804
}
786805
@ </table>
787806
if( zAnonPw && !noAnon ){
788807
const char *zDecoded = captcha_decode(uSeed);
789808
int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
@@ -1373,11 +1392,11 @@
13731392
/*
13741393
** Set the current logged in user to be uid. zCap is precomputed
13751394
** (override) capabilities. If zCap==0, then look up the capabilities
13761395
** in the USER table.
13771396
*/
1378
-void login_set_uid(int uid, const char *zCap){
1397
+int login_set_uid(int uid, const char *zCap){
13791398
const char *zPublicPages = 0; /* GLOB patterns of public pages */
13801399
13811400
/* At this point, we know that uid!=0. Find the privileges associated
13821401
** with user uid.
13831402
*/
@@ -1456,10 +1475,11 @@
14561475
if( glob_match(pGlob, zUri) ){
14571476
login_set_capabilities(db_get("default-perms", "u"), 0);
14581477
}
14591478
glob_free(pGlob);
14601479
}
1480
+ return g.zLogin!=0;
14611481
}
14621482
14631483
/*
14641484
** Memory of settings
14651485
*/
@@ -1906,16 +1926,21 @@
19061926
int captchaIsCorrect = 0; /* True on a correct captcha */
19071927
char *zCaptcha = ""; /* Value of the captcha text */
19081928
char *zPerms; /* Permissions for the default user */
19091929
int canDoAlerts = 0; /* True if receiving email alerts is possible */
19101930
int doAlerts = 0; /* True if subscription is wanted too */
1931
+
19111932
if( !db_get_boolean("self-register", 0) ){
19121933
style_header("Registration not possible");
19131934
@ <p>This project does not allow user self-registration. Please contact the
19141935
@ project administrator to obtain an account.</p>
19151936
style_finish_page();
19161937
return;
1938
+ }
1939
+ if( P("pwreset")!=0 ){
1940
+ login_reqpwreset_page();
1941
+ return;
19171942
}
19181943
zPerms = db_get("default-perms", "u");
19191944
19201945
/* Prompt the user for email alerts if this repository is configured for
19211946
** email alerts and if the default permissions include "7" */
@@ -2104,11 +2129,13 @@
21042129
@ value="%h(zEAddr)" size="30"></td>
21052130
@ </tr>
21062131
if( iErrLine==3 ){
21072132
@ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span>
21082133
if( uid>0 ){
2109
- @ <br /><button>ToDo: Request Password Reset For UID %d(uid)</button>
2134
+ @ <br />
2135
+ @ <input type="submit" name="pwreset" \
2136
+ @ value="Request Password Reset For %h(zEAddr)">
21102137
}
21112138
@ </td></tr>
21122139
}
21132140
if( canDoAlerts ){
21142141
int a = atoi(PD("alerts","1"));
@@ -2162,10 +2189,151 @@
21622189
@ </form>
21632190
style_finish_page();
21642191
21652192
free(zCaptcha);
21662193
}
2194
+
2195
+/*
2196
+** WEBPAGE: reqpwreset
2197
+**
2198
+** A web page to request a password reset.
2199
+*/
2200
+void login_reqpwreset_page(void){
2201
+ const char *zEAddr;
2202
+ const char *zDecoded;
2203
+ unsigned int uSeed;
2204
+ int iErrLine = -1;
2205
+ const char *zErr = 0;
2206
+ int uid = 0; /* User id with the email zEAddr */
2207
+ int captchaIsCorrect = 0; /* True on a correct captcha */
2208
+ char *zCaptcha = ""; /* Value of the captcha text */
2209
+
2210
+ if( !db_get_boolean("self-pw-reset", 0) || !alert_tables_exist() ){
2211
+ style_header("Password reset not possible");
2212
+ @ <p>This project does not allow users to reset their own passwords.
2213
+ @ If you need a password reset, you will have to negotiate that directly
2214
+ @ with the project administrator.
2215
+ style_finish_page();
2216
+ return;
2217
+ }
2218
+ zEAddr = PDT("ea","");
2219
+
2220
+ /* Verify user imputs */
2221
+ if( !cgi_csrf_safe(1) || P("reqpwreset")==0 ){
2222
+ /* This is the initial display of the form. No processing or error
2223
+ ** checking is to be done. Fall through into the form display
2224
+ */
2225
+ }else if( (captchaIsCorrect = captcha_is_correct(1))==0 ){
2226
+ iErrLine = 2;
2227
+ zErr = "Incorrect CAPTCHA";
2228
+ }else if( zEAddr[0]==0 ){
2229
+ iErrLine = 1;
2230
+ zErr = "Required";
2231
+ }else if( email_address_is_valid(zEAddr,0)==0 ){
2232
+ iErrLine = 1;
2233
+ zErr = "Not a valid email address";
2234
+ }else if( authorized_subscription_email(zEAddr)==0 ){
2235
+ iErrLine = 1;
2236
+ zErr = "Not an authorized email address";
2237
+ }else if( (uid = email_address_in_use(zEAddr))<=0 ){
2238
+ iErrLine = 1;
2239
+ zErr = "This email address is not associated with a user who has "
2240
+ "password reset privileges.";
2241
+ }else if( login_set_uid(uid,0)==0 || g.perm.Admin || g.perm.Setup
2242
+ || !g.perm.Password ){
2243
+ iErrLine = 1;
2244
+ zErr = "This email address is not associated with a user who has "
2245
+ "password reset privileges.";
2246
+ }else{
2247
+
2248
+ /* If all of the tests above have passed, that means that the submitted
2249
+ ** form contains valid data and we can proceed to issue the password
2250
+ ** reset email. */
2251
+ Blob hdr, body;
2252
+ AlertSender *pSender;
2253
+ char *zUrl = login_resetpw_suffix(uid, 0);
2254
+ pSender = alert_sender_new(0,0);
2255
+ blob_init(&hdr,0,0);
2256
+ blob_init(&body,0,0);
2257
+ blob_appendf(&hdr, "To: <%s>\n", zEAddr);
2258
+ blob_appendf(&hdr, "Subject: Password reset for %s\n", g.zBaseURL);
2259
+ blob_appendf(&body,
2260
+ "Someone has requested to reset the password for user \"%s\"\n",
2261
+ g.zLogin);
2262
+ blob_appendf(&body, "at %s.\n\n", g.zBaseURL);
2263
+ blob_appendf(&body,
2264
+ "If you did not request this password reset, ignore\n"
2265
+ "this email\n\n");
2266
+ blob_appendf(&body,
2267
+ "To reset the password, visit the following link:\n\n"
2268
+ " %s/resetpw/%s\n\n", g.zBaseURL, zUrl);
2269
+ fossil_free(zUrl);
2270
+ alert_send(pSender, &hdr, &body, 0);
2271
+ style_header("Email Verification");
2272
+ if( pSender->zErr ){
2273
+ @ <h1>Internal Error</h1>
2274
+ @ <p>The following internal error was encountered while trying
2275
+ @ to send the confirmation email:
2276
+ @ <blockquote><pre>
2277
+ @ %h(pSender->zErr)
2278
+ @ </pre></blockquote>
2279
+ }else{
2280
+ @ <p>An email containing a hyperlink that can be used to reset
2281
+ @ your password has been sent to "%h(zEAddr)".</p>
2282
+ }
2283
+ alert_sender_free(pSender);
2284
+ style_finish_page();
2285
+ return;
2286
+ }
2287
+
2288
+ /* Prepare the captcha. */
2289
+ if( captchaIsCorrect ){
2290
+ uSeed = strtoul(P("captchaseed"),0,10);
2291
+ }else{
2292
+ uSeed = captcha_seed();
2293
+ }
2294
+ zDecoded = captcha_decode(uSeed);
2295
+ zCaptcha = captcha_render(zDecoded);
2296
+
2297
+ style_header("Request Password Reset");
2298
+ /* Print out the registration form. */
2299
+ g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */
2300
+ form_begin(0, "%R/reqpwreset");
2301
+ @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)" />
2302
+ @ <p><input type="hidden" name="reqpwreset" value="1" />
2303
+ @ <table class="login_out">
2304
+ @ <tr>
2305
+ @ <td class="form_label" align="right" id="emaddr">Email Address:</td>
2306
+ @ <td><input aria-labelledby="emaddr" type="text" name="ea" \
2307
+ @ value="%h(zEAddr)" size="30"></td>
2308
+ @ </tr>
2309
+ if( iErrLine==1 ){
2310
+ @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
2311
+ }
2312
+ @ <tr>
2313
+ @ <td class="form_label" align="right" id="cptcha">Captcha:</td>
2314
+ @ <td><input type="text" name="captcha" aria-labelledby="cptcha" \
2315
+ @ value="%h(captchaIsCorrect?zDecoded:"")" size="30">
2316
+ captcha_speakit_button(uSeed, "Speak the captcha text");
2317
+ @ </td>
2318
+ @ </tr>
2319
+ if( iErrLine==2 ){
2320
+ @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
2321
+ }
2322
+ @ <tr><td></td>
2323
+ @ <td><input type="submit" name="new" value="Request Password Reset"/>\
2324
+ @ </td></tr>
2325
+ @ </table>
2326
+ @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha">
2327
+ @ %h(zCaptcha)
2328
+ @ </pre>
2329
+ @ Enter this 8-letter code in the "Captcha" box above.
2330
+ @ </td></tr></table></div>
2331
+ @ </form>
2332
+ style_finish_page();
2333
+ free(zCaptcha);
2334
+}
21672335
21682336
/*
21692337
** Run SQL on the repository database for every repository in our
21702338
** login group. The SQL is run in a separate database connection.
21712339
**
21722340
--- src/login.c
+++ src/login.c
@@ -510,10 +510,19 @@
510 zPattern = mprintf("%s/login*", g.zBaseURL);
511 rc = sqlite3_strglob(zPattern, zReferer)==0;
512 fossil_free(zPattern);
513 return rc;
514 }
 
 
 
 
 
 
 
 
 
515
516 /*
517 ** Return TRUE if self-registration is available. If the zNeeded
518 ** argument is not NULL, then only return true if self-registration is
519 ** available and any of the capabilities named in zNeeded are available
@@ -560,10 +569,14 @@
560 const int noAnon = P("noanon")!=0;
561 int rememberMe; /* If true, use persistent cookie, else
562 session cookie. Toggled per
563 checkbox. */
564
 
 
 
 
565 login_check_credentials();
566 fossil_redirect_to_https_if_needed(1);
567 sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
568 constant_time_cmp_function, 0, 0);
569 zUsername = P("u");
@@ -780,10 +793,16 @@
780 if( !noAnon && login_self_register_available(0) ){
781 @ <tr>
782 @ <td></td>
783 @ <td><input type="submit" name="self" value="Create A New Account">
784 @ </tr>
 
 
 
 
 
 
785 }
786 @ </table>
787 if( zAnonPw && !noAnon ){
788 const char *zDecoded = captcha_decode(uSeed);
789 int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
@@ -1373,11 +1392,11 @@
1373 /*
1374 ** Set the current logged in user to be uid. zCap is precomputed
1375 ** (override) capabilities. If zCap==0, then look up the capabilities
1376 ** in the USER table.
1377 */
1378 void login_set_uid(int uid, const char *zCap){
1379 const char *zPublicPages = 0; /* GLOB patterns of public pages */
1380
1381 /* At this point, we know that uid!=0. Find the privileges associated
1382 ** with user uid.
1383 */
@@ -1456,10 +1475,11 @@
1456 if( glob_match(pGlob, zUri) ){
1457 login_set_capabilities(db_get("default-perms", "u"), 0);
1458 }
1459 glob_free(pGlob);
1460 }
 
1461 }
1462
1463 /*
1464 ** Memory of settings
1465 */
@@ -1906,16 +1926,21 @@
1906 int captchaIsCorrect = 0; /* True on a correct captcha */
1907 char *zCaptcha = ""; /* Value of the captcha text */
1908 char *zPerms; /* Permissions for the default user */
1909 int canDoAlerts = 0; /* True if receiving email alerts is possible */
1910 int doAlerts = 0; /* True if subscription is wanted too */
 
1911 if( !db_get_boolean("self-register", 0) ){
1912 style_header("Registration not possible");
1913 @ <p>This project does not allow user self-registration. Please contact the
1914 @ project administrator to obtain an account.</p>
1915 style_finish_page();
1916 return;
 
 
 
 
1917 }
1918 zPerms = db_get("default-perms", "u");
1919
1920 /* Prompt the user for email alerts if this repository is configured for
1921 ** email alerts and if the default permissions include "7" */
@@ -2104,11 +2129,13 @@
2104 @ value="%h(zEAddr)" size="30"></td>
2105 @ </tr>
2106 if( iErrLine==3 ){
2107 @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span>
2108 if( uid>0 ){
2109 @ <br /><button>ToDo: Request Password Reset For UID %d(uid)</button>
 
 
2110 }
2111 @ </td></tr>
2112 }
2113 if( canDoAlerts ){
2114 int a = atoi(PD("alerts","1"));
@@ -2162,10 +2189,151 @@
2162 @ </form>
2163 style_finish_page();
2164
2165 free(zCaptcha);
2166 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2167
2168 /*
2169 ** Run SQL on the repository database for every repository in our
2170 ** login group. The SQL is run in a separate database connection.
2171 **
2172
--- src/login.c
+++ src/login.c
@@ -510,10 +510,19 @@
510 zPattern = mprintf("%s/login*", g.zBaseURL);
511 rc = sqlite3_strglob(zPattern, zReferer)==0;
512 fossil_free(zPattern);
513 return rc;
514 }
515
516 /*
517 ** Return true if users are allowed to reset their own passwords.
518 */
519 int login_self_password_reset_available(void){
520 if( !db_get_boolean("self-pw-reset",0) ) return 0;
521 if( !alert_tables_exist() ) return 0;
522 return 1;
523 }
524
525 /*
526 ** Return TRUE if self-registration is available. If the zNeeded
527 ** argument is not NULL, then only return true if self-registration is
528 ** available and any of the capabilities named in zNeeded are available
@@ -560,10 +569,14 @@
569 const int noAnon = P("noanon")!=0;
570 int rememberMe; /* If true, use persistent cookie, else
571 session cookie. Toggled per
572 checkbox. */
573
574 if( P("pwreset")!=0 ){
575 login_reqpwreset_page();
576 return;
577 }
578 login_check_credentials();
579 fossil_redirect_to_https_if_needed(1);
580 sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
581 constant_time_cmp_function, 0, 0);
582 zUsername = P("u");
@@ -780,10 +793,16 @@
793 if( !noAnon && login_self_register_available(0) ){
794 @ <tr>
795 @ <td></td>
796 @ <td><input type="submit" name="self" value="Create A New Account">
797 @ </tr>
798 }
799 if( login_self_password_reset_available() ){
800 @ <tr>
801 @ <td></td>
802 @ <td><input type="submit" name="pwreset" value="Reset My Password">
803 @ </tr>
804 }
805 @ </table>
806 if( zAnonPw && !noAnon ){
807 const char *zDecoded = captcha_decode(uSeed);
808 int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
@@ -1373,11 +1392,11 @@
1392 /*
1393 ** Set the current logged in user to be uid. zCap is precomputed
1394 ** (override) capabilities. If zCap==0, then look up the capabilities
1395 ** in the USER table.
1396 */
1397 int login_set_uid(int uid, const char *zCap){
1398 const char *zPublicPages = 0; /* GLOB patterns of public pages */
1399
1400 /* At this point, we know that uid!=0. Find the privileges associated
1401 ** with user uid.
1402 */
@@ -1456,10 +1475,11 @@
1475 if( glob_match(pGlob, zUri) ){
1476 login_set_capabilities(db_get("default-perms", "u"), 0);
1477 }
1478 glob_free(pGlob);
1479 }
1480 return g.zLogin!=0;
1481 }
1482
1483 /*
1484 ** Memory of settings
1485 */
@@ -1906,16 +1926,21 @@
1926 int captchaIsCorrect = 0; /* True on a correct captcha */
1927 char *zCaptcha = ""; /* Value of the captcha text */
1928 char *zPerms; /* Permissions for the default user */
1929 int canDoAlerts = 0; /* True if receiving email alerts is possible */
1930 int doAlerts = 0; /* True if subscription is wanted too */
1931
1932 if( !db_get_boolean("self-register", 0) ){
1933 style_header("Registration not possible");
1934 @ <p>This project does not allow user self-registration. Please contact the
1935 @ project administrator to obtain an account.</p>
1936 style_finish_page();
1937 return;
1938 }
1939 if( P("pwreset")!=0 ){
1940 login_reqpwreset_page();
1941 return;
1942 }
1943 zPerms = db_get("default-perms", "u");
1944
1945 /* Prompt the user for email alerts if this repository is configured for
1946 ** email alerts and if the default permissions include "7" */
@@ -2104,11 +2129,13 @@
2129 @ value="%h(zEAddr)" size="30"></td>
2130 @ </tr>
2131 if( iErrLine==3 ){
2132 @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span>
2133 if( uid>0 ){
2134 @ <br />
2135 @ <input type="submit" name="pwreset" \
2136 @ value="Request Password Reset For %h(zEAddr)">
2137 }
2138 @ </td></tr>
2139 }
2140 if( canDoAlerts ){
2141 int a = atoi(PD("alerts","1"));
@@ -2162,10 +2189,151 @@
2189 @ </form>
2190 style_finish_page();
2191
2192 free(zCaptcha);
2193 }
2194
2195 /*
2196 ** WEBPAGE: reqpwreset
2197 **
2198 ** A web page to request a password reset.
2199 */
2200 void login_reqpwreset_page(void){
2201 const char *zEAddr;
2202 const char *zDecoded;
2203 unsigned int uSeed;
2204 int iErrLine = -1;
2205 const char *zErr = 0;
2206 int uid = 0; /* User id with the email zEAddr */
2207 int captchaIsCorrect = 0; /* True on a correct captcha */
2208 char *zCaptcha = ""; /* Value of the captcha text */
2209
2210 if( !db_get_boolean("self-pw-reset", 0) || !alert_tables_exist() ){
2211 style_header("Password reset not possible");
2212 @ <p>This project does not allow users to reset their own passwords.
2213 @ If you need a password reset, you will have to negotiate that directly
2214 @ with the project administrator.
2215 style_finish_page();
2216 return;
2217 }
2218 zEAddr = PDT("ea","");
2219
2220 /* Verify user imputs */
2221 if( !cgi_csrf_safe(1) || P("reqpwreset")==0 ){
2222 /* This is the initial display of the form. No processing or error
2223 ** checking is to be done. Fall through into the form display
2224 */
2225 }else if( (captchaIsCorrect = captcha_is_correct(1))==0 ){
2226 iErrLine = 2;
2227 zErr = "Incorrect CAPTCHA";
2228 }else if( zEAddr[0]==0 ){
2229 iErrLine = 1;
2230 zErr = "Required";
2231 }else if( email_address_is_valid(zEAddr,0)==0 ){
2232 iErrLine = 1;
2233 zErr = "Not a valid email address";
2234 }else if( authorized_subscription_email(zEAddr)==0 ){
2235 iErrLine = 1;
2236 zErr = "Not an authorized email address";
2237 }else if( (uid = email_address_in_use(zEAddr))<=0 ){
2238 iErrLine = 1;
2239 zErr = "This email address is not associated with a user who has "
2240 "password reset privileges.";
2241 }else if( login_set_uid(uid,0)==0 || g.perm.Admin || g.perm.Setup
2242 || !g.perm.Password ){
2243 iErrLine = 1;
2244 zErr = "This email address is not associated with a user who has "
2245 "password reset privileges.";
2246 }else{
2247
2248 /* If all of the tests above have passed, that means that the submitted
2249 ** form contains valid data and we can proceed to issue the password
2250 ** reset email. */
2251 Blob hdr, body;
2252 AlertSender *pSender;
2253 char *zUrl = login_resetpw_suffix(uid, 0);
2254 pSender = alert_sender_new(0,0);
2255 blob_init(&hdr,0,0);
2256 blob_init(&body,0,0);
2257 blob_appendf(&hdr, "To: <%s>\n", zEAddr);
2258 blob_appendf(&hdr, "Subject: Password reset for %s\n", g.zBaseURL);
2259 blob_appendf(&body,
2260 "Someone has requested to reset the password for user \"%s\"\n",
2261 g.zLogin);
2262 blob_appendf(&body, "at %s.\n\n", g.zBaseURL);
2263 blob_appendf(&body,
2264 "If you did not request this password reset, ignore\n"
2265 "this email\n\n");
2266 blob_appendf(&body,
2267 "To reset the password, visit the following link:\n\n"
2268 " %s/resetpw/%s\n\n", g.zBaseURL, zUrl);
2269 fossil_free(zUrl);
2270 alert_send(pSender, &hdr, &body, 0);
2271 style_header("Email Verification");
2272 if( pSender->zErr ){
2273 @ <h1>Internal Error</h1>
2274 @ <p>The following internal error was encountered while trying
2275 @ to send the confirmation email:
2276 @ <blockquote><pre>
2277 @ %h(pSender->zErr)
2278 @ </pre></blockquote>
2279 }else{
2280 @ <p>An email containing a hyperlink that can be used to reset
2281 @ your password has been sent to "%h(zEAddr)".</p>
2282 }
2283 alert_sender_free(pSender);
2284 style_finish_page();
2285 return;
2286 }
2287
2288 /* Prepare the captcha. */
2289 if( captchaIsCorrect ){
2290 uSeed = strtoul(P("captchaseed"),0,10);
2291 }else{
2292 uSeed = captcha_seed();
2293 }
2294 zDecoded = captcha_decode(uSeed);
2295 zCaptcha = captcha_render(zDecoded);
2296
2297 style_header("Request Password Reset");
2298 /* Print out the registration form. */
2299 g.perm.Hyperlink = 1; /* Artificially enable hyperlinks */
2300 form_begin(0, "%R/reqpwreset");
2301 @ <p><input type="hidden" name="captchaseed" value="%u(uSeed)" />
2302 @ <p><input type="hidden" name="reqpwreset" value="1" />
2303 @ <table class="login_out">
2304 @ <tr>
2305 @ <td class="form_label" align="right" id="emaddr">Email Address:</td>
2306 @ <td><input aria-labelledby="emaddr" type="text" name="ea" \
2307 @ value="%h(zEAddr)" size="30"></td>
2308 @ </tr>
2309 if( iErrLine==1 ){
2310 @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
2311 }
2312 @ <tr>
2313 @ <td class="form_label" align="right" id="cptcha">Captcha:</td>
2314 @ <td><input type="text" name="captcha" aria-labelledby="cptcha" \
2315 @ value="%h(captchaIsCorrect?zDecoded:"")" size="30">
2316 captcha_speakit_button(uSeed, "Speak the captcha text");
2317 @ </td>
2318 @ </tr>
2319 if( iErrLine==2 ){
2320 @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
2321 }
2322 @ <tr><td></td>
2323 @ <td><input type="submit" name="new" value="Request Password Reset"/>\
2324 @ </td></tr>
2325 @ </table>
2326 @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha">
2327 @ %h(zCaptcha)
2328 @ </pre>
2329 @ Enter this 8-letter code in the "Captcha" box above.
2330 @ </td></tr></table></div>
2331 @ </form>
2332 style_finish_page();
2333 free(zCaptcha);
2334 }
2335
2336 /*
2337 ** Run SQL on the repository database for every repository in our
2338 ** login group. The SQL is run in a separate database connection.
2339 **
2340
--- src/setup.c
+++ src/setup.c
@@ -594,10 +594,19 @@
594594
@ A self-registration creates a new entry in the USER table and
595595
@ perhaps also in the SUBSCRIBER table if email notification is
596596
@ enabled.
597597
@ (Property: "self-register")</p>
598598
599
+ @ <hr />
600
+ onoff_attribute("Allow users to reset their own passwords",
601
+ "self-pw-reset", "selfpw", 0, 0);
602
+ @ <p>Allow users to request that an email contains a hyperlink to a
603
+ @ password reset page be sent to their email address of record. This
604
+ @ enables forgetful users to recover their forgotten passwords without
605
+ @ administrator intervention.
606
+ @ (Property: "self-pw-reset")</p>
607
+
599608
@ <hr />
600609
onoff_attribute("Email verification required for self-registration",
601610
"selfreg-verify", "sfverify", 0, 0);
602611
@ <p>If enabled, self-registration creates a new entry in the USER table
603612
@ with only capabilities "7". The default user capabilities are not
604613
--- src/setup.c
+++ src/setup.c
@@ -594,10 +594,19 @@
594 @ A self-registration creates a new entry in the USER table and
595 @ perhaps also in the SUBSCRIBER table if email notification is
596 @ enabled.
597 @ (Property: "self-register")</p>
598
 
 
 
 
 
 
 
 
 
599 @ <hr />
600 onoff_attribute("Email verification required for self-registration",
601 "selfreg-verify", "sfverify", 0, 0);
602 @ <p>If enabled, self-registration creates a new entry in the USER table
603 @ with only capabilities "7". The default user capabilities are not
604
--- src/setup.c
+++ src/setup.c
@@ -594,10 +594,19 @@
594 @ A self-registration creates a new entry in the USER table and
595 @ perhaps also in the SUBSCRIBER table if email notification is
596 @ enabled.
597 @ (Property: "self-register")</p>
598
599 @ <hr />
600 onoff_attribute("Allow users to reset their own passwords",
601 "self-pw-reset", "selfpw", 0, 0);
602 @ <p>Allow users to request that an email contains a hyperlink to a
603 @ password reset page be sent to their email address of record. This
604 @ enables forgetful users to recover their forgotten passwords without
605 @ administrator intervention.
606 @ (Property: "self-pw-reset")</p>
607
608 @ <hr />
609 onoff_attribute("Email verification required for self-registration",
610 "selfreg-verify", "sfverify", 0, 0);
611 @ <p>If enabled, self-registration creates a new entry in the USER table
612 @ with only capabilities "7". The default user capabilities are not
613

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button