Skip to content

[14.0][IMP] currency_rate_update_wise: rename from transferwise + security hardening - #219

Open
klodr wants to merge 2 commits into
OCA:14.0from
klodr:14.0-imp-currency_rate_update_wise
Open

[14.0][IMP] currency_rate_update_wise: rename from transferwise + security hardening#219
klodr wants to merge 2 commits into
OCA:14.0from
klodr:14.0-imp-currency_rate_update_wise

Conversation

@klodr

@klodr klodr commented Jun 19, 2026

Copy link
Copy Markdown

currency_rate_update_wise (rename of currency_rate_update_transferwise)

TransferWise rebranded to Wise in 2021. This renames the module currency_rate_update_transferwisecurrency_rate_update_wise and the provider TransferWiseWise, with identical rate-fetching functionality.

  • New provider res_currency_rate_provider_Wise.py.
  • migrations/14.0.2.0.0/pre-migration.py migrates existing res.currency.rate.provider records (service TransferWiseWise) and the related ir.model.data, so existing configurations keep working after a plain module update.
  • Security hardening of the rate-fetch HTTP call: request timeout, query-param encoding, explicit handling of non-JSON responses, and the API key restricted to base.group_system.
  • Hermetic tests (HTTP mocked), i18n (es/fr/it) and README updated.

See the commit message for the full rationale (Wise vs ECB coverage, UUID→JWT token transition).

Migrations to 15.0–18.0 will follow in dedicated PRs.

@OCA-git-bot OCA-git-bot added mod:currency_rate_update_wise Module currency_rate_update_wise series:14.0 labels Jun 19, 2026
…urity hardening

Rename the provider after the TransferWise -> Wise rebrand (2021) and apply
security fixes from a code audit, on the module that was merged in 14.0 (OCA
PR OCA#174). Existing installations get the renamed, hardened module through a
plain module update (an OpenUpgrade pre-migration handles the rename).

Why keep and update this provider, as a worldwide complement to the ECB provider.
The ECB provider is the official European, EUR-centric daily reference; Wise is a
global rate source covering currencies and use cases ECB does not:

- Rate quality: Wise returns the interbank mid-market rate. Aligned with the ECB
  fixing on a working day, it tracks the ECB reference markedly closer than
  xe.com (mean deviation ~0.02% vs ~0.12%, closest to ECB on 25 of 29 currencies).
- Worldwide coverage: Wise quotes ~164 currencies against the ECB reference list
  of 29, including ones ECB omits (e.g. AED, COP). ECB stays the European
  reference; Wise extends accurate rates to the rest of the world.
- Stable JSON API: an authenticated REST endpoint, not HTML scraping like the
  xe.com provider (which breaks whenever the web page changes).
- Targeted requests: we fetch only the currency pairs actually configured,
  instead of downloading a whole rate table.
- History and granularity: the API serves past rates (by timestamp or date
  range) at day/hour/minute granularity, enabling backfill and corrections.

Rename:
- module currency_rate_update_transferwise -> currency_rate_update_wise
- field res.company.transferwise_api_key -> wise_api_key
- provider service value 'TransferWise' -> 'Wise' (label "Wise.com")
- API base URL api.transferwise.com -> api.wise.com (/v1/rates endpoint unchanged)
- OpenUpgrade pre-migration (update_module_names + rename_columns + UPDATE
  service) -> no data loss: token, provider records and rate history preserved

Security hardening (from audit):
- network via requests with a (10, 30)s timeout instead of urllib without any
  timeout: a cron/worker can no longer hang indefinitely on an unresponsive API
- API token restricted with groups="base.group_system" (model + settings + view)
  and shown with the password widget -> no longer readable by non-admin users
- query parameters passed through requests params= (proper encoding)
- explicit handling of non-JSON responses (UserError instead of a raw traceback)
- robust error detection: isinstance(data, dict) and data.get("error")
- ondelete fixed (key is the service value "Wise"; dropped the stray label key)
- token storage is an unlimited opaque string, so both the legacy UUID format
  and the newer JWT format Wise is migrating to are accepted without truncation
- legacy-token heads-up: Wise is migrating API access tokens from UUID to JWT,
  with production issuing JWT from the end-July-2026 transition. During the
  migration window (2026-08-01 to 2027-08-01) a warning is logged (at Odoo load
  time and on each scheduled update) when a Wise token still uses the UUID
  format, inviting regeneration in the Wise Developer Hub. No warning outside
  that window (before: UUID is the only format; after: migration is over) nor
  when no key is set. Ref:
  https://docs.wise.com/guides/developer/auth-and-security/client-credentials-token-migration
- tests made hermetic (HTTP mocked, no real network call) + error-response and
  legacy-token-warning cases

Signed-off-by: Claude Perrin <klodr@users.noreply.github.com>
@klodr

klodr commented Jun 20, 2026

Copy link
Copy Markdown
Author

@OCA/currency-maintainers when you have a moment, could you please review? This renames currency_rate_update_transferwisecurrency_rate_update_wise (TransferWise → Wise rebrand) with an OpenUpgrade pre-migration and some security hardening. CI is green except the unrelated xe test and the pre-commit/pkg_resources infra issue, both addressed in #220 and #221. Thanks!

Wise answers HTTP 400 when the "from" and "to" query parameters are equal:

  GET /v1/rates?source=USD&target=EUR&from=2026-08-04&to=2026-08-04&group=day
  -> 400 Bad Request

That is exactly the shape of a scheduled run once the rates are up to
date: the provider then asks for the single missing day. The failure is
therefore intermittent — it only shows up when no more than one day is
missing, which is the steady state of a daily cron.

Widen the window by one day when it would collapse. The endpoint only
returns the days it actually has, so no spurious rate is created; asking
for a "to" one day ahead of today answers 200 with today's rate only.

The existing tests mock the HTTP layer and never inspect the outgoing
query, which is why this went unnoticed. Add a test that captures the
parameters and asserts the window is never zero-length.

Signed-off-by: Claude Perrin <klodr@users.noreply.github.com>
(cherry picked from commit bac73b5)
@klodr

klodr commented Aug 6, 2026

Copy link
Copy Markdown
Author

Note for reviewers: the red CI here is not caused by this PR

Both failures come from the 14.0 branch itself, and both already have a fix
open in this repository. Merging them first turns this PR green with no
change to its content.

pre-commitModuleNotFoundError: No module named 'pkg_resources'
Raised by the setuptools-odoo-make-default and
setuptools-odoo-get-requirements hooks: setuptools ≥ 81 dropped
pkg_resources. Fixed by #221, which bumps setuptools-odoo to 3.3.2.

test with OCB / test with OdooAssertionError: 0 != 1
Raised by currency_rate_update_xe, in test_cron and test_wizard. Those
tests query x-rates.com over the network and now get nothing back, so the
whole branch is red regardless of the module under review. Fixed by #220,
which sends a User-Agent, raises on HTTP errors and mocks the tests.

Suggested order: #221, then #220, then this one. Happy to rebase once they
land.

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

Labels

mod:currency_rate_update_wise Module currency_rate_update_wise series:14.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants