Skip to content

Support inserting embargoed (Access Control-restricted) assets into the Image content block - #6

Open
greg-pene wants to merge 1 commit into
cloudinary:mainfrom
greg-pene:support-embargoed-assets-in-image-block
Open

greg-pene wants to merge 1 commit into
cloudinary:mainfrom
greg-pene:support-embargoed-assets-in-image-block

Conversation

@greg-pene

Copy link
Copy Markdown

Summary

Today, selecting a Cloudinary asset with an Access Control token rule always shows "This asset is restricted" in the Media Library Widget and blocks insertion into the Image content block — even when the asset is just embargoed (token-gated now, scheduled to become public automatically at a future start date) rather than permanently restricted.

This PR adds embargo support:

  • New preview-server/ — a small standalone Express service, the only place the account's Cloudinary Access Control Key ever lives (cloudinary-sfmc is a pure client-side app with no access to any Cloudinary secret). POST /api/embargo-preview-url re-verifies via the Admin API that an asset is genuinely token-restricted (never trusts the client's claim) and, if so, signs a short-lived preview URL.
  • isNotRestricted (mediaLibValidators.js) now calls this service before giving up, and resolves instead of rejecting when the asset is a confirmed embargo case — attaching the signed preview URL and access_control rule to the asset rather than blocking it.
  • The content block's on-canvas preview polls/renders against that signed URL, but the saved/sent HTML always uses the plain public URL — so the actual email works the moment the embargo lifts and never depends on the preview server or an expired token.
  • The field's own thumbnail (CldAssetSelector) shows the same signed preview plus a "Temporary preview only — embargoed…" warning banner, and re-requests a fresh signed URL on its own if the current one goes stale (e.g. reopening the block after the preview server's TTL has expired).

Scope: the Image content block only. Video-to-GIF's source-video restriction check is unchanged (see preview-server/README.md's "Known limitations").

Why resolve instead of reject

The existing isNotRestricted validator did an unauthenticated HEAD request against the asset's plain delivery URL and treated any non-200 as "restricted" — which can't distinguish a genuinely embargoed asset from a permanently-restricted one, or even a transient network hiccup. The new flow asks a dedicated backend (which alone holds the Access Control Key) to confirm the specific reason via the Admin API, and only unblocks the ones that are actually time-bound embargoes.

New deployment requirement

preview-server needs to be deployed and configured (CLOUDINARY_ACCESS_CONTROL_KEY, CLOUDINARY_API_KEY/SECRET, ALLOWED_ORIGINS) alongside the existing cloudinary-sfmc app; see preview-server/README.md. Without it configured (previewServerUrl query param unset), behavior is unchanged from today — embargoed assets still show the existing "restricted" block.

Test plan

  • yarn test:unit at repo root — 45 passed
  • yarn test:lint — clean (0 errors)
  • cloudinary-sfmc: yarn test:unit — 34 passed; yarn build succeeds
  • preview-server: yarn test (Node's built-in test runner, mocked Cloudinary client) — 7 passed
  • Manually verified end-to-end against a real Cloudinary sandbox cloud with a genuinely token-restricted asset: selection no longer blocks, preview renders via the signed URL, banner shows, and a stale preview (after TTL expiry) self-heals on reload without user action

🤖 Generated with Claude Code

Today, an asset with a Cloudinary Access Control `token` rule always shows
"This asset is restricted" in the Media Library Widget and can't be
inserted at all — even when it's just embargoed (token now, becomes public
automatically at a scheduled date), not permanently restricted.

Adds a new standalone `preview-server` (the only place the account's
Access Control Key ever lives, since `cloudinary-sfmc` is a pure
client-side app with no access to any Cloudinary secret) that verifies an
asset is genuinely token-restricted via the Admin API and, if so, signs a
short-lived preview URL. `isNotRestricted` now tries that before giving up,
and resolves instead of rejecting when the asset is confirmed to be a real
embargo case, attaching the signed preview URL and access_control rule to
the asset.

The image content block then previews and polls against that signed URL
on-canvas, but the saved/sent HTML always uses the plain public URL — so
the actual email works the moment the embargo lifts and never depends on
the preview server or an expired token. The field's own thumbnail
(`CldAssetSelector`) shows the same signed preview plus a warning banner,
and re-requests a fresh signed URL on its own if the current one goes
stale (e.g. reopening the block after the preview server's TTL expired).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.

1 participant