Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: supabase vault #1605

Merged
merged 3 commits into from
Nov 7, 2023
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
3 changes: 3 additions & 0 deletions backend/repository/external_api_secret/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .create_secret import create_secret
from .delete_secret import delete_secret
from .read_secret import read_secret
21 changes: 21 additions & 0 deletions backend/repository/external_api_secret/create_secret.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from uuid import UUID

from models import get_supabase_client
from utils import build_secret_unique_name


def create_secret(
user_id: UUID, brain_id: UUID, secret_name: str, secret_value
) -> UUID | None:
supabase_client = get_supabase_client()
response = supabase_client.rpc(
"insert_secret",
{
"name": build_secret_unique_name(
user_id=user_id, brain_id=brain_id, secret_name=secret_name
),
"secret": secret_value,
},
).execute()

return response.data
18 changes: 18 additions & 0 deletions backend/repository/external_api_secret/delete_secret.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from uuid import UUID

from models import get_supabase_client
from utils import build_secret_unique_name


def delete_secret(user_id: UUID, brain_id: UUID, secret_name: str) -> bool:
supabase_client = get_supabase_client()
response = supabase_client.rpc(
"delete_secret",
{
"name": build_secret_unique_name(
user_id=user_id, brain_id=brain_id, secret_name=secret_name
),
},
).execute()

return response.data
20 changes: 20 additions & 0 deletions backend/repository/external_api_secret/read_secret.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from uuid import UUID

from models import get_supabase_client
from utils import build_secret_unique_name


def read_secret(
user_id: UUID, brain_id: UUID, secret_name: str, secret_value
) -> UUID | None:
supabase_client = get_supabase_client()
response = supabase_client.rpc(
"read_secret",
{
"secret_name": build_secret_unique_name(
user_id=user_id, brain_id=brain_id, secret_name=secret_name
),
},
).execute()

return response.data
5 changes: 5 additions & 0 deletions backend/repository/external_api_secret/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from uuid import UUID


def build_secret_unique_name(user_id: UUID, brain_id: UUID, secret_name: str):
return f"{user_id}-{brain_id}-{secret_name}"
53 changes: 53 additions & 0 deletions scripts/20231107104700_setup_vault.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
CREATE OR REPLACE FUNCTION insert_secret(name text, secret text)
returns uuid
language plpgsql
security definer
set search_path = public
as $$
begin
return vault.create_secret(secret, name);
end;
$$;


create or replace function read_secret(secret_name text)
returns text
language plpgsql
security definer set search_path = public
as $$
declare
secret text;
begin
select decrypted_secret from vault.decrypted_secrets where name =
secret_name into secret;
return secret;
end;
$$;

create or replace function delete_secret(secret_name text)
returns text
language plpgsql
security definer set search_path = public
as $$
declare
deleted_rows int;
begin
delete from vault.decrypted_secrets where name = secret_name;
get diagnostics deleted_rows = row_count;
if deleted_rows = 0 then
return false;
else
return true;
end if;
end;
$$;

-- Insert a migration record if it doesn't exist
INSERT INTO migrations (name)
SELECT '20231107104700_setup_vault'
WHERE NOT EXISTS (
SELECT 1 FROM migrations WHERE name = '20231107104700_setup_vault'
);

-- Commit the changes
COMMIT;
50 changes: 48 additions & 2 deletions scripts/pg_tables.sql
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,59 @@ CREATE TABLE IF NOT EXISTS brain_subscription_invitations (
FOREIGN KEY (brain_id) REFERENCES brains (brain_id)
);

-- Create functions for secrets in vault
CREATE OR REPLACE FUNCTION insert_secret(name text, secret text)
returns uuid
language plpgsql
security definer
set search_path = public
as $$
begin
return vault.create_secret(secret, name);
end;
$$;


create or replace function read_secret(secret_name text)
returns text
language plpgsql
security definer set search_path = public
as $$
declare
secret text;
begin
select decrypted_secret from vault.decrypted_secrets where name =
secret_name into secret;
return secret;
end;
$$;

create or replace function delete_secret(secret_name text)
returns text
language plpgsql
security definer set search_path = public
as $$
declare
deleted_rows int;
begin
delete from vault.decrypted_secrets where name = secret_name;
get diagnostics deleted_rows = row_count;
if deleted_rows = 0 then
return false;
else
return true;
end if;
end;
$$;


CREATE TABLE IF NOT EXISTS migrations (
name VARCHAR(255) PRIMARY KEY,
executed_at TIMESTAMPTZ DEFAULT current_timestamp
);

INSERT INTO migrations (name)
SELECT '202307111517031_change_vectors_id_type'
SELECT '20231107104700_setup_vault'
WHERE NOT EXISTS (
SELECT 1 FROM migrations WHERE name = '202307111517031_change_vectors_id_type'
SELECT 1 FROM migrations WHERE name = '20231107104700_setup_vault'
);
52 changes: 48 additions & 4 deletions scripts/tables.sql
Original file line number Diff line number Diff line change
Expand Up @@ -398,10 +398,54 @@ CREATE POLICY "Access Quivr Storage 1jccrwz_2" ON storage.objects FOR UPDATE TO

CREATE POLICY "Access Quivr Storage 1jccrwz_3" ON storage.objects FOR DELETE TO anon USING (bucket_id = 'quivr');

-- Create functions for secrets in vault
CREATE OR REPLACE FUNCTION insert_secret(name text, secret text)
returns uuid
language plpgsql
security definer
set search_path = public
as $$
begin
return vault.create_secret(secret, name);
end;
$$;


create or replace function read_secret(secret_name text)
returns text
language plpgsql
security definer set search_path = public
as $$
declare
secret text;
begin
select decrypted_secret from vault.decrypted_secrets where name =
secret_name into secret;
return secret;
end;
$$;

create or replace function delete_secret(secret_name text)
returns text
language plpgsql
security definer set search_path = public
as $$
declare
deleted_rows int;
begin
delete from vault.decrypted_secrets where name = secret_name;
get diagnostics deleted_rows = row_count;
if deleted_rows = 0 then
return false;
else
return true;
end if;
end;
$$;


INSERT INTO migrations (name)
SELECT '20231106110000_add_field_brain_type_to_brain_table'
SELECT '20231107104700_setup_vault'
WHERE NOT EXISTS (
SELECT 1 FROM migrations WHERE name = '20231106110000_add_field_brain_type_to_brain_table'
SELECT 1 FROM migrations WHERE name = '20231107104700_setup_vault'
);