| | @@ -510,10 +510,19 @@ |
| 510 | 510 | zPattern = mprintf("%s/login*", g.zBaseURL); |
| 511 | 511 | rc = sqlite3_strglob(zPattern, zReferer)==0; |
| 512 | 512 | fossil_free(zPattern); |
| 513 | 513 | return rc; |
| 514 | 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 | +} |
| 515 | 524 | |
| 516 | 525 | /* |
| 517 | 526 | ** Return TRUE if self-registration is available. If the zNeeded |
| 518 | 527 | ** argument is not NULL, then only return true if self-registration is |
| 519 | 528 | ** available and any of the capabilities named in zNeeded are available |
| | @@ -560,10 +569,14 @@ |
| 560 | 569 | const int noAnon = P("noanon")!=0; |
| 561 | 570 | int rememberMe; /* If true, use persistent cookie, else |
| 562 | 571 | session cookie. Toggled per |
| 563 | 572 | checkbox. */ |
| 564 | 573 | |
| 574 | + if( P("pwreset")!=0 ){ |
| 575 | + login_reqpwreset_page(); |
| 576 | + return; |
| 577 | + } |
| 565 | 578 | login_check_credentials(); |
| 566 | 579 | fossil_redirect_to_https_if_needed(1); |
| 567 | 580 | sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0, |
| 568 | 581 | constant_time_cmp_function, 0, 0); |
| 569 | 582 | zUsername = P("u"); |
| | @@ -780,10 +793,16 @@ |
| 780 | 793 | if( !noAnon && login_self_register_available(0) ){ |
| 781 | 794 | @ <tr> |
| 782 | 795 | @ <td></td> |
| 783 | 796 | @ <td><input type="submit" name="self" value="Create A New Account"> |
| 784 | 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> |
| 785 | 804 | } |
| 786 | 805 | @ </table> |
| 787 | 806 | if( zAnonPw && !noAnon ){ |
| 788 | 807 | const char *zDecoded = captcha_decode(uSeed); |
| 789 | 808 | int bAutoCaptcha = db_get_boolean("auto-captcha", 0); |
| | @@ -1373,11 +1392,11 @@ |
| 1373 | 1392 | /* |
| 1374 | 1393 | ** Set the current logged in user to be uid. zCap is precomputed |
| 1375 | 1394 | ** (override) capabilities. If zCap==0, then look up the capabilities |
| 1376 | 1395 | ** in the USER table. |
| 1377 | 1396 | */ |
| 1378 | | -void login_set_uid(int uid, const char *zCap){ |
| 1397 | +int login_set_uid(int uid, const char *zCap){ |
| 1379 | 1398 | const char *zPublicPages = 0; /* GLOB patterns of public pages */ |
| 1380 | 1399 | |
| 1381 | 1400 | /* At this point, we know that uid!=0. Find the privileges associated |
| 1382 | 1401 | ** with user uid. |
| 1383 | 1402 | */ |
| | @@ -1456,10 +1475,11 @@ |
| 1456 | 1475 | if( glob_match(pGlob, zUri) ){ |
| 1457 | 1476 | login_set_capabilities(db_get("default-perms", "u"), 0); |
| 1458 | 1477 | } |
| 1459 | 1478 | glob_free(pGlob); |
| 1460 | 1479 | } |
| 1480 | + return g.zLogin!=0; |
| 1461 | 1481 | } |
| 1462 | 1482 | |
| 1463 | 1483 | /* |
| 1464 | 1484 | ** Memory of settings |
| 1465 | 1485 | */ |
| | @@ -1906,16 +1926,21 @@ |
| 1906 | 1926 | int captchaIsCorrect = 0; /* True on a correct captcha */ |
| 1907 | 1927 | char *zCaptcha = ""; /* Value of the captcha text */ |
| 1908 | 1928 | char *zPerms; /* Permissions for the default user */ |
| 1909 | 1929 | int canDoAlerts = 0; /* True if receiving email alerts is possible */ |
| 1910 | 1930 | int doAlerts = 0; /* True if subscription is wanted too */ |
| 1931 | + |
| 1911 | 1932 | if( !db_get_boolean("self-register", 0) ){ |
| 1912 | 1933 | style_header("Registration not possible"); |
| 1913 | 1934 | @ <p>This project does not allow user self-registration. Please contact the |
| 1914 | 1935 | @ project administrator to obtain an account.</p> |
| 1915 | 1936 | style_finish_page(); |
| 1916 | 1937 | return; |
| 1938 | + } |
| 1939 | + if( P("pwreset")!=0 ){ |
| 1940 | + login_reqpwreset_page(); |
| 1941 | + return; |
| 1917 | 1942 | } |
| 1918 | 1943 | zPerms = db_get("default-perms", "u"); |
| 1919 | 1944 | |
| 1920 | 1945 | /* Prompt the user for email alerts if this repository is configured for |
| 1921 | 1946 | ** email alerts and if the default permissions include "7" */ |
| | @@ -2104,11 +2129,13 @@ |
| 2104 | 2129 | @ value="%h(zEAddr)" size="30"></td> |
| 2105 | 2130 | @ </tr> |
| 2106 | 2131 | if( iErrLine==3 ){ |
| 2107 | 2132 | @ <tr><td><td><span class='loginError'>↑ %h(zErr)</span> |
| 2108 | 2133 | 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)"> |
| 2110 | 2137 | } |
| 2111 | 2138 | @ </td></tr> |
| 2112 | 2139 | } |
| 2113 | 2140 | if( canDoAlerts ){ |
| 2114 | 2141 | int a = atoi(PD("alerts","1")); |
| | @@ -2162,10 +2189,151 @@ |
| 2162 | 2189 | @ </form> |
| 2163 | 2190 | style_finish_page(); |
| 2164 | 2191 | |
| 2165 | 2192 | free(zCaptcha); |
| 2166 | 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'>↑ %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'>↑ %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 | +} |
| 2167 | 2335 | |
| 2168 | 2336 | /* |
| 2169 | 2337 | ** Run SQL on the repository database for every repository in our |
| 2170 | 2338 | ** login group. The SQL is run in a separate database connection. |
| 2171 | 2339 | ** |
| 2172 | 2340 | |