From a501bb5444757942d200f9a56e56131a15c1a254 Mon Sep 17 00:00:00 2001 From: giancarlo Date: Wed, 8 May 2024 17:16:33 +0700 Subject: [PATCH] Update SQL tests and schema definitions for membership and invitation management This commit includes updates to the SQL tests for the database layer handling memberships and invitations. Test cases are improved and expanded. It also introduces some changes to the underlying schema, including creation of new indices on the 'accounts' table and several adjustments to function definitions with regard to how user IDs are accessed. Also, some views related to user accounts are redefined for better data handling. --- .../migrations/20221215192558_schema.sql | 70 +++++++++++-------- .../tests/database/invitations.test.sql | 18 ++++- .../tests/database/memberships.test.sql | 12 +++- 3 files changed, 70 insertions(+), 30 deletions(-) diff --git a/apps/web/supabase/migrations/20221215192558_schema.sql b/apps/web/supabase/migrations/20221215192558_schema.sql index 63f5d4b04..90f66127d 100644 --- a/apps/web/supabase/migrations/20221215192558_schema.sql +++ b/apps/web/supabase/migrations/20221215192558_schema.sql @@ -357,6 +357,11 @@ add constraint accounts_slug_null_if_personal_account_true check ( ) ); +-- Indexes +create index if not exists ix_accounts_primary_owner_user_id on public.accounts (primary_owner_user_id); + +create index if not exists ix_accounts_is_personal_account on public.accounts (is_personal_account); + -- constraint to ensure that the primary_owner_user_id is unique for personal accounts create unique index unique_personal_account on public.accounts (primary_owner_user_id) where @@ -701,7 +706,7 @@ set from public.accounts_memberships membership where - membership.user_id = auth.uid() + membership.user_id = (select auth.uid()) and membership.account_id = has_role_on_account.account_id and((membership.account_role = has_role_on_account.account_role or has_role_on_account.account_role is null))); @@ -2375,13 +2380,20 @@ select accounts.id as id, accounts.name as name, accounts.picture_url as picture_url, - accounts.public_data as public_data, - subscriptions.status as subscription_status + ( + select + status + from + public.subscriptions + where + account_id = accounts.id + limit + 1 + ) as subscription_status from public.accounts - left join public.subscriptions on accounts.id = subscriptions.account_id where - primary_owner_user_id = auth.uid () + primary_owner_user_id = (select auth.uid ()) and accounts.is_personal_account = true limit 1; @@ -2396,21 +2408,29 @@ select -- we create a view to load the user's accounts and memberships -- useful to display the user's accounts in the app create or replace view - public.user_accounts + public.user_accounts (id, name, picture_url, slug, role) with (security_invoker = true) as select - accounts.id as id, - accounts.name as name, - accounts.picture_url as picture_url, - accounts.slug as slug, - accounts_memberships.account_role as role + account.id, + account.name, + account.picture_url, + account.slug, + membership.account_role from - public.accounts - join public.accounts_memberships on accounts.id = accounts_memberships.account_id + public.accounts account + join public.accounts_memberships membership on account.id = membership.account_id where - accounts_memberships.user_id = auth.uid () - and accounts.is_personal_account = false; + membership.user_id = (select auth.uid ()) + and account.is_personal_account = false + and account.id in ( + select + account_id + from + public.accounts_memberships + where + user_id = (select auth.uid ()) + ); grant select @@ -2420,8 +2440,8 @@ select -- -- Function "public.team_account_workspace" -- Load all the data for a team account workspace -create -or replace function public.team_account_workspace (account_slug text) returns table ( +create or replace function public.team_account_workspace(account_slug text) +returns table ( id uuid, name varchar(255), picture_url varchar(1000), @@ -2431,9 +2451,7 @@ or replace function public.team_account_workspace (account_slug text) returns ta primary_owner_user_id uuid, subscription_status public.subscription_status, permissions public.app_permissions[] -) -set - search_path = '' as $$ +) as $$ begin return QUERY select @@ -2448,23 +2466,19 @@ begin array_agg(role_permissions.permission) from public.accounts - join public.accounts_memberships on accounts.id = - accounts_memberships.account_id + join public.accounts_memberships on accounts.id = accounts_memberships.account_id left join public.subscriptions on accounts.id = subscriptions.account_id - left join public.role_permissions on - accounts_memberships.account_role = role_permissions.role - left join public.roles on accounts_memberships.account_role = roles.name + join public.roles on accounts_memberships.account_role = roles.name + left join public.role_permissions on accounts_memberships.account_role = role_permissions.role where accounts.slug = account_slug - and public.accounts_memberships.user_id = auth.uid() + and public.accounts_memberships.user_id = (select auth.uid()) group by accounts.id, accounts_memberships.account_role, subscriptions.status, roles.hierarchy_level; - end; - $$ language plpgsql; grant diff --git a/apps/web/supabase/tests/database/invitations.test.sql b/apps/web/supabase/tests/database/invitations.test.sql index 16cb0bdcd..7da564484 100644 --- a/apps/web/supabase/tests/database/invitations.test.sql +++ b/apps/web/supabase/tests/database/invitations.test.sql @@ -58,6 +58,17 @@ select lives_ok( 'custom role should be able to create invitations' ); +select lives_ok( + $$ SELECT public.add_invitations_to_account('makerkit', ARRAY[ROW('example@makerkit.dev', 'custom-role')::public.invitation]); $$, + 'custom role should be able to create invitations using the function public.add_invitations_to_account' +); + +select throws_ok( + $$ SELECT public.add_invitations_to_account('makerkit', ARRAY[ROW('example2@makerkit.dev', 'owner')::public.invitation]); $$, + 'new row violates row-level security policy for table "invitations"', + 'cannot invite members with higher roles' +); + -- Foreigners should not be able to create invitations select tests.create_supabase_user('user'); @@ -70,6 +81,11 @@ select throws_ok( 'new row violates row-level security policy for table "invitations"' ); +select throws_ok( + $$ SELECT public.add_invitations_to_account('makerkit', ARRAY[ROW('example@example.com', 'member')::public.invitation]); $$, + 'new row violates row-level security policy for table "invitations"' +); + select is_empty($$ select * from public.invitations where account_id = makerkit.get_account_id_by_slug('makerkit') $$, 'no invitations should be listed' @@ -77,4 +93,4 @@ select is_empty($$ select * from finish(); -rollback; \ No newline at end of file +rollback; diff --git a/apps/web/supabase/tests/database/memberships.test.sql b/apps/web/supabase/tests/database/memberships.test.sql index f35b67550..4f7c04ff9 100644 --- a/apps/web/supabase/tests/database/memberships.test.sql +++ b/apps/web/supabase/tests/database/memberships.test.sql @@ -45,6 +45,11 @@ select is( 'The member can check if they have a role on the account' ); +select isnt_empty( + $$ select * from public.get_account_members('makerkit') $$, + 'The member can query the team account memberships using the get_account_members function' +); + select tests.authenticate_as('test'); -- Foreigners @@ -77,6 +82,11 @@ select is_empty( 'The foreigner cannot query the team account' ); +select is_empty( + $$ select * from public.get_account_members('makerkit') $$, + 'The foreigner cannot query the team members' +); + select * from finish(); -rollback; \ No newline at end of file +rollback;