Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions packages/database/src/dbTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,30 @@ export type Database = {
},
]
}
secret_token: {
Row: {
creator: string
expiry_date: string | null
id: string
one_time_use: boolean | null
payload: Json
}
Insert: {
creator?: string
expiry_date?: string | null
id?: string
one_time_use?: boolean | null
payload: Json
}
Update: {
creator?: string
expiry_date?: string | null
id?: string
one_time_use?: boolean | null
payload?: Json
}
Relationships: []
}
Space: {
Row: {
id: number
Expand Down Expand Up @@ -1448,6 +1472,14 @@ export type Database = {
}
Returns: number
}
create_secret_token: {
Args: {
expiry_interval?: string
v_one_time_use?: boolean
v_payload: Json
}
Returns: string
}
document_in_space: { Args: { document_id: number }; Returns: boolean }
document_of_content: {
Args: { content: Database["public"]["Views"]["my_contents"]["Row"] }
Expand Down Expand Up @@ -1489,6 +1521,7 @@ export type Database = {
}
Returns: boolean
}
get_secret_token: { Args: { token: string }; Returns: Json }
get_space_anonymous_email: {
Args: {
platform: Database["public"]["Enums"]["Platform"]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
CREATE TABLE IF NOT EXISTS public.secret_token (
id varchar PRIMARY KEY DEFAULT encode(extensions.gen_random_bytes(12), 'base64'),
creator UUID NOT NULL DEFAULT auth.uid(),
payload JSONB NOT NULL,
expiry_date timestamp without time zone,
one_time_use boolean DEFAULT true
);

CREATE INDEX IF NOT EXISTS secret_token_expiry_idx ON public.secret_token (expiry_date) WHERE expiry_date IS NOT null;

CREATE OR REPLACE FUNCTION public.create_secret_token(v_payload JSONB, v_one_time_use BOOLEAN DEFAULT true, expiry_interval INTERVAL DEFAULT '30d') RETURNS VARCHAR
SECURITY DEFINER
SET search_path = ''
LANGUAGE sql AS $$
INSERT INTO public.secret_token (payload, expiry_date, one_time_use) VALUES (v_payload, now()+expiry_interval, v_one_time_use) RETURNING id;
$$;

CREATE OR REPLACE FUNCTION public.get_secret_token(token VARCHAR) RETURNS JSONB
SECURITY DEFINER
SET search_path = ''
LANGUAGE plpgsql AS $$
DECLARE
v_payload JSONB;
v_one_time_use BOOLEAN;
BEGIN
DELETE FROM public.secret_token WHERE expiry_date < now();
DELETE FROM public.secret_token WHERE id=token AND one_time_use = true RETURNING payload INTO v_payload;
IF v_payload IS NULL THEN
SELECT payload INTO v_payload FROM public.secret_token WHERE id=token;
END IF;
RETURN v_payload;
END;
$$;

ALTER TABLE secret_token OWNER TO "postgres";

REVOKE ALL ON TABLE public.secret_token FROM anon;
GRANT ALL ON TABLE public.secret_token TO authenticated;
GRANT ALL ON TABLE public.secret_token TO service_role;

ALTER TABLE public.secret_token ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS concept_policy ON public.secret_token;
DROP POLICY IF EXISTS concept_select_policy ON public.secret_token;
CREATE POLICY concept_select_policy ON public.secret_token FOR SELECT USING (creator = auth.uid());
DROP POLICY IF EXISTS concept_delete_policy ON public.secret_token;
CREATE POLICY concept_delete_policy ON public.secret_token FOR DELETE USING (creator = auth.uid());
DROP POLICY IF EXISTS concept_insert_policy ON public.secret_token;
-- Do not allow insert except through create_secret_token
DROP POLICY IF EXISTS concept_update_policy ON public.secret_token;
CREATE POLICY concept_update_policy ON public.secret_token FOR UPDATE USING (creator = auth.uid());
50 changes: 50 additions & 0 deletions packages/database/supabase/schemas/secret_tokens.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
CREATE TABLE IF NOT EXISTS public.secret_token (
id varchar PRIMARY KEY DEFAULT encode(extensions.gen_random_bytes(12), 'base64'),
creator UUID NOT NULL DEFAULT auth.uid(),
payload JSONB NOT NULL,
expiry_date timestamp without time zone,
one_time_use boolean DEFAULT true
);

CREATE INDEX IF NOT EXISTS secret_token_expiry_idx ON public.secret_token (expiry_date) WHERE expiry_date IS NOT null;

CREATE OR REPLACE FUNCTION public.create_secret_token(v_payload JSONB, v_one_time_use BOOLEAN DEFAULT true, expiry_interval INTERVAL DEFAULT '30d') RETURNS VARCHAR
SECURITY DEFINER
SET search_path = ''
LANGUAGE sql AS $$
INSERT INTO public.secret_token (payload, expiry_date, one_time_use) VALUES (v_payload, now()+expiry_interval, v_one_time_use) RETURNING id;
$$;

CREATE OR REPLACE FUNCTION public.get_secret_token(token VARCHAR) RETURNS JSONB
SECURITY DEFINER
SET search_path = ''
LANGUAGE plpgsql AS $$
DECLARE
v_payload JSONB;
v_one_time_use BOOLEAN;
BEGIN
DELETE FROM public.secret_token WHERE expiry_date < now();
DELETE FROM public.secret_token WHERE id=token AND one_time_use = true RETURNING payload INTO v_payload;
IF v_payload IS NULL THEN
SELECT payload INTO v_payload FROM public.secret_token WHERE id=token;
END IF;
RETURN v_payload;
END;
$$;

ALTER TABLE secret_token OWNER TO "postgres";

REVOKE ALL ON TABLE public.secret_token FROM anon;
GRANT ALL ON TABLE public.secret_token TO authenticated;
GRANT ALL ON TABLE public.secret_token TO service_role;

ALTER TABLE public.secret_token ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS concept_policy ON public.secret_token;
DROP POLICY IF EXISTS concept_select_policy ON public.secret_token;
CREATE POLICY concept_select_policy ON public.secret_token FOR SELECT USING (creator = auth.uid());
DROP POLICY IF EXISTS concept_delete_policy ON public.secret_token;
CREATE POLICY concept_delete_policy ON public.secret_token FOR DELETE USING (creator = auth.uid());
DROP POLICY IF EXISTS concept_insert_policy ON public.secret_token;
-- Do not allow insert except through create_secret_token
DROP POLICY IF EXISTS concept_update_policy ON public.secret_token;
CREATE POLICY concept_update_policy ON public.secret_token FOR UPDATE USING (creator = auth.uid());
Loading