| | @@ -307,22 +307,59 @@ |
| 307 | 307 | while( zPw[0]=='*' ){ zPw++; } |
| 308 | 308 | return zPw[0]!=0; |
| 309 | 309 | } |
| 310 | 310 | |
| 311 | 311 | /* |
| 312 | | -** Return true if user capability string zNew contains any capability |
| 313 | | -** letter which is not in user capability string zOrig, else 0. This |
| 314 | | -** does not take inherited permissions into account. Either argument |
| 315 | | -** may be NULL. |
| 312 | +** Return true if user capability strings zOrig and zNew materially |
| 313 | +** differ, taking into account that they may be sorted in an arbitary |
| 314 | +** order. This does not take inherited permissions into |
| 315 | +** account. Either argument may be NULL. A NULL and an empty string |
| 316 | +** are considered equivalent here. e.g. "abc" and "cab" are equivalent |
| 317 | +** for this purpose, but "aCb" and "acb" are not. |
| 318 | +*/ |
| 319 | +static int userCapsChanged(const char *zOrig, const char *zNew){ |
| 320 | + if( !zOrig ){ |
| 321 | + return zNew ? (0!=*zNew) : 0; |
| 322 | + }else if( !zNew ){ |
| 323 | + return 0!=*zOrig; |
| 324 | + }else if( 0==fossil_strcmp(zOrig, zNew) ){ |
| 325 | + return 0; |
| 326 | + }else{ |
| 327 | + /* We don't know that zOrig and zNew are sorted equivalently. The |
| 328 | + ** following steps will compare strings which contain all the same |
| 329 | + ** capabilities letters as equivalent, regardless of the letters' |
| 330 | + ** order in their strings. */ |
| 331 | + char aOrig[128]; /* table of zOrig bytes */ |
| 332 | + int nOrig = 0, nNew = 0; |
| 333 | + |
| 334 | + memset( &aOrig[0], 0, sizeof(aOrig) ); |
| 335 | + for( ; *zOrig; ++zOrig, ++nOrig ){ |
| 336 | + if( 0==(*zOrig & 0x80) ){ |
| 337 | + aOrig[(int)*zOrig] = 1; |
| 338 | + } |
| 339 | + } |
| 340 | + for( ; *zNew; ++zNew, ++nNew ){ |
| 341 | + if( 0==(*zNew & 0x80) && !aOrig[(int)*zNew] ){ |
| 342 | + return 1; |
| 343 | + } |
| 344 | + } |
| 345 | + return nOrig!=nNew; |
| 346 | + } |
| 347 | +} |
| 348 | + |
| 349 | +/* |
| 350 | +** COMMAND: test-user-caps-changed |
| 351 | +** |
| 352 | +** Usage: %fossil test-user-caps-changed caps1 caps2 |
| 353 | +** |
| 316 | 354 | */ |
| 317 | | -static int userHasNewCaps(const char *zOrig, const char *zNew){ |
| 318 | | - for( ; zNew && *zNew; ++zNew ){ |
| 319 | | - if( !zOrig || strchr(zOrig,*zNew)==0 ){ |
| 320 | | - return *zNew; |
| 321 | | - } |
| 322 | | - } |
| 323 | | - return 0; |
| 355 | +void test_user_caps_changed(void){ |
| 356 | + |
| 357 | + char const * zOld = g.argc>2 ? g.argv[2] : NULL; |
| 358 | + char const * zNew = g.argc>3 ? g.argv[3] : NULL; |
| 359 | + fossil_print("Has changes? = %d\n", |
| 360 | + userCapsChanged( zOld, zNew )); |
| 324 | 361 | } |
| 325 | 362 | |
| 326 | 363 | /* |
| 327 | 364 | ** Sends notification of user permission elevation changes to all |
| 328 | 365 | ** subscribers with a "u" subscription. This is a no-op if alerts are |
| | @@ -336,11 +373,11 @@ |
| 336 | 373 | ** edits their subscriptions after an admin assigns them this one, |
| 337 | 374 | ** this particular one will be lost. "Feature or bug?" is unclear, |
| 338 | 375 | ** but it would be odd for a non-admin to be assigned this |
| 339 | 376 | ** capability. |
| 340 | 377 | */ |
| 341 | | -static void alert_user_elevation(const char *zLogin, /*Affected user*/ |
| 378 | +static void alert_user_cap_change(const char *zLogin, /*Affected user*/ |
| 342 | 379 | int uid, /*[user].uid*/ |
| 343 | 380 | int bIsNew, /*true if new user*/ |
| 344 | 381 | const char *zOrigCaps,/*Old caps*/ |
| 345 | 382 | const char *zNewCaps /*New caps*/){ |
| 346 | 383 | Blob hdr, body; |
| | @@ -489,11 +526,11 @@ |
| 489 | 526 | }else if( !cgi_csrf_safe(2) ){ |
| 490 | 527 | /* This might be a cross-site request forgery, so ignore it */ |
| 491 | 528 | }else{ |
| 492 | 529 | /* We have all the information we need to make the change to the user */ |
| 493 | 530 | char c; |
| 494 | | - int bHasNewCaps = 0 /* 1 if user's permissions are increased */; |
| 531 | + int bCapsChanged = 0 /* 1 if user's permissions changed */; |
| 495 | 532 | const int bIsNew = uid<=0; |
| 496 | 533 | char aCap[70], zNm[4]; |
| 497 | 534 | zNm[0] = 'a'; |
| 498 | 535 | zNm[2] = 0; |
| 499 | 536 | for(i=0, c='a'; c<='z'; c++){ |
| | @@ -511,11 +548,11 @@ |
| 511 | 548 | a[c&0x7f] = P(zNm)!=0; |
| 512 | 549 | if( a[c&0x7f] ) aCap[i++] = c; |
| 513 | 550 | } |
| 514 | 551 | |
| 515 | 552 | aCap[i] = 0; |
| 516 | | - bHasNewCaps = bIsNew || userHasNewCaps(zOldCaps, &aCap[0]); |
| 553 | + bCapsChanged = bIsNew || userCapsChanged(zOldCaps, &aCap[0]); |
| 517 | 554 | zPw = P("pw"); |
| 518 | 555 | zLogin = P("login"); |
| 519 | 556 | if( strlen(zLogin)==0 ){ |
| 520 | 557 | const char *zRef = cgi_referer("setup_ulist"); |
| 521 | 558 | style_header("User Creation Error"); |
| | @@ -616,18 +653,20 @@ |
| 616 | 653 | @ <span class="loginError">%h(zErr)</span> |
| 617 | 654 | @ |
| 618 | 655 | @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)"> |
| 619 | 656 | @ [Bummer]</a></p> |
| 620 | 657 | style_finish_page(); |
| 621 | | - if( bHasNewCaps ){ |
| 622 | | - alert_user_elevation(zLogin, uid, bIsNew, zOldCaps, &aCap[0]); |
| 658 | + if( bCapsChanged ){ |
| 659 | + /* It's possible that caps were updated locally even if |
| 660 | + ** login group updates failed. */ |
| 661 | + alert_user_cap_change(zLogin, uid, bIsNew, zOldCaps, &aCap[0]); |
| 623 | 662 | } |
| 624 | 663 | return; |
| 625 | 664 | } |
| 626 | 665 | } |
| 627 | | - if( bHasNewCaps ){ |
| 628 | | - alert_user_elevation(zLogin, uid, bIsNew, zOldCaps, &aCap[0]); |
| 666 | + if( bCapsChanged ){ |
| 667 | + alert_user_cap_change(zLogin, uid, bIsNew, zOldCaps, &aCap[0]); |
| 629 | 668 | } |
| 630 | 669 | cgi_redirect(cgi_referer("setup_ulist")); |
| 631 | 670 | return; |
| 632 | 671 | } |
| 633 | 672 | |
| 634 | 673 | |