Skip to content

fix(api): update database routing logic in MainRouter - #9080

Merged
AdriiiPRodri merged 6 commits into
masterfrom
PROWLER-310-read-replica-and-security-hub-invalid-router-problem
Oct 30, 2025
Merged

fix(api): update database routing logic in MainRouter#9080
AdriiiPRodri merged 6 commits into
masterfrom
PROWLER-310-read-replica-and-security-hub-invalid-router-problem

Conversation

@AdriiiPRodri

@AdriiiPRodri AdriiiPRodri commented Oct 29, 2025

Copy link
Copy Markdown
Contributor

Context

Security Hub exports were failing in production with Cannot assign "<Integration …>" and invalid input syntax for type uuid: "". The first came from reading the integration-provider relationship through the read replica; the second was Row Level Security blocking our integration.save() calls because the tenant context was only being set on the replica connection.

Description

  • Allow allow_relation to treat default, admin, and replica as compatible aliases so Django stops rejecting relationships built from replica reads.

  • Ensure every Security Hub write (regions update, connection flag) runs inside rls_transaction(..., using=MainRouter.default_db) before saving, so SET CONFIG(api.tenant_id, …) hits the same primary connection that persists the row.

Steps to review

  1. Pull the branch and reproduce a Security Hub export against a tenant with the integration enabled.
  2. Confirm logs show the integration completing without Cannot assign or invalid input syntax errors.
  3. Verify the integration record keeps the new regions map and connected status as expected.

Checklist

API

  • Verify if API specs need to be regenerated.
  • Check if version updates are required (e.g., specs, Poetry, etc.).
  • Ensure new entries are added to CHANGELOG.md, if applicable.

License

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@AdriiiPRodri
AdriiiPRodri requested a review from a team as a code owner October 29, 2025 17:05
@github-actions

github-actions Bot commented Oct 29, 2025

Copy link
Copy Markdown
Contributor

✅ All necessary CHANGELOG.md files have been updated.

@github-actions

github-actions Bot commented Oct 29, 2025

Copy link
Copy Markdown
Contributor

Conflict Markers Resolved

All conflict markers have been successfully resolved in this pull request.

Comment thread api/src/backend/api/db_router.py Fixed
@AdriiiPRodri

AdriiiPRodri commented Oct 29, 2025

Copy link
Copy Markdown
Contributor Author

Status update - Explanation

When the task blew up with invalid input syntax for type uuid: "", it wasn’t a bad value in our code, it was PostgreSQL’s row-level security telling us we’d lost track of the tenant context. Here’s what was going on:

We iterate findings inside with rls_transaction(tenant_id, using=READ_REPLICA_ALIAS). That context only touches the replica connection, so SET_CONFIG('api.tenant_id', ...) is executed on the replica pool. We do that on purpose to offload read traffic.

The moment we call integration.save(), Django’s router switches to the write alias (the primary). That connection never saw the SET_CONFIG, so current_setting('api.tenant_id') is empty and Postgres rejects the update when the RLS policy checks tenant_id = current_setting('api.tenant_id')::uuid.

Forcing the RLS transaction with using=MainRouter.default_db before each write (saving the regions, toggling connected, etc.) makes sure set_config happens on the same connection that performs the UPDATE. The RLS policy then sees the right tenant and allows the write.

The other piece was in api/db_router.py. Before the change, allow_relation only blessed {default, admin}. When we read IntegrationProviderRelationship from the replica but had the Integration instance from the primary, the router blocked the relationship and Django raised Cannot assign "<Integration ...>". Adding self.replica_db to the allowed set tells Django, "yes, these aliases all point at the same data, it’s fine to relate objects fetched from default/admin/replica even though only default/admin accept writes." That unblocked the lookup so the Security Hub code could actually run.

So the full fix is:

Let the router consider replica a safe partner for relations (reads still stay readonly, writes still go through the primary).
Whenever we mutate the Integration row—like refreshing the regions map or flipping connected, wrap the call in rls_transaction(tenant_id, using=MainRouter.default_db) so we reapply the tenant context on the primary connection before saving.
With those two tweaks the job no longer trips over the router, and RLS works fine

@AdriiiPRodri
AdriiiPRodri force-pushed the PROWLER-310-read-replica-and-security-hub-invalid-router-problem branch from 7d1de5a to a6ed144 Compare October 29, 2025 17:11
@AdriiiPRodri AdriiiPRodri added the backport-to-v5.13 Backport PR to the v5.13 branch label Oct 29, 2025
@AdriiiPRodri
AdriiiPRodri force-pushed the PROWLER-310-read-replica-and-security-hub-invalid-router-problem branch 2 times, most recently from ee52693 to 2c8e564 Compare October 29, 2025 17:15
@AdriiiPRodri
AdriiiPRodri force-pushed the PROWLER-310-read-replica-and-security-hub-invalid-router-problem branch from 2c8e564 to 2b992ab Compare October 29, 2025 17:16
josema-xyz
josema-xyz previously approved these changes Oct 30, 2025

@josema-xyz josema-xyz left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After reading some Django docs... 🚀

vicferpoy
vicferpoy previously approved these changes Oct 30, 2025
@github-actions

github-actions Bot commented Oct 30, 2025

Copy link
Copy Markdown
Contributor

🔒 Container Security Scan

Image: prowler-api:67f926c
Last scan: 2025-10-30 11:24:03 UTC

📊 Vulnerability Summary

Severity Count
🔴 Critical 4
Total 4

3 package(s) affected

⚠️ Action Required

Critical severity vulnerabilities detected. These should be addressed before merging:

  • Review the detailed scan results
  • Update affected packages to patched versions
  • Consider using a different base image if updates are unavailable

📋 Resources:

@AdriiiPRodri
AdriiiPRodri force-pushed the PROWLER-310-read-replica-and-security-hub-invalid-router-problem branch from e9f930e to 37cdd11 Compare October 30, 2025 11:20
@codecov

codecov Bot commented Oct 30, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.33333% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.98%. Comparing base (885ee62) to head (37cdd11).
⚠️ Report is 21 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #9080      +/-   ##
==========================================
+ Coverage   88.50%   93.98%   +5.47%     
==========================================
  Files          93      141      +48     
  Lines        3524    16962   +13438     
==========================================
+ Hits         3119    15941   +12822     
- Misses        405     1021     +616     
Flag Coverage Δ
api 93.98% <83.33%> (?)
prowler-py3.10-m365 ?
prowler-py3.11-m365 ?
prowler-py3.12-m365 ?
prowler-py3.9-m365 ?

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

Components Coverage Δ
prowler ∅ <ø> (∅)
api 93.98% <84.61%> (∅)
🚀 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.

@AdriiiPRodri
AdriiiPRodri merged commit aa8be0b into master Oct 30, 2025
14 of 15 checks passed
@AdriiiPRodri
AdriiiPRodri deleted the PROWLER-310-read-replica-and-security-hub-invalid-router-problem branch October 30, 2025 11:30
@prowler-bot prowler-bot added the was-backported The PR was successfully backported to the target branch label Oct 30, 2025
@prowler-bot

Copy link
Copy Markdown
Collaborator

💚 All backports created successfully

Status Branch Result
v5.13

Questions ?

Please refer to the Backport tool documentation and see the Github Action logs for details

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-to-v5.13 Backport PR to the v5.13 branch component/api was-backported The PR was successfully backported to the target branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants