Skip to content

fix(change_request): run auto-apply through the public action_apply - #461

Open
kneckinator wants to merge 2 commits into
19.0from
fix/auto-apply-extension-point
Open

fix(change_request): run auto-apply through the public action_apply#461
kneckinator wants to merge 2 commits into
19.0from
fix/auto-apply-extension-point

Conversation

@kneckinator

Copy link
Copy Markdown
Contributor

action_apply is the extension point modules override to hang post-apply work off an apply. Auto-apply-on-approve stopped going through it, so those overrides silently stopped running.

What happened

#365 gated action_apply on the change-request manager role, because it runs the apply strategy under sudo() and is reachable over RPC. Auto-apply was then routed to the internal mechanism so the approver could be a validator rather than a manager:

# _on_approve
if self.request_type_id.auto_apply_on_approve:
    self._apply_change_request()

That solved the authorization problem but changed the contract. Any module overriding action_apply — the natural place to react to an apply — keeps working when a manager clicks Apply, and quietly does nothing when the same request is auto-applied on approval. No error, no log; only the side effects go missing.

The fix

The manager gate already exempts env.su, and says so:

"Superuser (sudo) callers and the auto-apply-on-approve path … are unaffected."

So auto-apply can use the public entry point:

-    self._apply_change_request()
+    self.sudo().action_apply()

sudo() sets superuser mode without changing the user, so applied_by_id still records the real approver — there is a test for that, since it would be an easy thing to get wrong.

What is unchanged

Both guarantees from #365 hold, and both already had tests that still pass:

  • a validator calling action_apply directly is still refused (test_validator_cannot_apply_directly);
  • approving still applies for a non-manager approver (test_auto_apply_on_approve_runs_for_non_manager_approver).

The authorization boundary for RPC callers is untouched: env.su is false there.

Trade-off

Downstream overrides now run with su=True. The apply itself already runs under sudo() via _do_apply, so this widens privilege only for post-apply side effects. The alternative — a separate _after_apply() hook called by both paths — separates authorization from extension more cleanly, but requires every consumer to move their override, which is the cost this change exists to avoid.

Tests

Two added: auto-apply goes through action_apply (spied), and the applying user is recorded as the approver rather than the superuser. Full spp_change_request_v2 suite: 397 tests, 0 failures.

Merge order

19.0.3.1.10 → 19.0.3.1.12, leaving 3.1.11 to #459. Independent of #459 and #454 in content; only the version and changelog would conflict, and only if merged out of order.

Requiring change-request manager rights to apply meant auto-apply-on-
approve was routed to the internal mechanism instead, so that the
approver could be a validator rather than a manager. But action_apply is
the extension point modules override to hang post-apply work off an
apply, and bypassing it left those overrides silently not running on
approval — no error, just missing side effects, which is the hardest
kind of regression to notice.

Auto-apply now calls action_apply under sudo(), which the manager gate
already exempts (env.su), so the authorization boundary is unchanged for
RPC callers. sudo() sets su without changing uid, so applied_by_id still
records the real approver — asserted by a test.

The two guarantees from the original change are kept and still covered:
a validator cannot call action_apply directly, and approving still
applies.
Comment thread spp_change_request_v2/models/change_request.py Fixed
@codecov

codecov Bot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 75.93%. Comparing base (380b045) to head (d4def25).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             19.0     #461      +/-   ##
==========================================
- Coverage   75.93%   75.93%   -0.01%     
==========================================
  Files         627      627              
  Lines       43000    42993       -7     
==========================================
- Hits        32654    32647       -7     
  Misses      10346    10346              
Flag Coverage Δ
spp_api_v2_change_request 66.53% <ø> (ø)
spp_base_common 91.07% <ø> (ø)
spp_change_request_v2 78.53% <100.00%> (ø)
spp_cr_type_assign_program 92.50% <ø> (ø)
spp_dci_demo 94.28% <ø> (ø)
spp_farmer_registry_cr 61.24% <ø> (ø)
spp_farmer_registry_demo 63.39% <ø> (ø)
spp_mis_demo_v2 70.38% <ø> (ø)
spp_programs 66.97% <ø> (ø)
spp_registry 87.79% <ø> (ø)
spp_security 69.56% <ø> (ø)
spp_starter_sp_mis 86.84% <ø> (-2.05%) ⬇️
spp_studio_change_requests 84.85% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
spp_change_request_v2/models/change_request.py 84.30% <100.00%> (ø)

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

The rationale is already in the comment above it — the manager gate
exempts env.su, and sudo() preserves uid so attribution is unaffected —
but semgrep's odoo-sudo-without-context needs the marker to see it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants