Contribution Date
Contribution Project
Contribution Details
Add damrs_sync, and fix the data loss it exposed
A signed endpoint, a queue, and a worker that applies damrs events to the media
items referencing an asset. This closes the gap damrs_media left deliberately:
Drupal re-reads metadata only when a mapped field is empty or the source field
changed, so an asset retitled in the DAM would otherwise never update on the
site.
**Two correct modules destroyed data together.** damrs_media falls back to the
value already in a mapped field when damrs cannot answer, so an outage cannot
blank cached metadata. Refreshing works by clearing those fields so Drupal's own
"field is empty, read it" branch runs — which removes the very value the fallback
would have returned. A refresh event arriving during an outage therefore erased
the metadata it was meant to update. Both suites were green; the live check
showed a title going to NULL.
Fixing it needed a distinction the client could not express. asset() returns
NULL both for "damrs did not answer" and for "damrs says there is no such
asset", and for anything deciding whether to retry those are opposites:
retrying everything makes a deleted asset an item that never drains, and
retrying nothing makes a one-minute outage erase what it could not refresh. So
ApiResult carries the status, nothing is cleared until damrs has actually
produced the asset, an unreachable damrs suspends the queue run so the items
stay queued, and a deletion never asks at all.
**The verifier is pinned to forgeries, not to the happy path.** The endpoint has
no access requirement — damrs has no session and no form token — so the HMAC is
the entire boundary, and a verifier wrong in the accepting direction is an
endpoint anybody can post content changes to. That is a different severity from
the delivery tokens, where a mistake only stops images rendering. The vectors
carry the forgeries a plausible implementation accepts, including a correct
digest over the body *without* the timestamp: it passes every happy-path test
and makes every signature a permanent replay token.
Mutation-tested four ways. Signing the body alone, dropping the freshness
window and coercing the timestamp are all caught. Replacing hash_equals with
=== is not, and provably cannot be — only the timing differs — so that one is
guarded by a structural assertion which says outright what it is rather than
leaving a silent gap.
Driven end to end against the live endpoint with deliveries signed by the Rust
signer: valid is 202, and missing, wrong-secret, tampered-body and stale are
each 401. 17 kernel tests, 32 unit tests, phpcs clean.
Contribution Issue Link
Files count
0
Patches count
1