Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,47 @@ dump won't tell you):
(`secret`, `password`, `token`, `api_key`, `dsn`, ...) in `odoo_db/db.py`.
The global `--include-sensitive-information` reveals values (no per-command
flag). Masking applies to `text` and `json`; `prometheus` emits only a count.
- `mail` audits outbound mail config (`get_mail_audit`). Ports a script that
checked the same data via the Odoo API (`odooly`) to direct SQL — none of
it needs auth. Dict keys are unordered; CLI `text` leads with
`config_parameters`, odoo-activity's TUI (`panes/mail.py`) instead leads
with `mail_servers` (see that repo's AGENTS.md/README). Top-level: 3 bool
flags — `is_neutralized` (`get_is_neutralized`),
`is_legacy_mail_config_configured` (`_is_legacy_mail_config_configured`:
was any of the 4 pre-v17 ICP mail keys ever set — permanently true once
it has been, migrated or not) and `is_alias_domain_migration_pending`
(`_is_alias_domain_migration_pending`: of those, is the leftover config
still live — true only when a legacy key is set **and** a company still
has no alias domain; this is the one that separates "migrated fine" from
"still stuck") — plus 5 sections: `config_parameters`
(`get_mail_config_parameters`), `alias_domains`
(`get_mail_alias_domains`, Odoo 17+ only, `None` pre-17), `addresses`
(`get_mail_addresses`), `mail_servers` (`get_mail_servers`, `ir.mail_server`
ordered by `sequence`, each row flagged `is_test_catcher`/
`known_production_relay`/`is_neutralization_stub` — see
`_is_test_mail_catcher`/`_known_production_relay`/
`_is_neutralization_stub_mail_server`), `modules`
(`get_mail_relevant_modules`, currently just `mass_mailing`).

`mail_servers[].smtp_user`/`smtp_pass` are masked (`_SECRET_MASK`) like any
other secret; `--include-sensitive-information` reveals both.
`addresses` (company/OdooBot/admin) are organizational mailboxes, not
individual PII, and are deliberately never masked — see `get_mail_addresses`.

CLI `text` output (not `get_mail_audit()` itself, which always returns the
full 6-key `config_parameters` list) drops the 4 legacy ICP keys once
`is_alias_domain_migration_pending` is `False` — see
`_relevant_mail_config_parameters`, called from `main.py`. `json` never
filters, so the two formats diverge on those 4 keys by design (see that
function's docstring for why: cross-version JSON diffing needs the full
list).

Rationale for individual checks (upstream commit hashes for
`smtp_authentication`/`from_filter`, the neutralization stub row, the
test-catcher marker/host lists incl. the `papercut` exclusion and the
Mailtrap sandbox-vs-live split, the known-production-relay table, the
demo-data default addresses) lives in the corresponding function
docstrings/comments in `db.py`, not here.
- `attachments` audits `ir.attachment` storage in pure SQL — no ORM, so it
sees field-backed rows (`image_1920`, logos, signatures) natively. The ORM's
`_search` auto-injects `res_field = False` and hides them; raw SQL has no
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ odoo-db [OPTIONS] COMMAND [DB]
| `crons <db>` | List active scheduled actions (`--all` also includes inactive ones). `--running` shows crons currently held by an Odoo worker (RowShareLock on `ir_cron`) — transient debug data, not bundled into `prepare-audit`. `--include-code` adds the python source of each `state='code'` cron (ignored with `--running`, which already always shows it) |
| `jobs <db>` | Queue job counts by state (requires `queue_job` module) |
| `params <db> [pattern]` | Show `ir_config_parameter` keys and values. Optional `pattern` narrows to keys containing it (case-insensitive substring). Values of secret-bearing keys are masked `********` by default; the global `--include-sensitive-information` reveals them |
| `mail <db>` | Audit outbound mail configuration: whether the database is neutralized (`database.is_neutralized`, the single most common reason mail never leaves an Odoo database — flagged up front, with Odoo's own inserted stub relay named in its own summary line rather than mistaken for a real one); the `ir_config_parameter` keys mail cares about (`mail.bounce.alias`, `mail.catchall.alias`/`.domain`, `mail.default.from`, plus Trobz's `default_email` and `mail.default.from_filter`); on Odoo 17+, the per-company `mail.alias.domain` records that actually control bounce/catchall/default-from routing now (the first 4 of those ICP keys became legacy in v17 and are hidden from the CLI's text output once they can no longer affect routing — shown side by side with a note otherwise, on a pre-17 database where they're the only mechanism or a 17+ one where a company still has no alias domain assigned while a legacy key still holds a value; Odoo never clears those keys even on a database that migrated fine, so a leftover value alone doesn't mean the migration is stuck — the JSON output always keeps the full list, since this tool's main job is comparing it across a v16-to-v19 migration; `mail.default.from_filter` stays live at runtime and is not part of that migration); company/system(OdooBot)/admin partner emails, resolved via `ir_model_data` so a renamed admin login or a deleted record still shows up (as `(record missing)`) instead of silently vanishing — shown as-is (organizational mailboxes, not individual PII), flagged if still at Odoo default (case-insensitively); outgoing `ir.mail_server` relays, named in a summary line (not a per-row column) when the name/host matches a known test-mail catcher like mailhog or a well-known managed relay like Google Workspace or Microsoft 365 — a positive confirmation, not just the absence of the other flag; and `mass_mailing` install state. Ported from an odooly/API-based check to direct SQL — none of it needs auth. SMTP username/password are masked by default; the global `--include-sensitive-information` reveals them |
| `users <db>` | List active users with connection status |
| `groups <db>` | List `res.groups` (category, name, share flag). `--include-users` adds each group's member logins. `--include-acls` adds per-group model access rights and record rules, plus top-level `global_acls`/`global_rules` for rows with no group at all (apply to every user — excluded from prior output, now the highest-value rows in a permission audit) |
| `roles <db>` | List `res.users.role` (requires OCA `base_user_role`; prints a message if not installed). `--include-users` adds currently-enabled assigned users' logins. `--include-groups` adds the role's full resolved group set (its own group plus all directly and transitively implied groups) |
Expand Down Expand Up @@ -88,6 +89,12 @@ odoo-db params my_db mail
# Reveal masked secret values (database.secret, api keys, ...)
odoo-db --include-sensitive-information params my_db

# Audit outbound mail configuration (config keys, key addresses, relays, mass_mailing)
odoo-db mail my_db

# Same, revealing masked addresses and SMTP passwords
odoo-db --include-sensitive-information mail my_db

# List access groups, with members and ACLs
odoo-db groups my_db --include-users --include-acls

Expand Down
Loading