Skip to content

feat(mail): add mail configuration audit command - #29

Merged
nilshamerlinck merged 1 commit into
mainfrom
feat/mail-audit
Aug 21, 2026
Merged

feat(mail): add mail configuration audit command#29
nilshamerlinck merged 1 commit into
mainfrom
feat/mail-audit

Conversation

@Kimkhoi3010

@Kimkhoi3010 Kimkhoi3010 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds odoo-db mail <db>: a direct-SQL audit of outbound mail configuration, porting a script that checked the same things through the Odoo API (odooly) — none of this data needs auth. Returns one bundle with five sections:

  • config_parameters — the ir_config_parameter keys mail cares about (mail.bounce.alias, mail.catchall.alias/.domain, mail.default.from/.from_filter, plus Trobz's default_email). Keys not set still get a row with value: None ("(not defined)"); a key present with an empty string is preserved as "", distinct from absent (verified against real data). None of these keys are masked.
  • alias_domains (Odoo 17+ only, None otherwise) — the per-company mail.alias.domain records that actually control bounce/catchall/default-from routing since v17; the legacy ICP keys above are read only by a one-time migration helper past that version (verified against a real Odoo 18 source tree). bounce_email/catchall_email/default_from_email are computed in SQL mirroring AliasDomain's own compute methods. A company with no alias domain assigned shows alias_domain: None — a worse, distinct misconfiguration from "still on ICP defaults". Filters res_company.active = true (an archived company isn't sending real mail).
  • addresses — company partner (id 1), __system__/OdooBot, and admin emails, each flagged is_default if still an Odoo/demo-data default. Raw SQL has no implicit active=True filter, so archived records surface here rather than silently vanishing (unlike the original ORM-based script). Shown as-is, not masked — organizational mailboxes, not individual PII (per reviewer feedback, reversed from an earlier default-masking design).
  • mail_serversir.mail_server, ordered by sequence. smtp_authentication/from_filter (Odoo 17+ columns) are probed via to_regclass/pg_attribute and NULL on older versions. smtp_user/smtp_pass are real credentials and stay masked by default (per reviewer call, username included since it may itself be a real mailbox address) — reuses the existing global --include-sensitive-information rather than adding a per-command flag.
  • modules — install state of modules that materially change mail behavior (currently just mass_mailing).

Text output prints each section as its own table, with a note explaining the legacy-vs-authoritative relationship between config_parameters and alias_domains when the latter is present. Prometheus output adds odoo_db_mail_servers, odoo_db_mail_default_addresses, and (when alias_domains applies) odoo_db_mail_companies_missing_alias_domain.

Test plan

  • tests/test_smoke.pymail --help, get_mail_config_parameters (including absent-vs-blank key handling), get_mail_addresses (default-flagging, missing records, empty emails), get_mail_servers (masking, --include-sensitive-information, v17+ column detection)
  • AGENTS.md, README.md, site-docs/docs/cli-reference.md updated

@Kimkhoi3010
Kimkhoi3010 marked this pull request as draft August 17, 2026 09:51
@Kimkhoi3010
Kimkhoi3010 force-pushed the feat/mail-audit branch 6 times, most recently from c3b2d84 to 816643c Compare August 18, 2026 08:21
@Kimkhoi3010
Kimkhoi3010 marked this pull request as ready for review August 18, 2026 08:21
@Kimkhoi3010
Kimkhoi3010 force-pushed the feat/mail-audit branch 5 times, most recently from 7ff8309 to 72080b8 Compare August 19, 2026 10:38
Comment thread odoo_db/db.py
Comment thread odoo_db/main.py Outdated
Comment thread odoo_db/main.py Outdated

@xaviedoanhduy xaviedoanhduy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

requesting changes on three things that i think are wrong rather than merely improvable, all reproduced against real databases:

  1. login = 'admin' silently drops the admin row when that login was renamed, which is the normal state on odoo.sh. verified on a production copy of ours: no admin line in the output, and nothing saying it was looked for and not found.
  2. the known-relay port constraints contradict Google's own documentation, so smtp.gmail.com:587 with STARTTLS, the most common Odoo setup there is, is not recognised. a miss on a positive-confirmation flag reads as "not a known relay", so the audit quietly withholds the thing the feature exists to say.
  3. the audit says nothing about a neutralized database, which is the most frequent reason mail never leaves a staging instance (that one is in an earlier comment thread, still open).

everything else below is smaller: case-sensitive is_default, substring matching that flags lookalike domains, a missing Mailtrap, a version claim that is off by two, a summary line that does not name the server it is talking about, and an AGENTS.md entry carrying review history.

the SQL underneath is solid and i want to be clear about that: the masking, the to_regclass probes and the "raw SQL sees past the ORM's active=True" reasoning all hold up against v16 through v19. most comments carry a suggestion you can apply directly, and three earlier threads (neutralization, the archived-row gauges, the alias-domain gauge) are still open above.

Comment thread odoo_db/db.py Outdated
Comment thread odoo_db/db.py Outdated
Comment thread odoo_db/db.py Outdated
Comment thread odoo_db/db.py Outdated
Comment thread odoo_db/db.py Outdated
Comment thread odoo_db/main.py
Comment thread odoo_db/main.py Outdated
Comment thread odoo_db/main.py Outdated
Comment thread tests/test_smoke.py Outdated
Comment thread AGENTS.md Outdated
@Kimkhoi3010

Copy link
Copy Markdown
Contributor Author

requesting changes on three things that i think are wrong rather than merely improvable, all reproduced against real databases:

  1. login = 'admin' silently drops the admin row when that login was renamed, which is the normal state on odoo.sh. verified on a production copy of ours: no admin line in the output, and nothing saying it was looked for and not found.
  2. the known-relay port constraints contradict Google's own documentation, so smtp.gmail.com:587 with STARTTLS, the most common Odoo setup there is, is not recognised. a miss on a positive-confirmation flag reads as "not a known relay", so the audit quietly withholds the thing the feature exists to say.
  3. the audit says nothing about a neutralized database, which is the most frequent reason mail never leaves a staging instance (that one is in an earlier comment thread, still open).

everything else below is smaller: case-sensitive is_default, substring matching that flags lookalike domains, a missing Mailtrap, a version claim that is off by two, a summary line that does not name the server it is talking about, and an AGENTS.md entry carrying review history.

the SQL underneath is solid and i want to be clear about that: the masking, the to_regclass probes and the "raw SQL sees past the ORM's active=True" reasoning all hold up against v16 through v19. most comments carry a suggestion you can apply directly, and three earlier threads (neutralization, the archived-row gauges, the alias-domain gauge) are still open above.

Hi @xaviedoanhduy,
Thanks for your time. I have updated the PR.

@nilshamerlinck

nilshamerlinck commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

2 inputs:

  • for legacy config parameters: if they are really legacy and not used, let's only check for them in relevant dbs (avoid confusing the user/tpm that is trying to debug mail settings)
  • for the dummy smtp server introduced by neutralize, worth flagging it explicitly as such indeed

Comment thread odoo_db/db.py Outdated
Comment thread tests/test_smoke.py Outdated
Comment thread tests/test_smoke.py Outdated
Comment thread odoo_db/db.py Outdated
Comment thread odoo_db/db.py Outdated
Comment thread odoo_db/main.py Outdated
@Kimkhoi3010

Copy link
Copy Markdown
Contributor Author

Thanks @nilshamerlinck, @xaviedoanhduy, I have updated the PR.

@xaviedoanhduy xaviedoanhduy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

thanks for the fast turnaround on this, everything from the last round is addressed and the client database names are out of the code now. a few things are still left over, each with a suggestion inline.

Comment thread odoo_db/db.py Outdated
Comment thread odoo_db/main.py Outdated
Comment thread odoo_db/db.py
Comment thread odoo_db/main.py Outdated
Comment thread odoo_db/db.py Outdated
Comment thread AGENTS.md Outdated
@Kimkhoi3010

Copy link
Copy Markdown
Contributor Author

Hi @xaviedoanhduy, I have updated the PR.

@xaviedoanhduy xaviedoanhduy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

verified against real v16/v17/v19 databases plus a scratch one shaped like an odoo.sh staging build: the renamed-admin lookup, the case-folded defaults, the neutralization flag and stub, the active-only gauges and the Google/M365 ports all behave as described. two non-blocking comments below.

Comment thread odoo_db/main.py
Comment thread AGENTS.md Outdated
@nilshamerlinck
nilshamerlinck merged commit 8ff8433 into main Aug 21, 2026
7 checks passed
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.

3 participants