security(spp_change_request_v2): enforce manager authorization on CR apply (server-side) - #365
Draft
gonzalesedwin1123 wants to merge 5 commits into
Draft
security(spp_change_request_v2): enforce manager authorization on CR apply (server-side)#365gonzalesedwin1123 wants to merge 5 commits into
gonzalesedwin1123 wants to merge 5 commits into
Conversation
…ia sudo path A group_cr_user, on their OWN approved remove_member CR (owning it so the ownership record rule doesn't mask the check), can call action_apply() over RPC and drive the sudoed strategy to end membership. Test asserts AccessError + untouched membership (fails before the fix: 'AccessError not raised'), that a manager can apply, and that validator-driven auto-apply-on-approve still applies.
…apply action_apply() runs the apply strategy under sudo (writing spp.group.membership etc. that CR roles cannot), but the group_cr_manager restriction existed only on the review button. Odoo object methods are RPC-callable, so a group_cr_user could invoke action_apply() directly on an approved CR (one they own) and drive superuser membership writes via strategies like remove_member/transfer_member. Gate the public action_apply() with a server-side check (env.su or group_cr_manager) and move the apply mechanism to an internal, non-RPC _apply_change_request(). Auto-apply-on-approve (_on_approve) now calls the internal mechanism directly so validator-driven approvals still apply, and sudo/demo callers are unaffected.
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 19.0 #365 +/- ##
==========================================
- Coverage 74.28% 70.32% -3.97%
==========================================
Files 372 205 -167
Lines 25385 17892 -7493
==========================================
- Hits 18857 12582 -6275
+ Misses 6528 5310 -1218
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
… for CR apply authz README.rst / index.html regenerated from CI's pinned oca-gen output (19.0.3.0.4); test file reflowed to CI's ruff-format line length (local hook env differed).
Member
Author
|
Follow-up filed: #369 — |
…direct apply From staff review: document the manager-only boundary explicitly — a group_cr_validator (not a manager) calling action_apply directly is denied; validators cause an apply only by approving (auto-apply).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
change_request.py::action_apply()runs the apply strategy underself.sudo()(via_do_apply), so thestrategy executes as superuser and can write models CR roles cannot — notably
spp.group.membership,which is intentionally read-only for CR roles. The
group_cr_managerrestriction, however, lived only onthe review button (
stage_review_form.xml). Odoo object methods are callable over RPC, so buttongroups=is not a server-side authorization boundary.
group_cr_userhas write onspp.change.request, so a CR user could callaction_apply()directly over RPCon an approved change request they own and drive superuser membership writes (e.g.
remove_member,transfer_membercreate/end membership rows). Severity: high. (The report also notesapproval_stateisan unprotected field on the shared approval mixin — filed separately; see below.)
Fix
action_apply()server-side:env.su or has_group("…group_cr_manager"), elseAccessError._apply_change_request()(underscore methods are notcallable via
call_kw), so the authorization boundary lives on the public method._on_approve()(auto-apply-on-approve, default on) now calls_apply_change_request()directly — approvalis validator-gated and already authorizes the apply, so this does not break the primary flow where a
validator's approval auto-applies. Superuser/sudo callers (demo generators) are unaffected via
env.su._do_apply()'ssudo()is unchanged (membership writes are a system action by design); it is now onlyreachable post-gate or post-approval.
Tests
Test-first (red → green). New
tests/test_apply_authorization.py:test_cr_user_cannot_apply_over_rpc— agroup_cr_user, on their own approved remove_member CR (ownedso the CR ownership record rule doesn't mask the check), calling
action_apply()raisesAccessErrorandthe membership is untouched. Failed before the fix (
AssertionError: AccessError not raised— it appliedas superuser).
test_manager_can_apply— agroup_cr_managercan apply (regression).test_validator_cannot_apply_directly— agroup_cr_validatoris also blocked from callingaction_apply()directly (documents the manager-only boundary).test_auto_apply_on_approve_runs_for_non_manager_approver— a validator's_on_approvestill auto-applies(guards the split from breaking auto-apply).
./spp t spp_change_request_v2→ 0 failed of 338. No existing tests changed.Staff review
An adversarial staff review (verdict ship) confirmed
action_applyis the only ungated public path tothe sudo-apply sink (batch/conflict wizards reach apply only via approval-gated
action_approve), that_apply_change_requestis not RPC-callable, and that all existing apply tests pass via the superuser testcontext. Its one nit — asserting a validator is also blocked — was folded in. It also confirmed the deferred
approval_stateweakness (#369) does not enable an apply bypass here, since agroup_cr_usercannotself-approve through the workflow.
Scope / notes
change_request_service.apply()callsaction_apply()as the APIuser, so applying via the API now requires that user to hold
group_cr_manager(matches the security modelfor a manager-privileged action).
approval_stateon the sharedspp.approval.mixinhas no field-level protection,so
group_cr_usercanwrite({'approval_state': 'approved'})directly, bypassing the approval workflow.Cross-cutting (mixin used by cycle/entitlement/studio/drims/…); scoped out of this PR. The manager gate here
already closes the dangerous sudo-apply sink.
version
19.0.3.0.4dodges their.0.1/.0.2/.0.3, so only a manifest/HISTORY/README re-merge if onelands first.
README.rst/index.htmlto be regenerated from CI.