bb473e2c57
* feat: add read/write scopes to API keys API keys now require explicit scopes (e.g. transactions:read, invoices:write) instead of having implicit full access. The create dialog shows grouped checkboxes per domain with read/write split. Legacy keys with null scopes default to read-only. MCP tools/list is filtered by scope and tools/call rejects unauthorized calls. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Enhance API key scopes with suppliers and update descriptions for better clarity * fix: drop function before recreating with changed return type PostgreSQL cannot change return type via CREATE OR REPLACE. Drop the existing function first to avoid SQLSTATE 42P13. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add new migration to drop and recreate function with scopes return type The original migration was already applied, so a new migration is needed to DROP the function first before recreating with the updated return type. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add DROP FUNCTION to original migration, remove redundant fix migration Preview branches replay all migrations from scratch. The original migration must DROP the function before recreating it with a changed return type, otherwise PostgreSQL rejects the CREATE OR REPLACE. The separate fix migration is no longer needed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: rename migration to avoid duplicate version in schema_migrations Version 20260325120000 is already recorded in the preview DB from a prior failed apply. Renaming to 20260326130000 so Supabase treats it as a new migration. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
53 lines
1.6 KiB
PL/PgSQL
53 lines
1.6 KiB
PL/PgSQL
-- Drop and recreate validate_and_increment_api_key to add scopes to return type
|
|
-- (CREATE OR REPLACE cannot change return types in PostgreSQL)
|
|
DROP FUNCTION IF EXISTS public.validate_and_increment_api_key(text);
|
|
|
|
CREATE FUNCTION public.validate_and_increment_api_key(p_key_hash text)
|
|
RETURNS TABLE(user_id uuid, rate_limited boolean, scopes text[])
|
|
LANGUAGE plpgsql SECURITY DEFINER AS $$
|
|
DECLARE
|
|
v_user_id uuid;
|
|
v_rate_limit_rpm integer;
|
|
v_request_count integer;
|
|
v_window_start timestamptz;
|
|
v_scopes text[];
|
|
BEGIN
|
|
-- Lock row for atomic update
|
|
SELECT ak.user_id, ak.rate_limit_rpm, ak.request_count, ak.rate_limit_window_start, ak.scopes
|
|
INTO v_user_id, v_rate_limit_rpm, v_request_count, v_window_start, v_scopes
|
|
FROM public.api_keys ak
|
|
WHERE ak.key_hash = p_key_hash AND ak.revoked_at IS NULL
|
|
FOR UPDATE;
|
|
|
|
IF v_user_id IS NULL THEN
|
|
RETURN;
|
|
END IF;
|
|
|
|
-- Reset window if expired (> 1 minute old)
|
|
IF v_window_start IS NULL OR v_window_start < now() - interval '1 minute' THEN
|
|
UPDATE public.api_keys
|
|
SET request_count = 1,
|
|
rate_limit_window_start = now(),
|
|
last_used_at = now()
|
|
WHERE key_hash = p_key_hash;
|
|
|
|
RETURN QUERY SELECT v_user_id, false, v_scopes;
|
|
RETURN;
|
|
END IF;
|
|
|
|
-- Check rate limit
|
|
IF v_request_count >= v_rate_limit_rpm THEN
|
|
RETURN QUERY SELECT v_user_id, true, v_scopes;
|
|
RETURN;
|
|
END IF;
|
|
|
|
-- Increment counter
|
|
UPDATE public.api_keys
|
|
SET request_count = request_count + 1,
|
|
last_used_at = now()
|
|
WHERE key_hash = p_key_hash;
|
|
|
|
RETURN QUERY SELECT v_user_id, false, v_scopes;
|
|
END;
|
|
$$;
|