mirror of
https://github.com/StanGirard/quivr.git
synced 2024-11-27 10:20:32 +03:00
feat: supabase vault (#1605)
# Description https://github.com/StanGirard/quivr/issues/1551 ## Checklist before requesting a review Please delete options that are not relevant. - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my code - [ ] I have commented hard-to-understand areas - [ ] I have ideally added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged ## Screenshots (if appropriate):
This commit is contained in:
parent
dd2bc1f6fc
commit
ed5de8b80c
3
backend/repository/external_api_secret/__init__.py
Normal file
3
backend/repository/external_api_secret/__init__.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from .create_secret import create_secret
|
||||||
|
from .delete_secret import delete_secret
|
||||||
|
from .read_secret import read_secret
|
21
backend/repository/external_api_secret/create_secret.py
Normal file
21
backend/repository/external_api_secret/create_secret.py
Normal file
@ -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
backend/repository/external_api_secret/delete_secret.py
Normal file
18
backend/repository/external_api_secret/delete_secret.py
Normal file
@ -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
backend/repository/external_api_secret/read_secret.py
Normal file
20
backend/repository/external_api_secret/read_secret.py
Normal file
@ -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
backend/repository/external_api_secret/utils.py
Normal file
5
backend/repository/external_api_secret/utils.py
Normal file
@ -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
scripts/20231107104700_setup_vault.sql
Normal file
53
scripts/20231107104700_setup_vault.sql
Normal file
@ -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;
|
@ -167,13 +167,59 @@ CREATE TABLE IF NOT EXISTS brain_subscription_invitations (
|
|||||||
FOREIGN KEY (brain_id) REFERENCES brains (brain_id)
|
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 (
|
CREATE TABLE IF NOT EXISTS migrations (
|
||||||
name VARCHAR(255) PRIMARY KEY,
|
name VARCHAR(255) PRIMARY KEY,
|
||||||
executed_at TIMESTAMPTZ DEFAULT current_timestamp
|
executed_at TIMESTAMPTZ DEFAULT current_timestamp
|
||||||
);
|
);
|
||||||
|
|
||||||
INSERT INTO migrations (name)
|
INSERT INTO migrations (name)
|
||||||
SELECT '202307111517031_change_vectors_id_type'
|
SELECT '20231107104700_setup_vault'
|
||||||
WHERE NOT EXISTS (
|
WHERE NOT EXISTS (
|
||||||
SELECT 1 FROM migrations WHERE name = '202307111517031_change_vectors_id_type'
|
SELECT 1 FROM migrations WHERE name = '20231107104700_setup_vault'
|
||||||
);
|
);
|
@ -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 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)
|
INSERT INTO migrations (name)
|
||||||
SELECT '20231106110000_add_field_brain_type_to_brain_table'
|
SELECT '20231107104700_setup_vault'
|
||||||
WHERE NOT EXISTS (
|
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'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user