Fossil SCM
Update the sub-repository capability so that it is able to restrict permissions on the sub-repository to a subset of the login permissions.
Commit
c477b2470f552fe46bf269ba2cc722d3f7980f89
Parent
97d0118794cdb35…
2 files changed
+87
-4
+14
-3
+87
-4
| --- src/login.c | ||
| +++ src/login.c | ||
| @@ -500,27 +500,32 @@ | ||
| 500 | 500 | /* Set the capabilities */ |
| 501 | 501 | login_set_capabilities(zCap); |
| 502 | 502 | login_set_anon_nobody_capabilities(); |
| 503 | 503 | } |
| 504 | 504 | |
| 505 | +/* | |
| 506 | +** Memory of settings | |
| 507 | +*/ | |
| 508 | +static int login_anon_once = 1; | |
| 509 | +static char login_settings[26]; | |
| 510 | + | |
| 505 | 511 | /* |
| 506 | 512 | ** Add the default privileges of users "nobody" and "anonymous" as appropriate |
| 507 | 513 | ** for the user g.zLogin. |
| 508 | 514 | */ |
| 509 | 515 | void login_set_anon_nobody_capabilities(void){ |
| 510 | - static int once = 1; | |
| 511 | - if( g.zLogin && once ){ | |
| 516 | + if( g.zLogin && login_anon_once ){ | |
| 512 | 517 | const char *zCap; |
| 513 | 518 | /* All logged-in users inherit privileges from "nobody" */ |
| 514 | 519 | zCap = db_text("", "SELECT cap FROM user WHERE login = 'nobody'"); |
| 515 | 520 | login_set_capabilities(zCap); |
| 516 | 521 | if( fossil_strcmp(g.zLogin, "nobody")!=0 ){ |
| 517 | 522 | /* All logged-in users inherit privileges from "anonymous" */ |
| 518 | 523 | zCap = db_text("", "SELECT cap FROM user WHERE login = 'anonymous'"); |
| 519 | 524 | login_set_capabilities(zCap); |
| 520 | 525 | } |
| 521 | - once = 0; | |
| 526 | + login_anon_once = 0; | |
| 522 | 527 | } |
| 523 | 528 | } |
| 524 | 529 | |
| 525 | 530 | /* |
| 526 | 531 | ** Set the global capability flags based on a capability string. |
| @@ -528,10 +533,13 @@ | ||
| 528 | 533 | void login_set_capabilities(const char *zCap){ |
| 529 | 534 | static char *zDev = 0; |
| 530 | 535 | static char *zUser = 0; |
| 531 | 536 | int i; |
| 532 | 537 | for(i=0; zCap[i]; i++){ |
| 538 | + int c = zCap[i]; | |
| 539 | + if( c<'a' || c>'z' ) continue; | |
| 540 | + login_settings[c-'a'] = 1; | |
| 533 | 541 | switch( zCap[i] ){ |
| 534 | 542 | case 's': g.okSetup = 1; /* Fall thru into Admin */ |
| 535 | 543 | case 'a': g.okAdmin = g.okRdTkt = g.okWrTkt = g.okZip = |
| 536 | 544 | g.okRdWiki = g.okWrWiki = g.okNewWiki = |
| 537 | 545 | g.okApndWiki = g.okHistory = g.okClone = |
| @@ -617,18 +625,93 @@ | ||
| 617 | 625 | case 's': rc = g.okSetup; break; |
| 618 | 626 | case 't': rc = g.okTktFmt; break; |
| 619 | 627 | /* case 'u': READER */ |
| 620 | 628 | /* case 'v': DEVELOPER */ |
| 621 | 629 | case 'w': rc = g.okWrTkt; break; |
| 622 | - /* case 'x': */ | |
| 630 | + case 'x': rc = g.okPrivate; break; | |
| 623 | 631 | /* case 'y': */ |
| 624 | 632 | case 'z': rc = g.okZip; break; |
| 625 | 633 | default: rc = 0; break; |
| 626 | 634 | } |
| 627 | 635 | } |
| 628 | 636 | return rc; |
| 629 | 637 | } |
| 638 | + | |
| 639 | +/* | |
| 640 | +** For every character in zCap between 'a' and 'z' set a byte in seen[]. | |
| 641 | +*/ | |
| 642 | +static void setCap(const char *zCap, char *seen){ | |
| 643 | + int c; | |
| 644 | + if( zCap ){ | |
| 645 | + while( (c = *(zCap++))!=0 ){ | |
| 646 | + if( c>='a' && c<='z' ) seen[c-'a'] = 1; | |
| 647 | + } | |
| 648 | + } | |
| 649 | +} | |
| 650 | + | |
| 651 | +/* | |
| 652 | +** Remove privileges such that previleges are restricted to the | |
| 653 | +** set given in the argument. | |
| 654 | +*/ | |
| 655 | +void login_restrict_capabilities(const char *zAllowed){ | |
| 656 | + char zNew[30]; | |
| 657 | + char seen[26]; | |
| 658 | + int nNew = 0; | |
| 659 | + int i; | |
| 660 | + | |
| 661 | + /* Compute the intersection of current settings with zAllowed[] and | |
| 662 | + ** store the result in zNew[] | |
| 663 | + */ | |
| 664 | + memset(seen, 0, sizeof(seen)); | |
| 665 | + setCap(zAllowed, seen); | |
| 666 | + if( seen['v'-'a'] ){ | |
| 667 | + char *z = db_text(0, "SELECT cap FROM user WHERE login='developer'"); | |
| 668 | + setCap(z, seen); | |
| 669 | + fossil_free(z); | |
| 670 | + } | |
| 671 | + if( seen['u'-'a'] ){ | |
| 672 | + char *z = db_text(0, "SELECT cap FROM user WHERE login='reader'"); | |
| 673 | + setCap(z, seen); | |
| 674 | + fossil_free(z); | |
| 675 | + } | |
| 676 | + seen['u'-'a'] = 0; | |
| 677 | + seen['v'-'a'] = 0; | |
| 678 | + for(i=0; i<sizeof(seen); i++){ | |
| 679 | + if( seen[i] && login_settings[i] ) zNew[nNew++] = i+'a'; | |
| 680 | + } | |
| 681 | + zNew[nNew] = 0; | |
| 682 | + | |
| 683 | + /* Turn off all capabilities */ | |
| 684 | + g.okSetup = 0; | |
| 685 | + g.okAdmin = 0; | |
| 686 | + g.okDelete = 0; | |
| 687 | + g.okPassword = 0; | |
| 688 | + g.okQuery = 0; | |
| 689 | + g.okWrite = 0; | |
| 690 | + g.okRead = 0; | |
| 691 | + g.okHistory = 0; | |
| 692 | + g.okClone = 0; | |
| 693 | + g.okRdWiki = 0; | |
| 694 | + g.okNewWiki = 0; | |
| 695 | + g.okApndWiki = 0; | |
| 696 | + g.okWrWiki = 0; | |
| 697 | + g.okRdTkt = 0; | |
| 698 | + g.okNewTkt = 0; | |
| 699 | + g.okApndTkt = 0; | |
| 700 | + g.okWrTkt = 0; | |
| 701 | + g.okAttach = 0; | |
| 702 | + g.okTktFmt = 0; | |
| 703 | + g.okRdAddr = 0; | |
| 704 | + g.okZip = 0; | |
| 705 | + g.okPrivate = 0; | |
| 706 | + memset(login_settings, 0, sizeof(login_settings)); | |
| 707 | + | |
| 708 | + /* Set the reduced capabilities */ | |
| 709 | + login_set_capabilities(zNew); | |
| 710 | + login_anon_once = 1; | |
| 711 | + login_set_anon_nobody_capabilities(); | |
| 712 | +} | |
| 630 | 713 | |
| 631 | 714 | /* |
| 632 | 715 | ** Call this routine when the credential check fails. It causes |
| 633 | 716 | ** a redirect to the "login" page. |
| 634 | 717 | */ |
| 635 | 718 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -500,27 +500,32 @@ | |
| 500 | /* Set the capabilities */ |
| 501 | login_set_capabilities(zCap); |
| 502 | login_set_anon_nobody_capabilities(); |
| 503 | } |
| 504 | |
| 505 | /* |
| 506 | ** Add the default privileges of users "nobody" and "anonymous" as appropriate |
| 507 | ** for the user g.zLogin. |
| 508 | */ |
| 509 | void login_set_anon_nobody_capabilities(void){ |
| 510 | static int once = 1; |
| 511 | if( g.zLogin && once ){ |
| 512 | const char *zCap; |
| 513 | /* All logged-in users inherit privileges from "nobody" */ |
| 514 | zCap = db_text("", "SELECT cap FROM user WHERE login = 'nobody'"); |
| 515 | login_set_capabilities(zCap); |
| 516 | if( fossil_strcmp(g.zLogin, "nobody")!=0 ){ |
| 517 | /* All logged-in users inherit privileges from "anonymous" */ |
| 518 | zCap = db_text("", "SELECT cap FROM user WHERE login = 'anonymous'"); |
| 519 | login_set_capabilities(zCap); |
| 520 | } |
| 521 | once = 0; |
| 522 | } |
| 523 | } |
| 524 | |
| 525 | /* |
| 526 | ** Set the global capability flags based on a capability string. |
| @@ -528,10 +533,13 @@ | |
| 528 | void login_set_capabilities(const char *zCap){ |
| 529 | static char *zDev = 0; |
| 530 | static char *zUser = 0; |
| 531 | int i; |
| 532 | for(i=0; zCap[i]; i++){ |
| 533 | switch( zCap[i] ){ |
| 534 | case 's': g.okSetup = 1; /* Fall thru into Admin */ |
| 535 | case 'a': g.okAdmin = g.okRdTkt = g.okWrTkt = g.okZip = |
| 536 | g.okRdWiki = g.okWrWiki = g.okNewWiki = |
| 537 | g.okApndWiki = g.okHistory = g.okClone = |
| @@ -617,18 +625,93 @@ | |
| 617 | case 's': rc = g.okSetup; break; |
| 618 | case 't': rc = g.okTktFmt; break; |
| 619 | /* case 'u': READER */ |
| 620 | /* case 'v': DEVELOPER */ |
| 621 | case 'w': rc = g.okWrTkt; break; |
| 622 | /* case 'x': */ |
| 623 | /* case 'y': */ |
| 624 | case 'z': rc = g.okZip; break; |
| 625 | default: rc = 0; break; |
| 626 | } |
| 627 | } |
| 628 | return rc; |
| 629 | } |
| 630 | |
| 631 | /* |
| 632 | ** Call this routine when the credential check fails. It causes |
| 633 | ** a redirect to the "login" page. |
| 634 | */ |
| 635 |
| --- src/login.c | |
| +++ src/login.c | |
| @@ -500,27 +500,32 @@ | |
| 500 | /* Set the capabilities */ |
| 501 | login_set_capabilities(zCap); |
| 502 | login_set_anon_nobody_capabilities(); |
| 503 | } |
| 504 | |
| 505 | /* |
| 506 | ** Memory of settings |
| 507 | */ |
| 508 | static int login_anon_once = 1; |
| 509 | static char login_settings[26]; |
| 510 | |
| 511 | /* |
| 512 | ** Add the default privileges of users "nobody" and "anonymous" as appropriate |
| 513 | ** for the user g.zLogin. |
| 514 | */ |
| 515 | void login_set_anon_nobody_capabilities(void){ |
| 516 | if( g.zLogin && login_anon_once ){ |
| 517 | const char *zCap; |
| 518 | /* All logged-in users inherit privileges from "nobody" */ |
| 519 | zCap = db_text("", "SELECT cap FROM user WHERE login = 'nobody'"); |
| 520 | login_set_capabilities(zCap); |
| 521 | if( fossil_strcmp(g.zLogin, "nobody")!=0 ){ |
| 522 | /* All logged-in users inherit privileges from "anonymous" */ |
| 523 | zCap = db_text("", "SELECT cap FROM user WHERE login = 'anonymous'"); |
| 524 | login_set_capabilities(zCap); |
| 525 | } |
| 526 | login_anon_once = 0; |
| 527 | } |
| 528 | } |
| 529 | |
| 530 | /* |
| 531 | ** Set the global capability flags based on a capability string. |
| @@ -528,10 +533,13 @@ | |
| 533 | void login_set_capabilities(const char *zCap){ |
| 534 | static char *zDev = 0; |
| 535 | static char *zUser = 0; |
| 536 | int i; |
| 537 | for(i=0; zCap[i]; i++){ |
| 538 | int c = zCap[i]; |
| 539 | if( c<'a' || c>'z' ) continue; |
| 540 | login_settings[c-'a'] = 1; |
| 541 | switch( zCap[i] ){ |
| 542 | case 's': g.okSetup = 1; /* Fall thru into Admin */ |
| 543 | case 'a': g.okAdmin = g.okRdTkt = g.okWrTkt = g.okZip = |
| 544 | g.okRdWiki = g.okWrWiki = g.okNewWiki = |
| 545 | g.okApndWiki = g.okHistory = g.okClone = |
| @@ -617,18 +625,93 @@ | |
| 625 | case 's': rc = g.okSetup; break; |
| 626 | case 't': rc = g.okTktFmt; break; |
| 627 | /* case 'u': READER */ |
| 628 | /* case 'v': DEVELOPER */ |
| 629 | case 'w': rc = g.okWrTkt; break; |
| 630 | case 'x': rc = g.okPrivate; break; |
| 631 | /* case 'y': */ |
| 632 | case 'z': rc = g.okZip; break; |
| 633 | default: rc = 0; break; |
| 634 | } |
| 635 | } |
| 636 | return rc; |
| 637 | } |
| 638 | |
| 639 | /* |
| 640 | ** For every character in zCap between 'a' and 'z' set a byte in seen[]. |
| 641 | */ |
| 642 | static void setCap(const char *zCap, char *seen){ |
| 643 | int c; |
| 644 | if( zCap ){ |
| 645 | while( (c = *(zCap++))!=0 ){ |
| 646 | if( c>='a' && c<='z' ) seen[c-'a'] = 1; |
| 647 | } |
| 648 | } |
| 649 | } |
| 650 | |
| 651 | /* |
| 652 | ** Remove privileges such that previleges are restricted to the |
| 653 | ** set given in the argument. |
| 654 | */ |
| 655 | void login_restrict_capabilities(const char *zAllowed){ |
| 656 | char zNew[30]; |
| 657 | char seen[26]; |
| 658 | int nNew = 0; |
| 659 | int i; |
| 660 | |
| 661 | /* Compute the intersection of current settings with zAllowed[] and |
| 662 | ** store the result in zNew[] |
| 663 | */ |
| 664 | memset(seen, 0, sizeof(seen)); |
| 665 | setCap(zAllowed, seen); |
| 666 | if( seen['v'-'a'] ){ |
| 667 | char *z = db_text(0, "SELECT cap FROM user WHERE login='developer'"); |
| 668 | setCap(z, seen); |
| 669 | fossil_free(z); |
| 670 | } |
| 671 | if( seen['u'-'a'] ){ |
| 672 | char *z = db_text(0, "SELECT cap FROM user WHERE login='reader'"); |
| 673 | setCap(z, seen); |
| 674 | fossil_free(z); |
| 675 | } |
| 676 | seen['u'-'a'] = 0; |
| 677 | seen['v'-'a'] = 0; |
| 678 | for(i=0; i<sizeof(seen); i++){ |
| 679 | if( seen[i] && login_settings[i] ) zNew[nNew++] = i+'a'; |
| 680 | } |
| 681 | zNew[nNew] = 0; |
| 682 | |
| 683 | /* Turn off all capabilities */ |
| 684 | g.okSetup = 0; |
| 685 | g.okAdmin = 0; |
| 686 | g.okDelete = 0; |
| 687 | g.okPassword = 0; |
| 688 | g.okQuery = 0; |
| 689 | g.okWrite = 0; |
| 690 | g.okRead = 0; |
| 691 | g.okHistory = 0; |
| 692 | g.okClone = 0; |
| 693 | g.okRdWiki = 0; |
| 694 | g.okNewWiki = 0; |
| 695 | g.okApndWiki = 0; |
| 696 | g.okWrWiki = 0; |
| 697 | g.okRdTkt = 0; |
| 698 | g.okNewTkt = 0; |
| 699 | g.okApndTkt = 0; |
| 700 | g.okWrTkt = 0; |
| 701 | g.okAttach = 0; |
| 702 | g.okTktFmt = 0; |
| 703 | g.okRdAddr = 0; |
| 704 | g.okZip = 0; |
| 705 | g.okPrivate = 0; |
| 706 | memset(login_settings, 0, sizeof(login_settings)); |
| 707 | |
| 708 | /* Set the reduced capabilities */ |
| 709 | login_set_capabilities(zNew); |
| 710 | login_anon_once = 1; |
| 711 | login_set_anon_nobody_capabilities(); |
| 712 | } |
| 713 | |
| 714 | /* |
| 715 | ** Call this routine when the credential check fails. It causes |
| 716 | ** a redirect to the "login" page. |
| 717 | */ |
| 718 |
+14
-3
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -983,24 +983,35 @@ | ||
| 983 | 983 | |
| 984 | 984 | /* Look for sub-repositories. A sub-repository is another repository |
| 985 | 985 | ** that accepts the login credentials of the current repository. A |
| 986 | 986 | ** subrepository is identified by a CONFIG table entry "subrepo:NAME" |
| 987 | 987 | ** where NAME is the first component of the path. The value of the |
| 988 | - ** the CONFIG entries is the name of the repository. | |
| 988 | + ** the CONFIG entries is the string "CAP:FILENAME" where CAP is the | |
| 989 | + ** maximum capability string and FILENAME is the new repository | |
| 990 | + ** filename. | |
| 989 | 991 | */ |
| 990 | 992 | zAltRepo = db_text(0, "SELECT value FROM config WHERE name='subrepo:%q'", |
| 991 | 993 | g.zPath); |
| 992 | 994 | if( zAltRepo ){ |
| 993 | 995 | int nHost; |
| 996 | + int jj; | |
| 997 | + char *zInheritCap = zAltRepo; | |
| 994 | 998 | login_check_credentials(); |
| 999 | + for(jj=0; zAltRepo[jj] && zAltRepo[jj]!=':'; jj++){} | |
| 1000 | + if( zAltRepo[jj]==':' ){ | |
| 1001 | + zAltRepo[jj] = 0; | |
| 1002 | + zAltRepo += jj+1; | |
| 1003 | + }else{ | |
| 1004 | + zInheritCap = ""; | |
| 1005 | + } | |
| 995 | 1006 | if( zAltRepo[0]!='/' ){ |
| 996 | - zAltRepo = mprintf("%s/../%z", g.zRepositoryName, zAltRepo); | |
| 1007 | + zAltRepo = mprintf("%s/../%s", g.zRepositoryName, zAltRepo); | |
| 997 | 1008 | file_simplify_name(zAltRepo, -1); |
| 998 | 1009 | } |
| 999 | 1010 | db_close(1); |
| 1000 | 1011 | db_open_repository(zAltRepo); |
| 1001 | - fossil_free(zAltRepo); | |
| 1012 | + login_restrict_capabilities(zInheritCap); | |
| 1002 | 1013 | zPath += i; |
| 1003 | 1014 | nHost = g.zTop - g.zBaseURL; |
| 1004 | 1015 | g.zBaseURL = mprintf("%z/%s", g.zBaseURL, g.zPath); |
| 1005 | 1016 | g.zTop = g.zBaseURL + nHost; |
| 1006 | 1017 | continue; |
| 1007 | 1018 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -983,24 +983,35 @@ | |
| 983 | |
| 984 | /* Look for sub-repositories. A sub-repository is another repository |
| 985 | ** that accepts the login credentials of the current repository. A |
| 986 | ** subrepository is identified by a CONFIG table entry "subrepo:NAME" |
| 987 | ** where NAME is the first component of the path. The value of the |
| 988 | ** the CONFIG entries is the name of the repository. |
| 989 | */ |
| 990 | zAltRepo = db_text(0, "SELECT value FROM config WHERE name='subrepo:%q'", |
| 991 | g.zPath); |
| 992 | if( zAltRepo ){ |
| 993 | int nHost; |
| 994 | login_check_credentials(); |
| 995 | if( zAltRepo[0]!='/' ){ |
| 996 | zAltRepo = mprintf("%s/../%z", g.zRepositoryName, zAltRepo); |
| 997 | file_simplify_name(zAltRepo, -1); |
| 998 | } |
| 999 | db_close(1); |
| 1000 | db_open_repository(zAltRepo); |
| 1001 | fossil_free(zAltRepo); |
| 1002 | zPath += i; |
| 1003 | nHost = g.zTop - g.zBaseURL; |
| 1004 | g.zBaseURL = mprintf("%z/%s", g.zBaseURL, g.zPath); |
| 1005 | g.zTop = g.zBaseURL + nHost; |
| 1006 | continue; |
| 1007 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -983,24 +983,35 @@ | |
| 983 | |
| 984 | /* Look for sub-repositories. A sub-repository is another repository |
| 985 | ** that accepts the login credentials of the current repository. A |
| 986 | ** subrepository is identified by a CONFIG table entry "subrepo:NAME" |
| 987 | ** where NAME is the first component of the path. The value of the |
| 988 | ** the CONFIG entries is the string "CAP:FILENAME" where CAP is the |
| 989 | ** maximum capability string and FILENAME is the new repository |
| 990 | ** filename. |
| 991 | */ |
| 992 | zAltRepo = db_text(0, "SELECT value FROM config WHERE name='subrepo:%q'", |
| 993 | g.zPath); |
| 994 | if( zAltRepo ){ |
| 995 | int nHost; |
| 996 | int jj; |
| 997 | char *zInheritCap = zAltRepo; |
| 998 | login_check_credentials(); |
| 999 | for(jj=0; zAltRepo[jj] && zAltRepo[jj]!=':'; jj++){} |
| 1000 | if( zAltRepo[jj]==':' ){ |
| 1001 | zAltRepo[jj] = 0; |
| 1002 | zAltRepo += jj+1; |
| 1003 | }else{ |
| 1004 | zInheritCap = ""; |
| 1005 | } |
| 1006 | if( zAltRepo[0]!='/' ){ |
| 1007 | zAltRepo = mprintf("%s/../%s", g.zRepositoryName, zAltRepo); |
| 1008 | file_simplify_name(zAltRepo, -1); |
| 1009 | } |
| 1010 | db_close(1); |
| 1011 | db_open_repository(zAltRepo); |
| 1012 | login_restrict_capabilities(zInheritCap); |
| 1013 | zPath += i; |
| 1014 | nHost = g.zTop - g.zBaseURL; |
| 1015 | g.zBaseURL = mprintf("%z/%s", g.zBaseURL, g.zPath); |
| 1016 | g.zTop = g.zBaseURL + nHost; |
| 1017 | continue; |
| 1018 |