Fossil SCM
Begin adding the ability to restrict self-registration to people with a particular email address pattern. This check-in provides the setting to specify the authorized email addresses, but an attacker can still lie about his email address and sneak in that way. Still a work-in-progress.
Commit
7916dbaa03f6b8151cfb94ddb7663939bd581592cb52dc23636639c049c9a76d
Parent
2cfd12564010011…
4 files changed
+30
+14
-1
+1
-11
+15
+30
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -1459,10 +1459,37 @@ | ||
| 1459 | 1459 | "SELECT 1 FROM event WHERE user=%Q OR euser=%Q", |
| 1460 | 1460 | zUserID, zUserID, zUserID |
| 1461 | 1461 | ); |
| 1462 | 1462 | return rc; |
| 1463 | 1463 | } |
| 1464 | + | |
| 1465 | +/* | |
| 1466 | +** Check an email address and confirm that it is valid for self-registration. | |
| 1467 | +** The email address is known already to be well-formed. | |
| 1468 | +** | |
| 1469 | +** The default behavior is that any valid email address is accepted. | |
| 1470 | +** But if the "self-reg-email" setting exists and is not empty, then | |
| 1471 | +** it is a comma-separated list of GLOB patterns for email addresses | |
| 1472 | +** that are authorized to self-register. | |
| 1473 | +*/ | |
| 1474 | +static int authorized_self_register_email(const char *zEAddr){ | |
| 1475 | + char *zGlob = db_get("self-reg-email",0); | |
| 1476 | + Glob *pGlob; | |
| 1477 | + char *zAddr; | |
| 1478 | + int rc; | |
| 1479 | + | |
| 1480 | + if( zGlob==0 || zGlob[0]==0 ) return 1; | |
| 1481 | + zGlob = fossil_strtolwr(fossil_strdup(zGlob)); | |
| 1482 | + pGlob = glob_create(zGlob); | |
| 1483 | + fossil_free(zGlob); | |
| 1484 | + | |
| 1485 | + zAddr = fossil_strtolwr(fossil_strdup(zEAddr)); | |
| 1486 | + rc = glob_match(pGlob, zAddr); | |
| 1487 | + fossil_free(zAddr); | |
| 1488 | + glob_free(pGlob); | |
| 1489 | + return rc!=0; | |
| 1490 | +} | |
| 1464 | 1491 | |
| 1465 | 1492 | /* |
| 1466 | 1493 | ** WEBPAGE: register |
| 1467 | 1494 | ** |
| 1468 | 1495 | ** Page to allow users to self-register. The "self-register" setting |
| @@ -1521,10 +1548,13 @@ | ||
| 1521 | 1548 | iErrLine = 3; |
| 1522 | 1549 | zErr = "Required"; |
| 1523 | 1550 | }else if( email_address_is_valid(zEAddr,0)==0 ){ |
| 1524 | 1551 | iErrLine = 3; |
| 1525 | 1552 | zErr = "Not a valid email address"; |
| 1553 | + }else if( authorized_self_register_email(zEAddr)==0 ){ | |
| 1554 | + iErrLine = 3; | |
| 1555 | + zErr = "Not an authorized email address"; | |
| 1526 | 1556 | }else if( strlen(zPasswd)<6 ){ |
| 1527 | 1557 | iErrLine = 4; |
| 1528 | 1558 | zErr = "Password must be at least 6 characters long"; |
| 1529 | 1559 | }else if( fossil_strcmp(zPasswd,zConfirm)!=0 ){ |
| 1530 | 1560 | iErrLine = 5; |
| 1531 | 1561 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -1459,10 +1459,37 @@ | |
| 1459 | "SELECT 1 FROM event WHERE user=%Q OR euser=%Q", |
| 1460 | zUserID, zUserID, zUserID |
| 1461 | ); |
| 1462 | return rc; |
| 1463 | } |
| 1464 | |
| 1465 | /* |
| 1466 | ** WEBPAGE: register |
| 1467 | ** |
| 1468 | ** Page to allow users to self-register. The "self-register" setting |
| @@ -1521,10 +1548,13 @@ | |
| 1521 | iErrLine = 3; |
| 1522 | zErr = "Required"; |
| 1523 | }else if( email_address_is_valid(zEAddr,0)==0 ){ |
| 1524 | iErrLine = 3; |
| 1525 | zErr = "Not a valid email address"; |
| 1526 | }else if( strlen(zPasswd)<6 ){ |
| 1527 | iErrLine = 4; |
| 1528 | zErr = "Password must be at least 6 characters long"; |
| 1529 | }else if( fossil_strcmp(zPasswd,zConfirm)!=0 ){ |
| 1530 | iErrLine = 5; |
| 1531 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -1459,10 +1459,37 @@ | |
| 1459 | "SELECT 1 FROM event WHERE user=%Q OR euser=%Q", |
| 1460 | zUserID, zUserID, zUserID |
| 1461 | ); |
| 1462 | return rc; |
| 1463 | } |
| 1464 | |
| 1465 | /* |
| 1466 | ** Check an email address and confirm that it is valid for self-registration. |
| 1467 | ** The email address is known already to be well-formed. |
| 1468 | ** |
| 1469 | ** The default behavior is that any valid email address is accepted. |
| 1470 | ** But if the "self-reg-email" setting exists and is not empty, then |
| 1471 | ** it is a comma-separated list of GLOB patterns for email addresses |
| 1472 | ** that are authorized to self-register. |
| 1473 | */ |
| 1474 | static int authorized_self_register_email(const char *zEAddr){ |
| 1475 | char *zGlob = db_get("self-reg-email",0); |
| 1476 | Glob *pGlob; |
| 1477 | char *zAddr; |
| 1478 | int rc; |
| 1479 | |
| 1480 | if( zGlob==0 || zGlob[0]==0 ) return 1; |
| 1481 | zGlob = fossil_strtolwr(fossil_strdup(zGlob)); |
| 1482 | pGlob = glob_create(zGlob); |
| 1483 | fossil_free(zGlob); |
| 1484 | |
| 1485 | zAddr = fossil_strtolwr(fossil_strdup(zEAddr)); |
| 1486 | rc = glob_match(pGlob, zAddr); |
| 1487 | fossil_free(zAddr); |
| 1488 | glob_free(pGlob); |
| 1489 | return rc!=0; |
| 1490 | } |
| 1491 | |
| 1492 | /* |
| 1493 | ** WEBPAGE: register |
| 1494 | ** |
| 1495 | ** Page to allow users to self-register. The "self-register" setting |
| @@ -1521,10 +1548,13 @@ | |
| 1548 | iErrLine = 3; |
| 1549 | zErr = "Required"; |
| 1550 | }else if( email_address_is_valid(zEAddr,0)==0 ){ |
| 1551 | iErrLine = 3; |
| 1552 | zErr = "Not a valid email address"; |
| 1553 | }else if( authorized_self_register_email(zEAddr)==0 ){ |
| 1554 | iErrLine = 3; |
| 1555 | zErr = "Not an authorized email address"; |
| 1556 | }else if( strlen(zPasswd)<6 ){ |
| 1557 | iErrLine = 4; |
| 1558 | zErr = "Password must be at least 6 characters long"; |
| 1559 | }else if( fossil_strcmp(zPasswd,zConfirm)!=0 ){ |
| 1560 | iErrLine = 5; |
| 1561 |
+14
-1
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -503,15 +503,28 @@ | ||
| 503 | 503 | @ <hr /> |
| 504 | 504 | onoff_attribute("Allow users to register themselves", |
| 505 | 505 | "self-register", "selfregister", 0, 0); |
| 506 | 506 | @ <p>Allow users to register themselves through the HTTP UI. |
| 507 | 507 | @ The registration form always requires filling in a CAPTCHA |
| 508 | - @ (<em>auto-captcha</em> setting is ignored). Still, bear in mind that anyone | |
| 508 | + @ (<em>auto-captcha</em> setting is ignored). Keep in mind that anyone | |
| 509 | 509 | @ can register under any user name. This option is useful for public projects |
| 510 | 510 | @ where you do not want everyone in any ticket discussion to be named |
| 511 | 511 | @ "Anonymous". (Property: "self-register")</p> |
| 512 | 512 | |
| 513 | + @ <hr /> | |
| 514 | + entry_attribute("Authorized self-registration email addresses", 35, | |
| 515 | + "self-reg-email", "selfregemail", "", 0); | |
| 516 | + @ <p>This is a comma-separated list of GLOB patterns that specify | |
| 517 | + @ email addresses that are authorized to self-register. If blank | |
| 518 | + @ (the usual case), then any email address can be used to self-register. | |
| 519 | + @ This setting is used to limit self-registration to members of a particular | |
| 520 | + @ organization or group based on their email address. For example, | |
| 521 | + @ if the pattern is "<tt>*@megacorp.com, *@af.mil.to</tt>" then | |
| 522 | + @ only employees of MegaCorp and members of the Tonganese airforce | |
| 523 | + @ can self-register. | |
| 524 | + @ (Property: "self-reg-email")</p> | |
| 525 | + | |
| 513 | 526 | @ <hr /> |
| 514 | 527 | entry_attribute("Default privileges", 10, "default-perms", |
| 515 | 528 | "defaultperms", "u", 0); |
| 516 | 529 | @ <p>Permissions given to users that... <ul><li>register themselves using |
| 517 | 530 | @ the self-registration procedure (if enabled), or <li>access "public" |
| 518 | 531 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -503,15 +503,28 @@ | |
| 503 | @ <hr /> |
| 504 | onoff_attribute("Allow users to register themselves", |
| 505 | "self-register", "selfregister", 0, 0); |
| 506 | @ <p>Allow users to register themselves through the HTTP UI. |
| 507 | @ The registration form always requires filling in a CAPTCHA |
| 508 | @ (<em>auto-captcha</em> setting is ignored). Still, bear in mind that anyone |
| 509 | @ can register under any user name. This option is useful for public projects |
| 510 | @ where you do not want everyone in any ticket discussion to be named |
| 511 | @ "Anonymous". (Property: "self-register")</p> |
| 512 | |
| 513 | @ <hr /> |
| 514 | entry_attribute("Default privileges", 10, "default-perms", |
| 515 | "defaultperms", "u", 0); |
| 516 | @ <p>Permissions given to users that... <ul><li>register themselves using |
| 517 | @ the self-registration procedure (if enabled), or <li>access "public" |
| 518 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -503,15 +503,28 @@ | |
| 503 | @ <hr /> |
| 504 | onoff_attribute("Allow users to register themselves", |
| 505 | "self-register", "selfregister", 0, 0); |
| 506 | @ <p>Allow users to register themselves through the HTTP UI. |
| 507 | @ The registration form always requires filling in a CAPTCHA |
| 508 | @ (<em>auto-captcha</em> setting is ignored). Keep in mind that anyone |
| 509 | @ can register under any user name. This option is useful for public projects |
| 510 | @ where you do not want everyone in any ticket discussion to be named |
| 511 | @ "Anonymous". (Property: "self-register")</p> |
| 512 | |
| 513 | @ <hr /> |
| 514 | entry_attribute("Authorized self-registration email addresses", 35, |
| 515 | "self-reg-email", "selfregemail", "", 0); |
| 516 | @ <p>This is a comma-separated list of GLOB patterns that specify |
| 517 | @ email addresses that are authorized to self-register. If blank |
| 518 | @ (the usual case), then any email address can be used to self-register. |
| 519 | @ This setting is used to limit self-registration to members of a particular |
| 520 | @ organization or group based on their email address. For example, |
| 521 | @ if the pattern is "<tt>*@megacorp.com, *@af.mil.to</tt>" then |
| 522 | @ only employees of MegaCorp and members of the Tonganese airforce |
| 523 | @ can self-register. |
| 524 | @ (Property: "self-reg-email")</p> |
| 525 | |
| 526 | @ <hr /> |
| 527 | entry_attribute("Default privileges", 10, "default-perms", |
| 528 | "defaultperms", "u", 0); |
| 529 | @ <p>Permissions given to users that... <ul><li>register themselves using |
| 530 | @ the self-registration procedure (if enabled), or <li>access "public" |
| 531 |
+1
-11
| --- src/url.c | ||
| +++ src/url.c | ||
| @@ -66,20 +66,10 @@ | ||
| 66 | 66 | int proxyOrigPort; /* Tunneled port number for https through proxy */ |
| 67 | 67 | }; |
| 68 | 68 | #endif /* INTERFACE */ |
| 69 | 69 | |
| 70 | 70 | |
| 71 | -/* | |
| 72 | -** Convert a string to lower-case. | |
| 73 | -*/ | |
| 74 | -static void url_tolower(char *z){ | |
| 75 | - while( *z ){ | |
| 76 | - *z = fossil_tolower(*z); | |
| 77 | - z++; | |
| 78 | - } | |
| 79 | -} | |
| 80 | - | |
| 81 | 71 | /* |
| 82 | 72 | ** Parse the given URL. Populate members of the provided UrlData structure |
| 83 | 73 | ** as follows: |
| 84 | 74 | ** |
| 85 | 75 | ** isFile True if FILE: |
| @@ -177,11 +167,11 @@ | ||
| 177 | 167 | pUrlData->name++; |
| 178 | 168 | pUrlData->name[n-2] = 0; |
| 179 | 169 | } |
| 180 | 170 | zLogin = mprintf(""); |
| 181 | 171 | } |
| 182 | - url_tolower(pUrlData->name); | |
| 172 | + fossil_strtolwr(pUrlData->name); | |
| 183 | 173 | if( c==':' ){ |
| 184 | 174 | pUrlData->port = 0; |
| 185 | 175 | i++; |
| 186 | 176 | while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ |
| 187 | 177 | pUrlData->port = pUrlData->port*10 + c - '0'; |
| 188 | 178 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -66,20 +66,10 @@ | |
| 66 | int proxyOrigPort; /* Tunneled port number for https through proxy */ |
| 67 | }; |
| 68 | #endif /* INTERFACE */ |
| 69 | |
| 70 | |
| 71 | /* |
| 72 | ** Convert a string to lower-case. |
| 73 | */ |
| 74 | static void url_tolower(char *z){ |
| 75 | while( *z ){ |
| 76 | *z = fossil_tolower(*z); |
| 77 | z++; |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | /* |
| 82 | ** Parse the given URL. Populate members of the provided UrlData structure |
| 83 | ** as follows: |
| 84 | ** |
| 85 | ** isFile True if FILE: |
| @@ -177,11 +167,11 @@ | |
| 177 | pUrlData->name++; |
| 178 | pUrlData->name[n-2] = 0; |
| 179 | } |
| 180 | zLogin = mprintf(""); |
| 181 | } |
| 182 | url_tolower(pUrlData->name); |
| 183 | if( c==':' ){ |
| 184 | pUrlData->port = 0; |
| 185 | i++; |
| 186 | while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ |
| 187 | pUrlData->port = pUrlData->port*10 + c - '0'; |
| 188 |
| --- src/url.c | |
| +++ src/url.c | |
| @@ -66,20 +66,10 @@ | |
| 66 | int proxyOrigPort; /* Tunneled port number for https through proxy */ |
| 67 | }; |
| 68 | #endif /* INTERFACE */ |
| 69 | |
| 70 | |
| 71 | /* |
| 72 | ** Parse the given URL. Populate members of the provided UrlData structure |
| 73 | ** as follows: |
| 74 | ** |
| 75 | ** isFile True if FILE: |
| @@ -177,11 +167,11 @@ | |
| 167 | pUrlData->name++; |
| 168 | pUrlData->name[n-2] = 0; |
| 169 | } |
| 170 | zLogin = mprintf(""); |
| 171 | } |
| 172 | fossil_strtolwr(pUrlData->name); |
| 173 | if( c==':' ){ |
| 174 | pUrlData->port = 0; |
| 175 | i++; |
| 176 | while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){ |
| 177 | pUrlData->port = pUrlData->port*10 + c - '0'; |
| 178 |
+15
| --- src/util.c | ||
| +++ src/util.c | ||
| @@ -140,10 +140,25 @@ | ||
| 140 | 140 | } |
| 141 | 141 | #else |
| 142 | 142 | fossil_free(p); |
| 143 | 143 | #endif |
| 144 | 144 | } |
| 145 | + | |
| 146 | +/* | |
| 147 | +** Translate every upper-case character in the input string into | |
| 148 | +** its equivalent lower-case. | |
| 149 | +*/ | |
| 150 | +char *fossil_strtolwr(char *zIn){ | |
| 151 | + char *zStart = zIn; | |
| 152 | + if( zIn ){ | |
| 153 | + while( *zIn ){ | |
| 154 | + *zIn = fossil_tolower(*zIn); | |
| 155 | + zIn++; | |
| 156 | + } | |
| 157 | + } | |
| 158 | + return zStart; | |
| 159 | +} | |
| 145 | 160 | |
| 146 | 161 | /* |
| 147 | 162 | ** This function implements a cross-platform "system()" interface. |
| 148 | 163 | */ |
| 149 | 164 | int fossil_system(const char *zOrigCmd){ |
| 150 | 165 |
| --- src/util.c | |
| +++ src/util.c | |
| @@ -140,10 +140,25 @@ | |
| 140 | } |
| 141 | #else |
| 142 | fossil_free(p); |
| 143 | #endif |
| 144 | } |
| 145 | |
| 146 | /* |
| 147 | ** This function implements a cross-platform "system()" interface. |
| 148 | */ |
| 149 | int fossil_system(const char *zOrigCmd){ |
| 150 |
| --- src/util.c | |
| +++ src/util.c | |
| @@ -140,10 +140,25 @@ | |
| 140 | } |
| 141 | #else |
| 142 | fossil_free(p); |
| 143 | #endif |
| 144 | } |
| 145 | |
| 146 | /* |
| 147 | ** Translate every upper-case character in the input string into |
| 148 | ** its equivalent lower-case. |
| 149 | */ |
| 150 | char *fossil_strtolwr(char *zIn){ |
| 151 | char *zStart = zIn; |
| 152 | if( zIn ){ |
| 153 | while( *zIn ){ |
| 154 | *zIn = fossil_tolower(*zIn); |
| 155 | zIn++; |
| 156 | } |
| 157 | } |
| 158 | return zStart; |
| 159 | } |
| 160 | |
| 161 | /* |
| 162 | ** This function implements a cross-platform "system()" interface. |
| 163 | */ |
| 164 | int fossil_system(const char *zOrigCmd){ |
| 165 |