161 lines
6.6 KiB
SQL
161 lines
6.6 KiB
SQL
-- Preserve the protocol key kind/lifetime established by p_q_inner_data(_temp).
|
|
-- A temporary key must expire at the MTProto edge; it must never fall through
|
|
-- to the RPC router and be mistaken for an independent permanent identity.
|
|
ALTER TABLE public.auth_keys
|
|
ADD COLUMN expires_at integer NOT NULL DEFAULT -1;
|
|
|
|
ALTER TABLE public.auth_keys
|
|
ADD CONSTRAINT auth_keys_expires_at_valid CHECK (expires_at >= -1);
|
|
|
|
-- auth_keys.expires_at is the only protocol-lifetime fact. Retention seeks this
|
|
-- partial index so unbound temporary handshakes and bound keys follow the same
|
|
-- bounded cleanup path; the binding-side expiry index remains only for legacy
|
|
-- rollback compatibility.
|
|
CREATE INDEX auth_keys_temporary_expiry_seek_idx
|
|
ON public.auth_keys (expires_at, auth_key_id)
|
|
WHERE expires_at > 0;
|
|
|
|
-- Existing bound temporary keys are unambiguous and can be backfilled from the
|
|
-- durable bind record. Unclassified historical keys remain -1 and are rejected
|
|
-- once with protocol -404 after restart, forcing a clean handshake instead of
|
|
-- guessing that they are permanent. New handshakes always write 0 (permanent)
|
|
-- or their positive absolute expiry before dh_gen_ok.
|
|
DO $$
|
|
BEGIN
|
|
IF EXISTS (
|
|
SELECT 1
|
|
FROM public.temp_auth_key_bindings
|
|
WHERE expires_at <= 0
|
|
) THEN
|
|
RAISE EXCEPTION 'invalid non-positive temporary auth key expiry; repair before migration 0086';
|
|
END IF;
|
|
IF EXISTS (
|
|
SELECT 1
|
|
FROM public.temp_auth_key_bindings AS b
|
|
LEFT JOIN public.auth_keys AS k ON k.auth_key_id = b.perm_auth_key_id
|
|
WHERE k.auth_key_id IS NULL
|
|
) THEN
|
|
RAISE EXCEPTION 'temporary auth key binding references missing permanent key; repair before migration 0086';
|
|
END IF;
|
|
IF EXISTS (
|
|
SELECT 1
|
|
FROM public.temp_auth_key_bindings
|
|
WHERE temp_auth_key_id = perm_auth_key_id
|
|
) THEN
|
|
RAISE EXCEPTION 'temporary auth key self-binding; repair before migration 0086';
|
|
END IF;
|
|
IF EXISTS (
|
|
SELECT 1
|
|
FROM public.temp_auth_key_bindings AS temp_role
|
|
JOIN public.temp_auth_key_bindings AS perm_role
|
|
ON perm_role.perm_auth_key_id = temp_role.temp_auth_key_id
|
|
) THEN
|
|
RAISE EXCEPTION 'auth key appears in both temporary and permanent roles; repair before migration 0086';
|
|
END IF;
|
|
END
|
|
$$;
|
|
|
|
UPDATE public.auth_keys AS k
|
|
SET expires_at = b.expires_at
|
|
FROM public.temp_auth_key_bindings AS b
|
|
WHERE k.auth_key_id = b.temp_auth_key_id;
|
|
|
|
-- Do not normalize an early telesrv bug during reads. If a deployment contains
|
|
-- an authorization written against a bound temp key, stop the migration and
|
|
-- require an explicit data repair after inspecting the corresponding perm key.
|
|
DO $$
|
|
BEGIN
|
|
IF EXISTS (
|
|
SELECT 1
|
|
FROM public.authorizations AS a
|
|
JOIN public.temp_auth_key_bindings AS b
|
|
ON b.temp_auth_key_id = a.auth_key_id
|
|
) THEN
|
|
RAISE EXCEPTION 'invalid authorization on temporary auth key; repair before migration 0086';
|
|
END IF;
|
|
END
|
|
$$;
|
|
|
|
-- Every durable table keyed by business/device auth identity must also be free
|
|
-- of bound temporary IDs. These tables intentionally do not FK to auth_keys
|
|
-- because several retain historical delivery facts; silently deleting the key
|
|
-- would therefore strand an identity split instead of repairing it. The
|
|
-- physical-session exclusion tuple in dispatch_outbox is deliberately omitted:
|
|
-- it stores raw auth_key_id + session_id and a temporary raw key is valid there.
|
|
DO $$
|
|
BEGIN
|
|
IF EXISTS (
|
|
SELECT 1 FROM public.update_states AS s
|
|
JOIN public.temp_auth_key_bindings AS b ON b.temp_auth_key_id = s.auth_key_id
|
|
) THEN
|
|
RAISE EXCEPTION 'update state references temporary auth key; repair before migration 0086';
|
|
END IF;
|
|
IF EXISTS (
|
|
SELECT 1 FROM public.bootstrap_update_jobs AS j
|
|
JOIN public.temp_auth_key_bindings AS b ON b.temp_auth_key_id = j.auth_key_id
|
|
) THEN
|
|
RAISE EXCEPTION 'bootstrap update job references temporary auth key; repair before migration 0086';
|
|
END IF;
|
|
IF EXISTS (
|
|
SELECT 1 FROM public.secret_qts_watermarks AS q
|
|
JOIN public.temp_auth_key_bindings AS b ON b.temp_auth_key_id = q.auth_key_id
|
|
) THEN
|
|
RAISE EXCEPTION 'secret qts watermark references temporary auth key; repair before migration 0086';
|
|
END IF;
|
|
IF EXISTS (
|
|
SELECT 1 FROM public.encrypted_message_queue AS q
|
|
JOIN public.temp_auth_key_bindings AS b ON b.temp_auth_key_id = q.receiver_auth_key_id
|
|
) THEN
|
|
RAISE EXCEPTION 'encrypted message queue references temporary auth key; repair before migration 0086';
|
|
END IF;
|
|
IF EXISTS (
|
|
SELECT 1 FROM public.encrypted_state_event_delivery AS d
|
|
JOIN public.temp_auth_key_bindings AS b ON b.temp_auth_key_id = d.auth_key_id
|
|
) THEN
|
|
RAISE EXCEPTION 'encrypted state delivery references temporary auth key; repair before migration 0086';
|
|
END IF;
|
|
IF EXISTS (
|
|
SELECT 1 FROM public.encrypted_state_events AS e
|
|
JOIN public.temp_auth_key_bindings AS b ON b.temp_auth_key_id = e.target_auth_key_id
|
|
) THEN
|
|
RAISE EXCEPTION 'encrypted state event targets temporary auth key; repair before migration 0086';
|
|
END IF;
|
|
IF EXISTS (
|
|
SELECT 1 FROM public.secret_chats AS c
|
|
JOIN public.temp_auth_key_bindings AS b
|
|
ON b.temp_auth_key_id = c.admin_auth_key_id
|
|
OR b.temp_auth_key_id = c.participant_auth_key_id
|
|
) THEN
|
|
RAISE EXCEPTION 'secret chat references temporary auth key; repair before migration 0086';
|
|
END IF;
|
|
END
|
|
$$;
|
|
|
|
-- An authorization or the permanent side of a temp binding proves that the key
|
|
-- is permanent. Logged-out, unreferenced legacy keys cannot be proven either
|
|
-- way and intentionally keep the -1 sentinel described above.
|
|
UPDATE public.auth_keys AS k
|
|
SET expires_at = 0
|
|
WHERE k.expires_at = -1
|
|
AND (
|
|
EXISTS (
|
|
SELECT 1
|
|
FROM public.authorizations AS a
|
|
WHERE a.auth_key_id = k.auth_key_id
|
|
)
|
|
OR EXISTS (
|
|
SELECT 1
|
|
FROM public.temp_auth_key_bindings AS b
|
|
WHERE b.perm_auth_key_id = k.auth_key_id
|
|
)
|
|
);
|
|
|
|
-- This FK is the durable serialization boundary between auth.bindTempAuthKey
|
|
-- and permanent-key revoke/destroy. RESTRICT makes a concurrent delete fail
|
|
-- closed; deletion paths remove referenced temp keys first and retry on the
|
|
-- narrow FK race, so no committed binding can ever point at a missing perm key.
|
|
ALTER TABLE public.temp_auth_key_bindings
|
|
ADD CONSTRAINT temp_auth_key_bindings_perm_auth_key_id_fkey
|
|
FOREIGN KEY (perm_auth_key_id)
|
|
REFERENCES public.auth_keys(auth_key_id)
|
|
ON DELETE RESTRICT;
|