NUAAwqy opened a new pull request, #1768:
URL: https://github.com/apache/casbin/pull/1768

   ## What
   
   `Model.UpdatePolicies` can lose rules from the model's duplicate-detection 
index.
   
   The function looks each old rule up in `PolicyMap` inside the same loop that 
overwrites
   `PolicyMap`. When a batch's new rules are a permutation of its old rules - 
"swap these two rows",
   "shift these three" - a later lookup finds the index that was just assigned 
to an earlier new
   rule. Two rows then point at one index, the row that was just written is 
overwritten again, and
   the key that was just added is deleted. The call still returns `true`.
   
   Reproduce with two policies:
   
   ```go
   // old: [alice, data1, read] [bob, data2, write]
   // new: [bob, data2, write]  [alice, data1, read]      // just swap them
   ok, _ := e.UpdatePolicies(old, new)
   ```
   
   Before:
   
   ```
   policy after UpdatePolicies: [["alice" "data1" "read"] ["bob" "data2" 
"write"]]   // the swap never happened
   PolicyMap has 1 entries but Policy has 2 rows
   GetPolicy reports ["bob" "data2" "write"] but HasPolicy says the model does 
not have it
   ```
   
   so the rule is listed by `GetPolicy` yet `HasPolicy` and `RemovePolicy` can 
no longer reach it, and
   it cannot be deleted from the model.
   
   After: the slice holds the swapped rules, `PolicyMap` has exactly one key 
per row, and
   `HasPolicy` / `RemovePolicy` / `UpdatePolicy` agree with `GetPolicy`.
   
   ## Fix
   
   Resolve and de-duplicate every index for the whole batch first, then write. 
Old keys are all
   deleted before any new key is inserted, because in a permuted batch a new 
rule is still keyed as an
   old rule. An unknown or repeated old rule returns `false` before the first 
write, so the model is
   never left half-updated - which is what `89584f4` ("feat: implement atomic 
UpdatePolicies() API")
   intended and what the old `rollbackFlag`/`defer` bookkeeping was trying, 
unsuccessfully, to do.
   
   `UpdatePolicy` (singular) is untouched: it does a single lookup before 
writing and is not affected
   by this.
   
   ## Test
   
   `model/policy_test.go` (new, table-driven) keeps `PolicyMap` and the policy 
slice in step for a
   two-rule swap, a three-rule rotation, an ordinary batch of unrelated rules, 
an unknown old rule and
   a repeated old rule. The first two fail on `master` and pass here; the last 
two pin the
   "nothing is modified when the batch is rejected" behaviour.
   
   - `go test -race ./...`: 8 packages ok
   - `gofmt -l .`: empty
   - `golangci-lint run` (v2.15.0, as `.golangci.yml` requires): 0 issues
   
   ## Notes
   
   - No existing test covered a permuted batch: `management_api_test.go:273` is 
the only
     `UpdatePolicies` call and its new rules share no key with the surviving 
rows, so it passes
     either way and is unchanged.
   - I have not run this through an external `UpdatableAdapter`/`Watcher`, so 
the storage side of
     `UpdatePolicies` is untested here; the change is confined to the model.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to