This function returns an SDS string representing the specified user ACL * rules related to command execution, in the same format you could set them * back using ACL SETUSER. The function will return just the set of rules needed * to recreate the user commands bitmap, without including other user flags such * as on/off, passwords and so forth. The returned string always starts with * the +@all
| 471 | * the +@all or -@all rule, depending on the user bitmap, and is followed, if |
| 472 | * needed, by the other rules needed to narrow or extend what the user can do. */ |
| 473 | sds ACLDescribeUserCommandRules(user *u) { |
| 474 | sds rules = sdsempty(); |
| 475 | int additive; /* If true we start from -@all and add, otherwise if |
| 476 | false we start from +@all and remove. */ |
| 477 | |
| 478 | /* This code is based on a trick: as we generate the rules, we apply |
| 479 | * them to a fake user, so that as we go we still know what are the |
| 480 | * bit differences we should try to address by emitting more rules. */ |
| 481 | user fu = {0}; |
| 482 | user *fakeuser = &fu; |
| 483 | |
| 484 | /* Here we want to understand if we should start with +@all and remove |
| 485 | * the commands corresponding to the bits that are not set in the user |
| 486 | * commands bitmap, or the contrary. Note that semantically the two are |
| 487 | * different. For instance starting with +@all and subtracting, the user |
| 488 | * will be able to execute future commands, while -@all and adding will just |
| 489 | * allow the user the run the selected commands and/or categories. |
| 490 | * How do we test for that? We use the trick of a reserved command ID bit |
| 491 | * that is set only by +@all (and its alias "allcommands"). */ |
| 492 | if (ACLUserCanExecuteFutureCommands(u)) { |
| 493 | additive = 0; |
| 494 | rules = sdscat(rules,"+@all "); |
| 495 | ACLSetUser(fakeuser,"+@all",-1); |
| 496 | } else { |
| 497 | additive = 1; |
| 498 | rules = sdscat(rules,"-@all "); |
| 499 | ACLSetUser(fakeuser,"-@all",-1); |
| 500 | } |
| 501 | |
| 502 | /* Attempt to find a good approximation for categories and commands |
| 503 | * based on the current bits used, by looping over the category list |
| 504 | * and applying the best fit each time. Often a set of categories will not |
| 505 | * perfectly match the set of commands into it, so at the end we do a |
| 506 | * final pass adding/removing the single commands needed to make the bitmap |
| 507 | * exactly match. A temp user is maintained to keep track of categories |
| 508 | * already applied. */ |
| 509 | user tu = {0}; |
| 510 | user *tempuser = &tu; |
| 511 | |
| 512 | /* Keep track of the categories that have been applied, to prevent |
| 513 | * applying them twice. */ |
| 514 | char applied[sizeof(ACLCommandCategories)/sizeof(ACLCommandCategories[0])]; |
| 515 | memset(applied, 0, sizeof(applied)); |
| 516 | |
| 517 | memcpy(tempuser->allowed_commands, |
| 518 | u->allowed_commands, |
| 519 | sizeof(u->allowed_commands)); |
| 520 | while (1) { |
| 521 | int best = -1; |
| 522 | unsigned long mindiff = INT_MAX, maxsame = 0; |
| 523 | for (int j = 0; ACLCommandCategories[j].flag != 0; j++) { |
| 524 | if (applied[j]) continue; |
| 525 | |
| 526 | unsigned long on, off, diff, same; |
| 527 | ACLCountCategoryBitsForUser(tempuser,&on,&off,ACLCommandCategories[j].name); |
| 528 | /* Check if the current category is the best this loop: |
| 529 | * * It has more commands in common with the user than commands |
| 530 | * that are different. |
no test coverage detected