154 lines
5.1 KiB
PL/PgSQL
154 lines
5.1 KiB
PL/PgSQL
-- Collapse the identity-sensitive auth.bindTempAuthKey write boundary into one
|
|
-- database call. The caller still owns the surrounding transaction/savepoint;
|
|
-- this function preserves the global identity -> raw temp -> permanent row
|
|
-- lock order shared with selector advance, revocation and direct deletion.
|
|
|
|
CREATE OR REPLACE FUNCTION public.telesrv_bind_temp_auth_key(
|
|
p_temp_auth_key_id bigint,
|
|
p_perm_auth_key_id bigint,
|
|
p_nonce bigint,
|
|
p_temp_session_id bigint,
|
|
p_expires_at integer,
|
|
p_encrypted_message bytea
|
|
)
|
|
RETURNS TABLE (
|
|
bind_status text,
|
|
merged_layer integer,
|
|
merged_observation_id bigint
|
|
)
|
|
LANGUAGE plpgsql
|
|
AS $$
|
|
DECLARE
|
|
v_temp_expiry integer;
|
|
v_temp_layer integer;
|
|
v_temp_observation_id bigint;
|
|
v_current_perm_id bigint;
|
|
v_perm_expiry integer;
|
|
v_perm_layer integer;
|
|
v_perm_observation_id bigint;
|
|
v_merged_layer integer;
|
|
v_merged_observation_id bigint;
|
|
v_updated integer;
|
|
BEGIN
|
|
IF p_expires_at <= 0 OR p_temp_auth_key_id = p_perm_auth_key_id THEN
|
|
RETURN QUERY SELECT 'binding_invalid'::text, 0, 0::bigint;
|
|
RETURN;
|
|
END IF;
|
|
|
|
PERFORM pg_advisory_xact_lock(
|
|
1096111176::integer,
|
|
hashint8(p_perm_auth_key_id)::integer
|
|
);
|
|
|
|
SELECT key.expires_at, key.layer, key.layer_observation_id
|
|
INTO v_temp_expiry, v_temp_layer, v_temp_observation_id
|
|
FROM public.auth_keys AS key
|
|
WHERE key.auth_key_id = p_temp_auth_key_id
|
|
FOR UPDATE;
|
|
IF NOT FOUND OR v_temp_expiry <= 0 OR v_temp_expiry <> p_expires_at THEN
|
|
RETURN QUERY SELECT 'binding_invalid'::text, 0, 0::bigint;
|
|
RETURN;
|
|
END IF;
|
|
|
|
SELECT binding.perm_auth_key_id
|
|
INTO v_current_perm_id
|
|
FROM public.temp_auth_key_bindings AS binding
|
|
WHERE binding.temp_auth_key_id = p_temp_auth_key_id;
|
|
IF FOUND AND v_current_perm_id <> p_perm_auth_key_id THEN
|
|
RETURN QUERY SELECT 'already_bound'::text, 0, 0::bigint;
|
|
RETURN;
|
|
END IF;
|
|
|
|
SELECT key.expires_at, key.layer, key.layer_observation_id
|
|
INTO v_perm_expiry, v_perm_layer, v_perm_observation_id
|
|
FROM public.auth_keys AS key
|
|
WHERE key.auth_key_id = p_perm_auth_key_id
|
|
FOR UPDATE;
|
|
IF NOT FOUND OR v_perm_expiry <> 0 THEN
|
|
RETURN QUERY SELECT 'binding_invalid'::text, 0, 0::bigint;
|
|
RETURN;
|
|
END IF;
|
|
|
|
IF v_temp_layer < 0
|
|
OR v_perm_layer < 0
|
|
OR v_temp_observation_id < 0
|
|
OR v_perm_observation_id < 0
|
|
OR (v_temp_observation_id > 0 AND v_temp_layer = 0)
|
|
OR (v_perm_observation_id > 0 AND v_perm_layer = 0)
|
|
THEN
|
|
RETURN QUERY SELECT 'layer_invalid'::text, 0, 0::bigint;
|
|
RETURN;
|
|
END IF;
|
|
|
|
IF v_temp_observation_id > v_perm_observation_id THEN
|
|
v_merged_layer := v_temp_layer;
|
|
v_merged_observation_id := v_temp_observation_id;
|
|
ELSIF v_perm_observation_id > v_temp_observation_id THEN
|
|
v_merged_layer := v_perm_layer;
|
|
v_merged_observation_id := v_perm_observation_id;
|
|
ELSIF v_temp_observation_id > 0 AND v_temp_layer <> v_perm_layer THEN
|
|
RETURN QUERY SELECT 'layer_conflict'::text, 0, 0::bigint;
|
|
RETURN;
|
|
ELSIF v_temp_observation_id > 0 THEN
|
|
v_merged_layer := v_temp_layer;
|
|
v_merged_observation_id := v_temp_observation_id;
|
|
ELSE
|
|
v_merged_layer := v_perm_layer;
|
|
v_merged_observation_id := 0;
|
|
END IF;
|
|
|
|
INSERT INTO public.temp_auth_key_bindings (
|
|
temp_auth_key_id,
|
|
perm_auth_key_id,
|
|
nonce,
|
|
temp_session_id,
|
|
expires_at,
|
|
encrypted_message
|
|
) VALUES (
|
|
p_temp_auth_key_id,
|
|
p_perm_auth_key_id,
|
|
p_nonce,
|
|
p_temp_session_id,
|
|
p_expires_at,
|
|
p_encrypted_message
|
|
)
|
|
ON CONFLICT (temp_auth_key_id) DO UPDATE SET
|
|
nonce = EXCLUDED.nonce,
|
|
temp_session_id = EXCLUDED.temp_session_id,
|
|
expires_at = EXCLUDED.expires_at,
|
|
encrypted_message = EXCLUDED.encrypted_message,
|
|
created_at = now()
|
|
WHERE public.temp_auth_key_bindings.perm_auth_key_id = EXCLUDED.perm_auth_key_id;
|
|
GET DIAGNOSTICS v_updated = ROW_COUNT;
|
|
IF v_updated <> 1 THEN
|
|
RETURN QUERY SELECT 'binding_invalid'::text, 0, 0::bigint;
|
|
RETURN;
|
|
END IF;
|
|
|
|
UPDATE public.auth_keys AS key
|
|
SET layer = v_merged_layer,
|
|
layer_observation_id = v_merged_observation_id
|
|
WHERE key.auth_key_id = ANY(
|
|
ARRAY[p_temp_auth_key_id, p_perm_auth_key_id]::bigint[]
|
|
);
|
|
GET DIAGNOSTICS v_updated = ROW_COUNT;
|
|
IF v_updated <> 2 THEN
|
|
RAISE EXCEPTION
|
|
'merge bound auth key Layer defaults updated % of 2 locked keys',
|
|
v_updated
|
|
USING ERRCODE = '23000';
|
|
END IF;
|
|
|
|
UPDATE public.authorizations AS authz
|
|
SET layer = v_merged_layer
|
|
WHERE authz.auth_key_id = ANY(
|
|
ARRAY[p_temp_auth_key_id, p_perm_auth_key_id]::bigint[]
|
|
);
|
|
|
|
RETURN QUERY SELECT 'ok'::text, v_merged_layer, v_merged_observation_id;
|
|
END;
|
|
$$;
|
|
|
|
COMMENT ON FUNCTION public.telesrv_bind_temp_auth_key(
|
|
bigint, bigint, bigint, bigint, integer, bytea
|
|
) IS 'Atomically binds a temporary auth key and returns the committed Layer observation while preserving identity lock order';
|