Skip to content

Retention policy - #7

Merged
simonredfern merged 32 commits into
simonredfern:developfrom
OpenBankProject:develop
Nov 6, 2026
Merged

simonredfern merged 32 commits into
simonredfern:developfrom
OpenBankProject:develop

Conversation

@simonredfern

Copy link
Copy Markdown
Owner

No description provided.

simonredfern and others added 30 commits October 19, 2026 14:44
Various Sentinel found fixes 1
…ilure

The example of the dynamic change request response no longer carries JNothing as current_payload, so GET resource-docs/v7.0.0/swagger returns 200. When an example cannot be turned into a schema, the error now names the entity and field. A scenario in SwaggerDocsTest requests the v7.0.0 export.
fix: make the v7.0.0 swagger export succeed and name the entity on failure
Cascade-deleting an account or a bank must remove the account's own
counterparties with their bespoke fields, metadata, attributes and
limits, and must not touch counterparties of an account with the same
ACCOUNT_ID at another bank or of another account at the same bank.
DeleteAccountCascade now removes the counterparties the account created,
with their bespoke fields, attributes and limits, and the account's
counterparty metadata with its locations. Rows are selected by both
BANK_ID and ACCOUNT_ID, so another bank's account with the same
ACCOUNT_ID keeps its counterparties. Bank and product cascades use the
same code and get the same behaviour.
deleteCards looked the account up by ACCOUNT_ID alone, so deleting an
account also deleted the cards of an account with the same ACCOUNT_ID at
another bank. It now selects the account by BANK_ID and ACCOUNT_ID.
… BANK_ID too

deleteEntitlements found the account's users by ACCOUNT_ID alone, so the
users of an account with the same ACCOUNT_ID at another bank lost their
entitlements at the deleted account's bank. It now selects the account
access by BANK_ID and ACCOUNT_ID.
…ons change

DynamicEntityHelper rebuilds its definitions map from NewStyle.getDynamicEntities,
which keeps its list in Redis for dynamicEntity.cache.ttl.seconds. forgetDefinitions
now also deletes the memoized getDynamicEntities lists, once the request's
transaction has committed, so the rebuilt map reads the current rows and a new
entity's record endpoints answer immediately instead of after the map's
time-to-live. This node's map is still dropped at once for reads later in the
same request.
…for definitions

A definition change now bumps a new dynamic_entity cache namespace once its
transaction has committed, instead of deleting the memoized lists by pattern.
The lists NewStyle.getDynamicEntities and getDynamicEntitiesByUserId memoize
carry that version in their keys, and DynamicEntityHelper keeps its
definitions map (DefinitionsMapCache) only while the version is the one it
was built under, so every instance reads the definitions again within a
second, and a read still in flight cannot store old rows under the new
version. The dynamic resource-doc caches are bumped in the same after-commit
step rather than inside the request transaction. The namespace is listed on
the cache info page and can be bumped through the cache namespace endpoint.
…REQUEST_ID answers

In mail test mode, CommonsEmailWrapper now keeps the mail it would have
sent (bounded, oldest first) and can take mail.test.mode.delay_millis per
send, the way an SMTP round trip does. Two scenarios create a consent
with an EMAIL and an IMPLICIT challenge and expect the challenge mail to
have been sent, once and to the right address, by the time the 201
arrives. Both fail today: the challenge is sent from a Future the request
does not wait for.
…EST_ID

The EMAIL, SMS and IMPLICIT challenges were started from a Future the
request did not wait for. The send's connector method-routing lookup then
ran while the request's database connection was being closed
(SQLException: Connection is closed), and the challenge was lost; on a
deployment only a minority of challenges arrived. The send is now part
of the request. What a failed send does is settled in the commits that
follow.
…TP sessions time out

A consent request that names no email address, answered with EMAIL, must
get the 400 the other Create Consent endpoints give, and leave no consent
behind. SMTP sessions must bound connect, read and write, since mail is
now sent inside requests. The new consent scenarios reuse the suite's
create-consent URLs.
… Create Consent endpoints do

Create Consent By CONSENT_REQUEST_ID now fails when the challenge cannot
be sent, like the v3.1.0, v5.1.0 and v6.0.0 Create Consent endpoints. A
consent request that names no email address (EMAIL), no phone number
(SMS), or a user without an address for the implicit SCA method
(IMPLICIT) gets its 400 before the consent is written, since a failed
request keeps what it already wrote. Consumer pairs that skip SCA need no
recipient, as before.
… in tests

Mail is sent inside requests, so an SMTP server that goes quiet must not
hold a request and its database connection. Sessions now set
mail.smtp.connectiontimeout, mail.smtp.timeout and mail.smtp.writetimeout
from mail.smtp.timeout.millis (default 10000). The in-memory record of
test-mode mail and its delay only apply when OBP runs its tests, so a
sandbox in mail test mode does not hold challenge codes in memory.
…ge cannot be sent

A VRP consent request without an email address, answered with EMAIL, must
be refused before its view, grant, counterparty and limit are written. An
SMS challenge that cannot be sent (no SMS provider configured) must leave
the consent rejected rather than INITIATED. The EMAIL-without-email
scenario now asserts the exact answer of the consent lookup, and only the
scenarios that check timing slow the test-mode send down.
…ten, and reject a consent whose challenge failed

The consumer, the skip-SCA decision and the challenge's method and
recipient are now worked out right after the consent request is read,
before a VRP consent request writes its view, grant, counterparty and
limit. A request refused there leaves nothing behind. The send uses that
one recipient, so the check and the send cannot disagree, and the
implicit SCA method is looked up once; an implicit method other than
EMAIL or SMS sends nothing and needs no address, as before. When the
send itself fails after the consent is written (an SMS provider that is
missing or refuses), the consent is set to REJECTED before the 400, so no
one is left with a consent they can never answer.
…d document it

The name no longer reads like Jakarta Mail's own mail.smtp.timeout, and the longer default leaves room for servers that delay their greeting.
…P mandate, and no VRP write precedes a TTL refusal

A send that throws before it builds its Future (unreadable SMTP settings
outside mail test mode) must still leave the consent rejected. A VRP
consent whose SMS cannot be sent must be rejected with its view and the
PSU's grant on it released. A VRP consent refused for its time to live
must be refused before its mandate is written. An unusable
mail.smtp.timeout_millis must fall back to the default, never to "wait
forever". The SMS scenarios are skipped where an SMS provider is
configured, so they never send a real message.
…k precedes the VRP writes

When the challenge cannot be sent, the consent is rejected and, like a
revoke, its VRP mandate is released (the PSU's grant on the view, the
counterparty limit, the view). The send is started inside its Future, so
a connector that throws before building its own is handled the same way,
and the caller always gets the send's error even if this clean-up fails.
The time-to-live check moves up with the other read-only checks, before a
VRP consent request writes anything. The ResourceDocs now say that
IMPLICIT sends the challenge the implicit SCA method names, that a failed
SMS uses up the consent request, and list GetConsentImplicitSCAError.
Jakarta Mail reads 0 as wait forever, and Lift reads an unparseable value as 0, so only a positive number of milliseconds is taken.
…rceDocs

The IMPLICIT and SMS descriptions of Create Consent By CONSENT_REQUEST_ID now describe the challenge as sent, and IMPLICIT lists GetConsentImplicitSCAError. Entries re-recorded with allowlist_helper.py; the parity check reports no drift and no stale entries.
…llenges and double-entry rows

Deleting an account by cascade now removes the transaction requests it
paid from, including ones without a transaction yet, the expected
challenge answers of those requests, and the double-entry book rows on
either side of the account. Bank cascade deletion inherits this, since
it deletes each account through DeleteAccountCascade.
…request

fix: send the consent challenge within Create Consent By CONSENT_REQUEST_ID
feat: cascade deletes remove the account's counterparties and stay within the account's bank
…-rebuild

fix(dynamic-entity): drop the memoized definition lists when definitions change
…into feature/cascade-delete-double-entry-and-challenges

# Conflicts:
#	obp-api/src/main/scala/deletion/DeleteAccountCascade.scala
…entry-and-challenges

feat: cascade account deletion to transaction requests, challenges and double-entry rows
Features Files, Assets and auth mode UserOrApplication -> UserXORApplication
@simonredfern
simonredfern merged commit 9afdf2c into simonredfern:develop Nov 6, 2026
12 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.

2 participants