mirror of
https://github.com/ether/etherpad-lite.git
synced 2026-07-21 00:59:11 +00:00
* feat(pad): suppress deletion token for durable identities; relabel recovery action (#7926) Builds on the allowPadDeletionByAllUsers suppression with the rest of the ideas discussed on the issue. Server (handleClientReady): - A creator's deletion token is now also withheld when they have a *durable* identity: authenticated (req.session.user with a username) AND the deployment pins that identity to a stable authorID via a getAuthorId hook. Only then does the creator path (author === revision-0 author) survive a cookie clear or a different device, making the recovery token redundant. - This deliberately tightens the previous `requireAuthentication => always suppress` rule: without a getAuthorId hook the authorID still comes from the per-browser token cookie, so an authenticated user on a second device is NOT the creator. Withholding the token there would strand them, so they now keep getting one. SSO deployments using the documented getAuthorId pattern get the clean no-modal experience. - New `canDeleteWithoutToken` clientVar (allowPadDeletionByAllUsers OR durable identity) drives the client label. Client (pad_editor.ts): - When canDeleteWithoutToken, the recovery-token disclosure summary is labelled plainly "Delete Pad" (reusing the already-translated pad.settings.deletePad key) instead of the jargon "Delete with token". Tests (socketio.ts): anonymous -> token; allowPadDeletionByAllUsers -> none; authenticated without a getAuthorId hook -> token; authenticated with one -> none. Verified in a browser for both label/modal outcomes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(pad): render recovery disclosure for all sessions; hide it (not relabel) when no token is needed Addresses Qodo review on #7930: 1. Token UI fully suppressed when not needed (was: only the summary relabelled). When canDeleteWithoutToken (allowPadDeletionByAllUsers or a durable identity), pad_editor.ts now hides the whole #delete-pad-with-token disclosure — label, token field and submit — so no deletion-token wording remains. With the plain "Delete Pad" button present this is also the cleaner UX. 2. Recovery disclosure no longer gated on !requireAuthentication. Because a token can now be issued under requireAuthentication when the deployment lacks a durable getAuthorId mapping, the template must render the recovery form there too — otherwise an authenticated creator gets a token with no UI to enter it on another device. It is rendered hidden by default and shown by the client only when canDeleteWithoutToken is false. 3. API.createPad aligned: also returns null deletionToken under allowPadDeletionByAllUsers, matching the socket/UI path. Tests: deletePad.ts gains a createPad-under-allowPadDeletionByAllUsers case. Verified live in Chromium: allowAll -> disclosure hidden, no modal; default anonymous -> disclosure visible, modal shown. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6a5ad2d90d
commit
2ba72de1de
6 changed files with 106 additions and 14 deletions
|
|
@ -1298,15 +1298,31 @@ const handleClientReady = async (socket:any, message: ClientReadyMessage) => {
|
|||
// once. Readonly sessions never see it.
|
||||
const isCreator =
|
||||
!sessionInfo.readonly && sessionInfo.author === await pad.getRevisionAuthor(0);
|
||||
// Skip token issuance — and so the client never shows the "Save your pad
|
||||
// deletion token" modal (issue #7926) — when the token cannot help:
|
||||
// - requireAuthentication: every creator already has a stable identity, so
|
||||
// the cookie/identity path is sufficient.
|
||||
// The deletion token is a recovery handle for the one class of creator that
|
||||
// can otherwise lose the ability to delete their pad: a user whose creator
|
||||
// status lives only in a per-browser author-token cookie. It is pointless —
|
||||
// and the "Save your pad deletion token" modal only overwhelms users who
|
||||
// will never need it (issue #7926) — when either of these holds:
|
||||
//
|
||||
// - allowPadDeletionByAllUsers: anyone can delete the pad with no token at
|
||||
// all (see handlePadDelete's flagOk branch), so a recovery token is noise
|
||||
// and the modal only overwhelms users who will never need it.
|
||||
// all (see handlePadDelete's flagOk branch).
|
||||
// - the creator has a *durable* identity: authenticated (req.session.user
|
||||
// with a username) AND the deployment maps that identity to a stable
|
||||
// authorID via a getAuthorId hook. Only then does `isCreator`
|
||||
// (author === revision-0 author) survive a cookie clear or a different
|
||||
// device, so the creator path replaces the token on any device.
|
||||
//
|
||||
// Note we deliberately do NOT treat requireAuthentication alone as durable:
|
||||
// without a getAuthorId hook the authorID still comes from the per-browser
|
||||
// token cookie (AuthorManager.getAuthorId -> getAuthor4Token), so an
|
||||
// authenticated user on a second device is NOT the creator and would be
|
||||
// stranded if we also withheld the token. The getAuthorId hook is the
|
||||
// documented way (doc/api/hooks_server-side) to pin authorID to username.
|
||||
const hasGetAuthorIdHook = (plugins.hooks.getAuthorId || []).length > 0;
|
||||
const hasDurableIdentity = hasGetAuthorIdHook && !!(user && user.username);
|
||||
const canDeleteWithoutToken = settings.allowPadDeletionByAllUsers || hasDurableIdentity;
|
||||
const padDeletionToken =
|
||||
isCreator && !settings.requireAuthentication && !settings.allowPadDeletionByAllUsers
|
||||
isCreator && !canDeleteWithoutToken
|
||||
? await padDeletionManager.createDeletionTokenIfAbsent(sessionInfo.padId)
|
||||
: null;
|
||||
|
||||
|
|
@ -1325,6 +1341,11 @@ const handleClientReady = async (socket:any, message: ClientReadyMessage) => {
|
|||
enablePadWideSettings: settings.enablePadWideSettings,
|
||||
enablePluginPadOptions: settings.enablePluginPadOptions,
|
||||
padDeletionToken,
|
||||
// Drives the deletion-button label/visibility in pad settings: when the
|
||||
// user can already delete without a token the recovery-token disclosure is
|
||||
// redundant, so the client labels the action "Delete Pad" instead of
|
||||
// "Delete with token" (issue #7926). See showDeletionTokenModalIfPresent.
|
||||
canDeleteWithoutToken,
|
||||
// Allow-listed copy — settings.privacyBanner could carry extra nested
|
||||
// keys from a hand-edited settings.json; sending those by reference
|
||||
// would leak them to every browser. See getPublicPrivacyBanner().
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue