B2B-88: add starter kit structure and elements
This commit is contained in:
74
supabase copy/tests/database/00000-dbdev.sql
Normal file
74
supabase copy/tests/database/00000-dbdev.sql
Normal file
@@ -0,0 +1,74 @@
|
||||
create extension if not exists http with schema extensions;
|
||||
create extension if not exists pg_tle;
|
||||
|
||||
select
|
||||
no_plan ();
|
||||
|
||||
create or replace function install_extensions()
|
||||
returns void
|
||||
as $$
|
||||
declare
|
||||
installed boolean;
|
||||
begin
|
||||
select exists (
|
||||
select
|
||||
1
|
||||
from
|
||||
pg_catalog.pg_extension
|
||||
where
|
||||
extname = 'supabase-dbdev'
|
||||
) into installed;
|
||||
|
||||
if installed then
|
||||
return;
|
||||
end if;
|
||||
|
||||
perform
|
||||
pgtle.install_extension(
|
||||
'supabase-dbdev',
|
||||
resp.contents ->> 'version',
|
||||
'PostgreSQL package manager',
|
||||
resp.contents ->> 'sql'
|
||||
)
|
||||
from http(
|
||||
(
|
||||
'GET',
|
||||
'https://api.database.dev/rest/v1/'
|
||||
|| 'package_versions?select=sql,version'
|
||||
|| '&package_name=eq.supabase-dbdev'
|
||||
|| '&order=version.desc'
|
||||
|| '&limit=1',
|
||||
array[
|
||||
('apiKey', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InhtdXB0cHBsZnZpaWZyYndtbXR2Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2ODAxMDczNzIsImV4cCI6MTk5NTY4MzM3Mn0.z2CN0mvO2No8wSi46Gw59DFGCTJrzM0AQKsu_5k134s')::http_header
|
||||
],
|
||||
null,
|
||||
null
|
||||
)
|
||||
) x,
|
||||
lateral (
|
||||
select
|
||||
((row_to_json(x) -> 'content') #>> '{}')::json -> 0
|
||||
) resp(contents);
|
||||
|
||||
create extension if not exists "supabase-dbdev";
|
||||
|
||||
perform dbdev.install('supabase-dbdev');
|
||||
perform dbdev.install('basejump-supabase_test_helpers');
|
||||
end
|
||||
$$ language plpgsql;
|
||||
|
||||
select install_extensions();
|
||||
|
||||
select has_column(
|
||||
'auth',
|
||||
'users',
|
||||
'id',
|
||||
'id should exist'
|
||||
);
|
||||
|
||||
select
|
||||
*
|
||||
from
|
||||
finish ();
|
||||
|
||||
rollback;
|
||||
151
supabase copy/tests/database/00000-makerkit-helpers.sql
Normal file
151
supabase copy/tests/database/00000-makerkit-helpers.sql
Normal file
@@ -0,0 +1,151 @@
|
||||
create schema if not exists makerkit;
|
||||
|
||||
-- anon, authenticated, and service_role should have access to makerkit schema
|
||||
grant USAGE on schema makerkit to anon, authenticated, service_role;
|
||||
|
||||
-- Don't allow public to execute any functions in the makerkit schema
|
||||
alter default PRIVILEGES in schema makerkit revoke execute on FUNCTIONS from public;
|
||||
|
||||
-- Grant execute to anon, authenticated, and service_role for testing purposes
|
||||
alter default PRIVILEGES in schema makerkit grant execute on FUNCTIONS to anon,
|
||||
authenticated, service_role;
|
||||
|
||||
create or replace function makerkit.get_id_by_identifier(
|
||||
identifier text
|
||||
)
|
||||
returns uuid
|
||||
as $$
|
||||
begin
|
||||
|
||||
return (select id from auth.users where raw_user_meta_data->>'test_identifier' = identifier);
|
||||
|
||||
end;
|
||||
|
||||
$$ language PLPGSQL;
|
||||
|
||||
create or replace function makerkit.set_identifier(
|
||||
identifier text,
|
||||
user_email text
|
||||
)
|
||||
returns text
|
||||
security definer
|
||||
set search_path = auth, pg_temp
|
||||
as
|
||||
$$
|
||||
begin
|
||||
update auth.users
|
||||
set raw_user_meta_data = jsonb_build_object('test_identifier', identifier)
|
||||
where email = user_email;
|
||||
|
||||
return identifier;
|
||||
|
||||
end;
|
||||
|
||||
$$ language PLPGSQL;
|
||||
|
||||
create or replace function makerkit.get_account_by_slug(
|
||||
account_slug text
|
||||
)
|
||||
returns setof accounts
|
||||
as
|
||||
$$
|
||||
begin
|
||||
return query
|
||||
select *
|
||||
from accounts
|
||||
where slug = account_slug;
|
||||
|
||||
end;
|
||||
|
||||
$$ language PLPGSQL;
|
||||
|
||||
create or replace function makerkit.authenticate_as(
|
||||
identifier text
|
||||
) returns void
|
||||
as
|
||||
$$
|
||||
begin
|
||||
perform tests.authenticate_as(identifier);
|
||||
perform makerkit.set_session_aal('aal1');
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
create or replace function makerkit.get_account_id_by_slug(
|
||||
account_slug text
|
||||
)
|
||||
returns uuid
|
||||
as
|
||||
$$
|
||||
|
||||
begin
|
||||
|
||||
return
|
||||
(select id
|
||||
from accounts
|
||||
where slug = account_slug);
|
||||
|
||||
end;
|
||||
|
||||
$$ language PLPGSQL;
|
||||
|
||||
|
||||
create or replace function makerkit.set_mfa_factor(
|
||||
identifier text = gen_random_uuid()
|
||||
)
|
||||
returns void
|
||||
as
|
||||
$$
|
||||
begin
|
||||
insert into "auth"."mfa_factors" ("id", "user_id", "friendly_name", "factor_type", "status", "created_at", "updated_at", "secret")
|
||||
values (gen_random_uuid(), auth.uid(), identifier, 'totp', 'verified', '2025-02-24 09:48:18.402031+00', '2025-02-24 09:48:18.402031+00',
|
||||
'HOWQFBA7KBDDRSBNMGFYZAFNPRSZ62I5');
|
||||
end;
|
||||
$$ language plpgsql security definer;
|
||||
|
||||
create or replace function makerkit.set_session_aal(session_aal auth.aal_level)
|
||||
returns void
|
||||
as
|
||||
$$
|
||||
begin
|
||||
perform set_config('request.jwt.claims', json_build_object(
|
||||
'sub', current_setting('request.jwt.claims')::json ->> 'sub',
|
||||
'email', current_setting('request.jwt.claims')::json ->> 'email',
|
||||
'phone', current_setting('request.jwt.claims')::json ->> 'phone',
|
||||
'user_metadata', current_setting('request.jwt.claims')::json ->> 'user_metadata',
|
||||
'app_metadata', current_setting('request.jwt.claims')::json ->> 'app_metadata',
|
||||
'aal', session_aal)::text, true);
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
create or replace function makerkit.set_super_admin() returns void
|
||||
as
|
||||
$$
|
||||
begin
|
||||
perform set_config('request.jwt.claims', json_build_object(
|
||||
'sub', current_setting('request.jwt.claims')::json ->> 'sub',
|
||||
'email', current_setting('request.jwt.claims')::json ->> 'email',
|
||||
'phone', current_setting('request.jwt.claims')::json ->> 'phone',
|
||||
'user_metadata', current_setting('request.jwt.claims')::json ->> 'user_metadata',
|
||||
'app_metadata', json_build_object('role', 'super-admin'),
|
||||
'aal', current_setting('request.jwt.claims')::json ->> 'aal'
|
||||
)::text, true);
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
begin;
|
||||
|
||||
select plan(1);
|
||||
|
||||
select is_empty($$
|
||||
select
|
||||
*
|
||||
from
|
||||
makerkit.get_account_by_slug('test') $$,
|
||||
'get_account_by_slug should return an empty set when the account does not exist'
|
||||
);
|
||||
|
||||
select *
|
||||
from
|
||||
finish();
|
||||
|
||||
rollback;
|
||||
104
supabase copy/tests/database/account-permissions.test.sql
Normal file
104
supabase copy/tests/database/account-permissions.test.sql
Normal file
@@ -0,0 +1,104 @@
|
||||
BEGIN;
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select no_plan();
|
||||
|
||||
--- we insert a user into auth.users and return the id into user_id to use
|
||||
|
||||
select tests.create_supabase_user('test1', 'test1@test.com');
|
||||
|
||||
select tests.create_supabase_user('test2');
|
||||
|
||||
-- Create an team account
|
||||
|
||||
select makerkit.authenticate_as('test1');
|
||||
|
||||
select public.create_team_account('Test');
|
||||
|
||||
-- the owner account has permissions to manage members
|
||||
select row_eq(
|
||||
$$ select public.has_permission(
|
||||
auth.uid(), makerkit.get_account_id_by_slug('test'), 'members.manage'::app_permissions) $$,
|
||||
row(true::boolean),
|
||||
'The owner of the team account should have the members.manage permission'
|
||||
);
|
||||
|
||||
-- the owner account has permissions to manage billing
|
||||
select row_eq(
|
||||
$$ select public.has_permission(
|
||||
auth.uid(), makerkit.get_account_id_by_slug('test'), 'billing.manage'::app_permissions) $$,
|
||||
row(true::boolean),
|
||||
'The owner of the team account should have the billing.manage permission'
|
||||
);
|
||||
|
||||
-- Foreigner should not have permissions to manage members
|
||||
|
||||
select makerkit.authenticate_as('test2');
|
||||
|
||||
select row_eq(
|
||||
$$ select public.has_permission(
|
||||
auth.uid(), makerkit.get_account_id_by_slug('test'), 'members.manage'::app_permissions) $$,
|
||||
row(false::boolean),
|
||||
'Foreigners should not have the members.manage permission'
|
||||
);
|
||||
|
||||
-- Custom roles
|
||||
-- New roles created for the app
|
||||
|
||||
set local role postgres;
|
||||
|
||||
-- the name should be unique
|
||||
|
||||
select throws_ok(
|
||||
$$ insert into public.roles (name, hierarchy_level) values ('owner', 4) $$,
|
||||
'duplicate key value violates unique constraint "roles_pkey"'
|
||||
);
|
||||
|
||||
-- the hierarchy level should be unique
|
||||
select throws_ok(
|
||||
$$ insert into public.roles (name, hierarchy_level) values ('custom-role-2', 1) $$,
|
||||
'duplicate key value violates unique constraint "roles_hierarchy_level_key"'
|
||||
);
|
||||
|
||||
-- Custom Account Role
|
||||
|
||||
set local role postgres;
|
||||
|
||||
-- the names should be unique
|
||||
select throws_ok(
|
||||
$$ insert into public.roles (name, hierarchy_level) values ('owner', 1) $$,
|
||||
'duplicate key value violates unique constraint "roles_pkey"'
|
||||
);
|
||||
|
||||
-- update user role to custom role
|
||||
update public.accounts_memberships
|
||||
set account_role = 'custom-role'
|
||||
where account_id = makerkit.get_account_id_by_slug('test')
|
||||
and user_id = tests.get_supabase_uid('test1');
|
||||
|
||||
set local role postgres;
|
||||
|
||||
-- insert permissions for the custom role
|
||||
insert into public.role_permissions (role, permission) values ('custom-role', 'members.manage');
|
||||
|
||||
select makerkit.authenticate_as('test1');
|
||||
|
||||
-- the custom role does not have permissions to manage billing
|
||||
select row_eq(
|
||||
$$ select public.has_permission(
|
||||
auth.uid(), makerkit.get_account_id_by_slug('test'), 'billing.manage'::app_permissions) $$,
|
||||
row(false::boolean),
|
||||
'The custom role should not have the billing.manage permission'
|
||||
);
|
||||
|
||||
-- the custom role can manage members
|
||||
select row_eq(
|
||||
$$ select public.has_permission(
|
||||
auth.uid(), makerkit.get_account_id_by_slug('test'), 'members.manage'::app_permissions) $$,
|
||||
row(true::boolean),
|
||||
'The custom role should have the members.manage permission'
|
||||
);
|
||||
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
128
supabase copy/tests/database/account-slug.test.sql
Normal file
128
supabase copy/tests/database/account-slug.test.sql
Normal file
@@ -0,0 +1,128 @@
|
||||
BEGIN;
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select no_plan();
|
||||
|
||||
--- we insert a user into auth.users and return the id into user_id to use
|
||||
|
||||
select tests.create_supabase_user('test1', 'test1@test.com');
|
||||
|
||||
select tests.create_supabase_user('test2');
|
||||
|
||||
-- Create an team account
|
||||
|
||||
select makerkit.authenticate_as('test1');
|
||||
|
||||
select public.create_team_account('Test');
|
||||
select public.create_team_account('Test');
|
||||
select public.create_team_account('Test');
|
||||
|
||||
-- should automatically create slugs for the accounts
|
||||
select row_eq(
|
||||
$$ select slug from public.accounts where name = 'Test' and slug = 'test' $$,
|
||||
row('test'::text),
|
||||
'The first team account should automatically create a slug named "test"'
|
||||
);
|
||||
|
||||
select row_eq(
|
||||
$$ select slug from public.accounts where name = 'Test' and slug = 'test-1' $$,
|
||||
row('test-1'::text),
|
||||
'The second team account should automatically create a slug named "test-1"'
|
||||
);
|
||||
|
||||
select row_eq(
|
||||
$$ select slug from public.accounts where name = 'Test' and slug = 'test-2' $$,
|
||||
row('test-2'::text),
|
||||
'The third team account should automatically create a slug named "test-2"'
|
||||
);
|
||||
|
||||
-- Should automatically update the slug if the name is updated
|
||||
update public.accounts set name = 'Test 4' where slug = 'test-2';
|
||||
|
||||
select row_eq(
|
||||
$$ select slug from public.accounts where name = 'Test 4' $$,
|
||||
row('test-4'::text),
|
||||
'Updating the name of a team account should update the slug'
|
||||
);
|
||||
|
||||
-- Should fail if the slug is updated to an existing slug
|
||||
select throws_ok(
|
||||
$$ update public.accounts set slug = 'test-1' where slug = 'test-4' $$,
|
||||
'duplicate key value violates unique constraint "accounts_slug_key"'
|
||||
);
|
||||
|
||||
-- Test special characters in the slug
|
||||
update public.accounts set slug = 'test-5' where slug = 'test-4[';
|
||||
|
||||
select row_eq(
|
||||
$$ select slug from public.accounts where name = 'Test 4' $$,
|
||||
row('test-4'::text),
|
||||
'Updating the name of a team account should update the slug'
|
||||
);
|
||||
|
||||
-- Test various special characters
|
||||
update public.accounts set name = 'Test@Special#Chars$' where slug = 'test-4';
|
||||
|
||||
select row_eq(
|
||||
$$ select slug from public.accounts where name = 'Test@Special#Chars$' $$,
|
||||
row('test-special-chars'::text),
|
||||
'Special characters should be removed from slug'
|
||||
);
|
||||
|
||||
-- Test multiple consecutive special characters
|
||||
update public.accounts set name = 'Test!!Multiple---Special$$$Chars' where slug = 'test-special-chars';
|
||||
|
||||
select row_eq(
|
||||
$a$ select slug from public.accounts where name = 'Test!!Multiple---Special$$$Chars' $a$,
|
||||
row('test-multiple-special-chars'::text),
|
||||
'Multiple consecutive special characters should be replaced with single hyphen'
|
||||
);
|
||||
|
||||
-- Test leading and trailing special characters
|
||||
update public.accounts set name = '!!!LeadingAndTrailing###' where slug = 'test-multiple-special-chars';
|
||||
|
||||
select row_eq(
|
||||
$$ select slug from public.accounts where name = '!!!LeadingAndTrailing###' $$,
|
||||
row('leadingandtrailing'::text),
|
||||
'Leading and trailing special characters should be removed'
|
||||
);
|
||||
|
||||
-- Test non-ASCII characters
|
||||
update public.accounts set name = 'Testéñ中文Русский' where slug = 'leadingandtrailing';
|
||||
|
||||
select row_eq(
|
||||
$$ select slug from public.accounts where name = 'Testéñ中文Русский' $$,
|
||||
row('testen'::text),
|
||||
'Non-ASCII characters should be transliterated or removed'
|
||||
);
|
||||
|
||||
-- Test mixed case with special characters
|
||||
update public.accounts set name = 'Test Mixed CASE With Special@Chars!' where slug = 'testen';
|
||||
|
||||
select row_eq(
|
||||
$$ select slug from public.accounts where name = 'Test Mixed CASE With Special@Chars!' $$,
|
||||
row('test-mixed-case-with-special-chars'::text),
|
||||
'Mixed case should be converted to lowercase and special chars handled'
|
||||
);
|
||||
|
||||
-- Test using parentheses
|
||||
update public.accounts set name = 'Test (Parentheses)' where slug = 'test-mixed-case-with-special-chars';
|
||||
|
||||
select row_eq(
|
||||
$$ select slug from public.accounts where name = 'Test (Parentheses)' $$,
|
||||
row('test-parentheses'::text),
|
||||
'Parentheses should be removed from slug'
|
||||
);
|
||||
|
||||
-- Test using asterisk
|
||||
update public.accounts set name = 'Test * Asterisk' where slug = 'test-parentheses';
|
||||
|
||||
select row_eq(
|
||||
$$ select slug from public.accounts where name = 'Test * Asterisk' $$,
|
||||
row('test-asterisk'::text),
|
||||
'Asterisk should be removed from slug'
|
||||
);
|
||||
|
||||
select * from finish();
|
||||
|
||||
ROLLBACK;
|
||||
94
supabase copy/tests/database/delete-membership.test.sql
Normal file
94
supabase copy/tests/database/delete-membership.test.sql
Normal file
@@ -0,0 +1,94 @@
|
||||
begin;
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select no_plan();
|
||||
|
||||
select makerkit.set_identifier('primary_owner', 'test@makerkit.dev');
|
||||
select makerkit.set_identifier('owner', 'owner@makerkit.dev');
|
||||
select makerkit.set_identifier('member', 'member@makerkit.dev');
|
||||
select makerkit.set_identifier('custom', 'custom@makerkit.dev');
|
||||
|
||||
-- another user not in the team
|
||||
select tests.create_supabase_user('test', 'test@supabase.com');
|
||||
|
||||
-- an owner cannot remove the primary owner
|
||||
select makerkit.authenticate_as('owner');
|
||||
|
||||
select throws_ok(
|
||||
$$ delete from public.accounts_memberships
|
||||
where account_id = makerkit.get_account_id_by_slug('makerkit')
|
||||
and user_id = '31a03e74-1639-45b6-bfa7-77447f1a4762' $$,
|
||||
'The primary account owner cannot be actioned'
|
||||
);
|
||||
|
||||
-- an owner can remove accounts with lower roles
|
||||
select lives_ok(
|
||||
$$ delete from public.accounts_memberships
|
||||
where account_id = makerkit.get_account_id_by_slug('makerkit')
|
||||
and user_id = '6b83d656-e4ab-48e3-a062-c0c54a427368' $$,
|
||||
'Owner should be able to remove a member'
|
||||
);
|
||||
|
||||
-- a member cannot remove a member with a higher role
|
||||
select makerkit.authenticate_as('member');
|
||||
|
||||
-- delete a membership record where the user is a higher role than the current user
|
||||
select throws_ok(
|
||||
$$ delete from public.accounts_memberships
|
||||
where account_id = makerkit.get_account_id_by_slug('makerkit')
|
||||
and user_id = '5c064f1b-78ee-4e1c-ac3b-e99aa97c99bf' $$,
|
||||
'You do not have permission to action a member from this account'
|
||||
);
|
||||
|
||||
-- an primary_owner cannot remove themselves
|
||||
select makerkit.authenticate_as('primary_owner');
|
||||
|
||||
select throws_ok(
|
||||
$$ delete from public.accounts_memberships
|
||||
where account_id = makerkit.get_account_id_by_slug('makerkit')
|
||||
and user_id = '31a03e74-1639-45b6-bfa7-77447f1a4762' $$,
|
||||
'The primary account owner cannot be removed from the account membership list'
|
||||
);
|
||||
|
||||
-- a primary_owner can remove another member
|
||||
select lives_ok(
|
||||
$$ delete from public.accounts_memberships
|
||||
where account_id = makerkit.get_account_id_by_slug('makerkit')
|
||||
and user_id = 'b73eb03e-fb7a-424d-84ff-18e2791ce0b4'; $$,
|
||||
'Primary owner should be able to remove another member'
|
||||
);
|
||||
|
||||
-- foreigners
|
||||
|
||||
-- a user not in the account cannot remove a member
|
||||
|
||||
select makerkit.authenticate_as('test');
|
||||
|
||||
select throws_ok(
|
||||
$$ delete from public.accounts_memberships
|
||||
where account_id = '5deaa894-2094-4da3-b4fd-1fada0809d1c'
|
||||
and user_id = tests.get_supabase_uid('owner'); $$,
|
||||
'You do not have permission to action a member from this account'
|
||||
);
|
||||
|
||||
select makerkit.authenticate_as('owner');
|
||||
|
||||
select isnt_empty(
|
||||
$$ select 1 from public.accounts_memberships
|
||||
where account_id = '5deaa894-2094-4da3-b4fd-1fada0809d1c'
|
||||
and user_id = tests.get_supabase_uid('owner'); $$,
|
||||
'Foreigners should not be able to remove members');
|
||||
|
||||
select makerkit.authenticate_as('test');
|
||||
|
||||
-- a user not in the account cannot remove themselves
|
||||
select throws_ok(
|
||||
$$ delete from public.accounts_memberships
|
||||
where account_id = makerkit.get_account_id_by_slug('makerkit')
|
||||
and user_id = auth.uid(); $$,
|
||||
'You do not have permission to action a member from this account'
|
||||
);
|
||||
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
111
supabase copy/tests/database/invitations.test.sql
Normal file
111
supabase copy/tests/database/invitations.test.sql
Normal file
@@ -0,0 +1,111 @@
|
||||
begin;
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select no_plan();
|
||||
|
||||
-- test
|
||||
|
||||
select makerkit.set_identifier('test', 'test@makerkit.dev');
|
||||
select makerkit.set_identifier('member', 'member@makerkit.dev');
|
||||
select makerkit.set_identifier('custom', 'custom@makerkit.dev');
|
||||
select makerkit.set_identifier('owner', 'owner@makerkit.dev');
|
||||
|
||||
select makerkit.authenticate_as('test');
|
||||
|
||||
select lives_ok(
|
||||
$$ insert into public.invitations (email, invited_by, account_id, role, invite_token) values ('invite1@makerkit.dev', auth.uid(), makerkit.get_account_id_by_slug('makerkit'), 'member', gen_random_uuid()); $$,
|
||||
'owner should be able to create invitations'
|
||||
);
|
||||
|
||||
-- check two invitations to the same email/account are not allowed
|
||||
select throws_ok(
|
||||
$$ insert into public.invitations (email, invited_by, account_id, role, invite_token) values ('invite1@makerkit.dev', auth.uid(), makerkit.get_account_id_by_slug('makerkit'), 'member', gen_random_uuid()) $$,
|
||||
'duplicate key value violates unique constraint "invitations_email_account_id_key"'
|
||||
);
|
||||
|
||||
select makerkit.authenticate_as('member');
|
||||
|
||||
-- check a member cannot invite members with higher roles
|
||||
select throws_ok(
|
||||
$$ insert into public.invitations (email, invited_by, account_id, role, invite_token) values ('invite2@makerkit.dev', auth.uid(), makerkit.get_account_id_by_slug('makerkit'), 'owner', gen_random_uuid()) $$,
|
||||
'new row violates row-level security policy for table "invitations"'
|
||||
);
|
||||
|
||||
-- check a member can invite members with the same or lower roles
|
||||
select lives_ok(
|
||||
$$ insert into public.invitations (email, invited_by, account_id, role, invite_token) values ('invite2@makerkit.dev', auth.uid(), makerkit.get_account_id_by_slug('makerkit'), 'member', gen_random_uuid()) $$,
|
||||
'member should be able to create invitations for members or lower roles'
|
||||
);
|
||||
|
||||
-- test invite exists
|
||||
select isnt_empty(
|
||||
$$ select * from public.invitations where account_id = makerkit.get_account_id_by_slug('makerkit') $$,
|
||||
'invitations should be listed'
|
||||
);
|
||||
|
||||
select makerkit.authenticate_as('owner');
|
||||
|
||||
-- check the owner can invite members with lower roles
|
||||
select lives_ok(
|
||||
$$ insert into public.invitations (email, invited_by, account_id, role, invite_token) values ('invite3@makerkit.dev', auth.uid(), makerkit.get_account_id_by_slug('makerkit'), 'member', gen_random_uuid()) $$,
|
||||
'owner should be able to create invitations'
|
||||
);
|
||||
|
||||
-- authenticate_as the custom role
|
||||
select makerkit.authenticate_as('custom');
|
||||
|
||||
-- it will fail because the custom role does not have the invites.manage permission
|
||||
select throws_ok(
|
||||
$$ insert into public.invitations (email, invited_by, account_id, role, invite_token) values ('invite3@makerkit.dev', auth.uid(), makerkit.get_account_id_by_slug('makerkit'), 'custom-role', gen_random_uuid()) $$,
|
||||
'new row violates row-level security policy for table "invitations"'
|
||||
);
|
||||
|
||||
set local role postgres;
|
||||
|
||||
-- add permissions to invite members to the custom role
|
||||
insert into public.role_permissions (role, permission) values ('custom-role', 'invites.manage');
|
||||
|
||||
-- authenticate_as the custom role
|
||||
select makerkit.authenticate_as('custom');
|
||||
|
||||
select lives_ok(
|
||||
$$ insert into public.invitations (email, invited_by, account_id, role, invite_token) values ('invite4@makerkit.dev', auth.uid(), makerkit.get_account_id_by_slug('makerkit'), 'custom-role', gen_random_uuid()) $$,
|
||||
'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');
|
||||
|
||||
select makerkit.authenticate_as('user');
|
||||
|
||||
-- it will fail because the user is not a member of the account
|
||||
select throws_ok(
|
||||
$$ insert into public.invitations (email, invited_by, account_id, role, invite_token) values ('invite4@makerkit.dev', auth.uid(), makerkit.get_account_id_by_slug('makerkit'), 'member', gen_random_uuid()) $$,
|
||||
'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'
|
||||
);
|
||||
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
92
supabase copy/tests/database/memberships.test.sql
Normal file
92
supabase copy/tests/database/memberships.test.sql
Normal file
@@ -0,0 +1,92 @@
|
||||
begin;
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select no_plan();
|
||||
|
||||
select makerkit.set_identifier('primary_owner', 'test@makerkit.dev');
|
||||
select makerkit.set_identifier('owner', 'owner@makerkit.dev');
|
||||
select makerkit.set_identifier('member', 'member@makerkit.dev');
|
||||
select makerkit.set_identifier('custom', 'custom@makerkit.dev');
|
||||
|
||||
-- another user not in the team
|
||||
select tests.create_supabase_user('test', 'test@supabase.com');
|
||||
|
||||
select makerkit.authenticate_as('owner');
|
||||
|
||||
-- Can check if an account is a team member
|
||||
|
||||
-- Primary owner
|
||||
select is(
|
||||
(select public.is_team_member(
|
||||
makerkit.get_account_id_by_slug('makerkit'),
|
||||
tests.get_supabase_uid('member')
|
||||
)),
|
||||
true,
|
||||
'The primary account owner can check if a member is a team member'
|
||||
);
|
||||
|
||||
select makerkit.authenticate_as('member');
|
||||
|
||||
-- Member
|
||||
select is(
|
||||
(select public.is_team_member(
|
||||
makerkit.get_account_id_by_slug('makerkit'),
|
||||
tests.get_supabase_uid('owner')
|
||||
)),
|
||||
true,
|
||||
'The member can check if another member is a team member'
|
||||
);
|
||||
|
||||
select is(
|
||||
(select public.has_role_on_account(
|
||||
makerkit.get_account_id_by_slug('makerkit')
|
||||
)),
|
||||
true,
|
||||
'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 makerkit.authenticate_as('test');
|
||||
|
||||
-- Foreigners
|
||||
-- Cannot query the team account memberships
|
||||
select is(
|
||||
(select public.is_team_member(
|
||||
makerkit.get_account_id_by_slug('makerkit'),
|
||||
tests.get_supabase_uid('owner')
|
||||
)),
|
||||
false,
|
||||
'The foreigner cannot check if a member is a team member'
|
||||
);
|
||||
|
||||
-- Does not have a role on the account
|
||||
select is(
|
||||
(select public.has_role_on_account(
|
||||
makerkit.get_account_id_by_slug('makerkit')
|
||||
)),
|
||||
false,
|
||||
'The foreigner does not have a role on the account'
|
||||
);
|
||||
|
||||
select is_empty(
|
||||
$$ select * from public.accounts_memberships where account_id = makerkit.get_account_id_by_slug('makerkit') $$,
|
||||
'The foreigner cannot query the team account memberships'
|
||||
);
|
||||
|
||||
select is_empty(
|
||||
$$ select * from public.accounts where id = makerkit.get_account_id_by_slug('makerkit') $$,
|
||||
'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;
|
||||
77
supabase copy/tests/database/notifications.test.sql
Normal file
77
supabase copy/tests/database/notifications.test.sql
Normal file
@@ -0,0 +1,77 @@
|
||||
BEGIN;
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select no_plan();
|
||||
|
||||
--- we insert a user into auth.users and return the id into user_id to use
|
||||
|
||||
select tests.create_supabase_user('test1', 'test1@test.com');
|
||||
|
||||
select tests.create_supabase_user('test2');
|
||||
|
||||
select makerkit.authenticate_as('test1');
|
||||
|
||||
-- users cannot insert into notifications
|
||||
select throws_ok(
|
||||
$$ insert into public.notifications(account_id, body) values (tests.get_supabase_uid('test1'), 'test'); $$,
|
||||
'permission denied for table notifications'
|
||||
);
|
||||
|
||||
set local role service_role;
|
||||
|
||||
-- service role can insert into notifications
|
||||
select lives_ok(
|
||||
$$ insert into public.notifications(account_id, body) values (tests.get_supabase_uid('test1'), 'test'); $$,
|
||||
'service role can insert into notifications'
|
||||
);
|
||||
|
||||
select makerkit.authenticate_as('test1');
|
||||
|
||||
-- user can read their own notifications
|
||||
select row_eq(
|
||||
$$ select account_id, body from public.notifications where account_id = tests.get_supabase_uid('test1'); $$,
|
||||
row (tests.get_supabase_uid('test1'), 'test'::varchar),
|
||||
'user can read their own notifications'
|
||||
);
|
||||
|
||||
-- user can read their team notifications
|
||||
select makerkit.set_identifier('primary_owner', 'test@makerkit.dev');
|
||||
select makerkit.set_identifier('owner', 'owner@makerkit.dev');
|
||||
select makerkit.set_identifier('member', 'member@makerkit.dev');
|
||||
select makerkit.set_identifier('custom', 'custom@makerkit.dev');
|
||||
|
||||
set local role service_role;
|
||||
|
||||
-- service role can insert into notifications
|
||||
select lives_ok(
|
||||
$$ insert into public.notifications(account_id, body) values (makerkit.get_account_id_by_slug('makerkit'), 'test'); $$,
|
||||
'service role can insert into notifications'
|
||||
);
|
||||
|
||||
select makerkit.authenticate_as('member');
|
||||
|
||||
select row_eq(
|
||||
$$ select account_id, body from public.notifications where account_id = makerkit.get_account_id_by_slug('makerkit'); $$,
|
||||
row (makerkit.get_account_id_by_slug('makerkit'), 'test'::varchar),
|
||||
'user can read their team notifications'
|
||||
);
|
||||
|
||||
-- foreigners
|
||||
|
||||
select makerkit.authenticate_as('test2');
|
||||
|
||||
-- foreigner cannot read other user's notifications
|
||||
select is_empty(
|
||||
$$ select account_id, body from public.notifications where account_id = tests.get_supabase_uid('test1'); $$,
|
||||
'foreigner cannot read other users notifications'
|
||||
);
|
||||
|
||||
-- foreigner cannot read other teams notifications
|
||||
select is_empty(
|
||||
$$ select account_id, body from public.notifications where account_id = makerkit.get_account_id_by_slug('makerkit'); $$,
|
||||
'foreigner cannot read other teams notifications'
|
||||
);
|
||||
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
1112
supabase copy/tests/database/otp.test.sql
Normal file
1112
supabase copy/tests/database/otp.test.sql
Normal file
File diff suppressed because it is too large
Load Diff
57
supabase copy/tests/database/personal-accounts.test.sql
Normal file
57
supabase copy/tests/database/personal-accounts.test.sql
Normal file
@@ -0,0 +1,57 @@
|
||||
BEGIN;
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select no_plan();
|
||||
|
||||
--- we insert a user into auth.users and return the id into user_id to use
|
||||
|
||||
select tests.create_supabase_user('test1', 'test1@test.com');
|
||||
|
||||
select tests.create_supabase_user('test2');
|
||||
|
||||
------------
|
||||
--- Primary Owner
|
||||
------------
|
||||
select makerkit.authenticate_as('test1');
|
||||
|
||||
-- should create the personal account automatically with the same ID as the user
|
||||
SELECT row_eq(
|
||||
$$ select primary_owner_user_id, is_personal_account, name from public.accounts order by created_at desc limit 1 $$,
|
||||
ROW (tests.get_supabase_uid('test1'), true, 'test1'::varchar),
|
||||
'Inserting a user should create a personal account when personal accounts are enabled'
|
||||
);
|
||||
|
||||
-- anon users should not be able to see the personal account
|
||||
|
||||
set local role anon;
|
||||
|
||||
SELECT throws_ok(
|
||||
$$ select * from public.accounts order by created_at desc limit 1 $$,
|
||||
'permission denied for schema public'
|
||||
);
|
||||
|
||||
-- the primary owner should be able to see the personal account
|
||||
|
||||
select makerkit.authenticate_as('test1');
|
||||
|
||||
SELECT isnt_empty(
|
||||
$$ select * from public.accounts where primary_owner_user_id = tests.get_supabase_uid('test1') $$,
|
||||
'The primary owner should be able to see the personal account'
|
||||
);
|
||||
|
||||
------------
|
||||
--- Other Users
|
||||
|
||||
-- other users should not be able to see the personal account
|
||||
|
||||
select makerkit.authenticate_as('test2');
|
||||
|
||||
SELECT is_empty(
|
||||
$$ select * from public.accounts where primary_owner_user_id = tests.get_supabase_uid('test1') $$,
|
||||
'Other users should not be able to see the personal account'
|
||||
);
|
||||
|
||||
SELECT *
|
||||
FROM finish();
|
||||
|
||||
ROLLBACK;
|
||||
@@ -0,0 +1,95 @@
|
||||
begin;
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select no_plan();
|
||||
|
||||
select makerkit.set_identifier('primary_owner', 'test@makerkit.dev');
|
||||
select makerkit.set_identifier('owner', 'owner@makerkit.dev');
|
||||
select makerkit.set_identifier('member', 'member@makerkit.dev');
|
||||
select makerkit.set_identifier('custom', 'custom@makerkit.dev');
|
||||
|
||||
INSERT INTO public.billing_customers(account_id, provider, customer_id)
|
||||
VALUES (tests.get_supabase_uid('primary_owner'), 'stripe', 'cus_test');
|
||||
|
||||
-- Call the upsert_order function
|
||||
SELECT public.upsert_order(tests.get_supabase_uid('primary_owner'), 'cus_test', 'order_test', 'pending', 'stripe', 100, 'usd', '[
|
||||
{"id":"order_item_1", "product_id": "prod_test", "variant_id": "var_test", "price_amount": 100, "quantity": 1},
|
||||
{"id":"order_item_2", "product_id": "prod_test", "variant_id": "var_test_2", "price_amount": 100, "quantity": 10}
|
||||
]');
|
||||
|
||||
-- Verify that the order was created correctly
|
||||
SELECT is(
|
||||
(SELECT status FROM public.orders WHERE id = 'order_test'),
|
||||
'pending',
|
||||
'The order status should be pending'
|
||||
);
|
||||
|
||||
-- Verify that the subscription items were created correctly
|
||||
SELECT row_eq(
|
||||
$$ select count(*) from order_items where order_id = 'order_test' $$,
|
||||
row(2::bigint),
|
||||
'The order items should be created'
|
||||
);
|
||||
|
||||
-- Call the upsert_order function again to update the order
|
||||
select public.upsert_order(tests.get_supabase_uid('primary_owner'), 'cus_test', 'order_test', 'succeeded', 'stripe', 100, 'usd', '[
|
||||
{"id":"order_item_1", "product_id": "prod_test_2", "variant_id": "var_test", "price_amount": 100, "quantity": 10}
|
||||
]');
|
||||
|
||||
-- Verify that the order was updated correctly
|
||||
select is(
|
||||
(select status FROM public.orders WHERE id = 'order_test'),
|
||||
'succeeded',
|
||||
'The order status should be succeeded'
|
||||
);
|
||||
|
||||
select row_eq(
|
||||
$$ select quantity from order_items where variant_id = 'var_test' $$,
|
||||
row(10::int),
|
||||
'The order items should be updated'
|
||||
);
|
||||
|
||||
select is_empty(
|
||||
$$ select * from order_items where id = 'order_item_2' $$,
|
||||
'The order item should be deleted when the order is updated'
|
||||
);
|
||||
|
||||
select row_eq(
|
||||
$$ select product_id from order_items where id = 'order_item_1' $$,
|
||||
row('prod_test_2'::text),
|
||||
'The order item should be deleted when the order is updated'
|
||||
);
|
||||
|
||||
select makerkit.authenticate_as('primary_owner');
|
||||
|
||||
-- account can read their own subscription
|
||||
select isnt_empty(
|
||||
$$ select 1 from orders where id = 'order_test' $$,
|
||||
'The account can read their own order'
|
||||
);
|
||||
|
||||
select isnt_empty(
|
||||
$$ select * from order_items where order_id = 'order_test' $$,
|
||||
'The account can read their own orders items'
|
||||
);
|
||||
|
||||
-- foreigners
|
||||
select tests.create_supabase_user('foreigner');
|
||||
select makerkit.authenticate_as('foreigner');
|
||||
|
||||
-- account cannot read other's subscription
|
||||
select is_empty(
|
||||
$$ select 1 from orders where id = 'order_test' $$,
|
||||
'The account cannot read the other account orders'
|
||||
);
|
||||
|
||||
select is_empty(
|
||||
$$ select 1 from order_items where order_id = 'order_test' $$,
|
||||
'The account cannot read the other account order items'
|
||||
);
|
||||
|
||||
-- Finish the tests and clean up
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
begin;
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select no_plan();
|
||||
|
||||
select makerkit.set_identifier('primary_owner', 'test@makerkit.dev');
|
||||
select makerkit.set_identifier('owner', 'owner@makerkit.dev');
|
||||
select makerkit.set_identifier('member', 'member@makerkit.dev');
|
||||
select makerkit.set_identifier('custom', 'custom@makerkit.dev');
|
||||
|
||||
-- Create a test account and billing customer
|
||||
INSERT INTO public.billing_customers(account_id, provider, customer_id)
|
||||
VALUES (tests.get_supabase_uid('primary_owner'), 'stripe', 'cus_test');
|
||||
|
||||
-- Call the upsert_subscription function
|
||||
SELECT public.upsert_subscription(tests.get_supabase_uid('primary_owner'), 'cus_test', 'sub_test', true, 'active', 'stripe', false, 'usd', now(), now() + interval '1 month', '[
|
||||
{
|
||||
"id": "sub_123",
|
||||
"product_id": "prod_test",
|
||||
"variant_id": "var_test",
|
||||
"type": "flat",
|
||||
"price_amount": 1000,
|
||||
"quantity": 1,
|
||||
"interval": "month",
|
||||
"interval_count": 1
|
||||
},
|
||||
{
|
||||
"id": "sub_456",
|
||||
"product_id": "prod_test_2",
|
||||
"variant_id": "var_test_2",
|
||||
"type": "flat",
|
||||
"price_amount": 2000,
|
||||
"quantity": 2,
|
||||
"interval": "month",
|
||||
"interval_count": 1
|
||||
},
|
||||
{
|
||||
"id": "sub_789",
|
||||
"product_id": "prod_test_3",
|
||||
"variant_id": "var_test_3",
|
||||
"type": "flat",
|
||||
"price_amount": 2000,
|
||||
"quantity": 2,
|
||||
"interval": "month",
|
||||
"interval_count": 1
|
||||
}
|
||||
]');
|
||||
|
||||
-- Verify that the subscription items were created correctly
|
||||
SELECT row_eq(
|
||||
$$ select count(*) from subscription_items where subscription_id = 'sub_test' $$,
|
||||
row(3::bigint),
|
||||
'The subscription items should be created'
|
||||
);
|
||||
|
||||
-- Verify that the subscription was created correctly
|
||||
SELECT is(
|
||||
(SELECT active FROM public.subscriptions WHERE id = 'sub_test'),
|
||||
true,
|
||||
'The subscription should be active'
|
||||
);
|
||||
|
||||
SELECT is(
|
||||
(SELECT status FROM public.subscriptions WHERE id = 'sub_test'),
|
||||
'active',
|
||||
'The subscription status should be active'
|
||||
);
|
||||
|
||||
-- Call the upsert_subscription function again to update the subscription
|
||||
SELECT public.upsert_subscription(tests.get_supabase_uid('primary_owner'), 'cus_test', 'sub_test', false, 'past_due', 'stripe', true, 'usd', now(), now() + interval '1 month', '[
|
||||
{
|
||||
"id": "sub_123",
|
||||
"product_id": "prod_test",
|
||||
"variant_id": "var_test",
|
||||
"type": "flat",
|
||||
"price_amount": 2000,
|
||||
"quantity": 1,
|
||||
"interval": "month",
|
||||
"interval_count": 1
|
||||
},
|
||||
{
|
||||
"id": "sub_456",
|
||||
"product_id": "prod_test_3",
|
||||
"variant_id": "var_test_2",
|
||||
"type": "flat",
|
||||
"price_amount": 2000,
|
||||
"quantity": 2,
|
||||
"interval": "year",
|
||||
"interval_count": 12
|
||||
}
|
||||
]');
|
||||
|
||||
-- Verify that the subscription items were updated correctly
|
||||
SELECT row_eq(
|
||||
$$ select price_amount from subscription_items where variant_id = 'var_test' $$,
|
||||
row('2000'::numeric),
|
||||
'The subscription items should be updated'
|
||||
);
|
||||
|
||||
-- Verify that the subscription items were updated correctly
|
||||
SELECT row_eq(
|
||||
$$ select product_id from subscription_items where id = 'sub_456' $$,
|
||||
row('prod_test_3'::varchar),
|
||||
'The subscription items should be updated'
|
||||
);
|
||||
|
||||
-- Verify that the subscription items were updated correctly
|
||||
SELECT row_eq(
|
||||
$$ select interval from subscription_items where variant_id = 'var_test_2' $$,
|
||||
row('year'::varchar),
|
||||
'The subscription items should be updated'
|
||||
);
|
||||
|
||||
-- Verify that the subscription was updated correctly
|
||||
select is(
|
||||
(select active FROM public.subscriptions WHERE id = 'sub_test'),
|
||||
false,
|
||||
'The subscription should be inactive'
|
||||
);
|
||||
|
||||
select is(
|
||||
(select status FROM public.subscriptions WHERE id = 'sub_test'),
|
||||
'past_due',
|
||||
'The subscription status should be past_due'
|
||||
);
|
||||
|
||||
select isnt_empty(
|
||||
$$ select * from public.subscription_items where subscription_id = 'sub_test' $$,
|
||||
'The account can read their own subscription items'
|
||||
);
|
||||
|
||||
select is_empty(
|
||||
$$ select * from public.subscription_items where subscription_id = 'sub_test' and variant_id = 'var_test_3' $$,
|
||||
'The subscription items should be deleted when the subscription is updated and the item is missing'
|
||||
);
|
||||
|
||||
-- Call the upsert_subscription function again to update the subscription
|
||||
select public.upsert_subscription(tests.get_supabase_uid('primary_owner'), 'cus_test', 'sub_test', true, 'active', 'stripe', false, 'usd', now(), now() + interval '1 month', '[]');
|
||||
|
||||
-- Verify that the subscription was updated correctly
|
||||
select is(
|
||||
(select active FROM public.subscriptions WHERE id = 'sub_test'),
|
||||
true,
|
||||
'The subscription should be active'
|
||||
);
|
||||
|
||||
select makerkit.authenticate_as('primary_owner');
|
||||
|
||||
-- account can read their own subscription
|
||||
select isnt_empty(
|
||||
$$ select 1 from subscriptions where id = 'sub_test' $$,
|
||||
'The account can read their own subscription'
|
||||
);
|
||||
|
||||
select is_empty(
|
||||
$$ select * from subscription_items where subscription_id = 'sub_test' $$,
|
||||
'No subscription items should be returned when the subscription is empty'
|
||||
);
|
||||
|
||||
-- users cannot manually update subscriptions
|
||||
select throws_ok(
|
||||
$$ select public.upsert_subscription(tests.get_supabase_uid('primary_owner'), 'cus_test', 'sub_test', true, 'active', 'stripe', false, 'usd', now(), now() + interval '1 month', '[]') $$,
|
||||
'permission denied for function upsert_subscription'
|
||||
);
|
||||
|
||||
select is(
|
||||
(public.has_active_subscription(tests.get_supabase_uid('primary_owner'))),
|
||||
true,
|
||||
'The function public.has_active_subscription should return true when the account has a subscription'
|
||||
);
|
||||
|
||||
-- foreigners
|
||||
select tests.create_supabase_user('foreigner');
|
||||
select makerkit.authenticate_as('foreigner');
|
||||
|
||||
-- account cannot read other's subscription
|
||||
select is_empty(
|
||||
$$ select 1 from subscriptions where id = 'sub_test' $$,
|
||||
'The account cannot read the other account subscriptions'
|
||||
);
|
||||
|
||||
select is_empty(
|
||||
$$ select 1 from subscription_items where subscription_id = 'sub_test' $$,
|
||||
'The account cannot read the other account subscription items'
|
||||
);
|
||||
|
||||
select is(
|
||||
(public.has_active_subscription(tests.get_supabase_uid('primary_owner'))),
|
||||
false,
|
||||
'The function public.has_active_subscription should return false when a foreigner is querying the account subscription'
|
||||
);
|
||||
|
||||
-- Finish the tests and clean up
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
57
supabase copy/tests/database/schema-conditions.test.sql
Normal file
57
supabase copy/tests/database/schema-conditions.test.sql
Normal file
@@ -0,0 +1,57 @@
|
||||
begin;
|
||||
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select
|
||||
no_plan();
|
||||
|
||||
CREATE OR REPLACE FUNCTION check_schema_conditions()
|
||||
RETURNS void AS
|
||||
$$
|
||||
DECLARE
|
||||
_table RECORD;
|
||||
_column RECORD;
|
||||
columnCheckCount INTEGER;
|
||||
BEGIN
|
||||
FOR _table IN (SELECT tablename FROM pg_tables WHERE schemaname = 'public')
|
||||
LOOP
|
||||
-- 1. Check if every table has RLS enabled
|
||||
IF (
|
||||
SELECT relrowsecurity FROM pg_class
|
||||
INNER JOIN pg_namespace n ON n.oid = pg_class.relnamespace
|
||||
WHERE n.nspname = 'public' AND relname = _table.tablename
|
||||
) IS FALSE THEN
|
||||
RAISE EXCEPTION 'Table "%" does not have RLS enabled.', _table.tablename;
|
||||
END IF;
|
||||
|
||||
-- 2. Check that every text column in the current table has a constraint
|
||||
FOR _column IN (SELECT column_name FROM information_schema.columns WHERE table_schema = 'public' AND table_name = _table.tablename AND data_type = 'text')
|
||||
LOOP
|
||||
SELECT COUNT(*)
|
||||
INTO columnCheckCount
|
||||
FROM information_schema.constraint_column_usage
|
||||
WHERE table_schema = 'public' AND table_name = _table.tablename AND column_name = _column.column_name;
|
||||
|
||||
IF columnCheckCount = 0 THEN
|
||||
RAISE NOTICE 'Text column "%.%" does not have a constraint
|
||||
.',
|
||||
_table.tablename, _column.column_name;
|
||||
END IF;
|
||||
END LOOP;
|
||||
END LOOP;
|
||||
|
||||
RAISE NOTICE 'Schema check completed.';
|
||||
END
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
select lives_ok($$
|
||||
select
|
||||
check_schema_conditions();
|
||||
$$, 'check_schema_conditions()');
|
||||
|
||||
select
|
||||
*
|
||||
from
|
||||
finish();
|
||||
|
||||
rollback;
|
||||
52
supabase copy/tests/database/schema.test.sql
Normal file
52
supabase copy/tests/database/schema.test.sql
Normal file
@@ -0,0 +1,52 @@
|
||||
BEGIN;
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select no_plan();
|
||||
|
||||
select has_table('public', 'config', 'Makerkit config table should exist');
|
||||
select has_table('public', 'accounts', 'Makerkit accounts table should exist');
|
||||
select has_table('public', 'accounts_memberships', 'Makerkit account_users table should exist');
|
||||
select has_table('public', 'invitations', 'Makerkit invitations table should exist');
|
||||
select has_table('public', 'billing_customers', 'Makerkit billing_customers table should exist');
|
||||
select has_table('public', 'subscriptions', 'Makerkit subscriptions table should exist');
|
||||
select has_table('public', 'subscription_items', 'Makerkit subscription_items table should exist');
|
||||
select has_table('public', 'orders', 'Makerkit orders table should exist');
|
||||
select has_table('public', 'order_items', 'Makerkit order_items table should exist');
|
||||
select has_table('public', 'roles', 'Makerkit roles table should exist');
|
||||
select has_table('public', 'role_permissions', 'Makerkit roles_permissions table should exist');
|
||||
|
||||
select tests.rls_enabled('public', 'config');
|
||||
select tests.rls_enabled('public', 'accounts');
|
||||
select tests.rls_enabled('public', 'accounts_memberships');
|
||||
select tests.rls_enabled('public', 'invitations');
|
||||
select tests.rls_enabled('public', 'billing_customers');
|
||||
select tests.rls_enabled('public', 'subscriptions');
|
||||
select tests.rls_enabled('public', 'subscription_items');
|
||||
select tests.rls_enabled('public', 'orders');
|
||||
select tests.rls_enabled('public', 'order_items');
|
||||
select tests.rls_enabled('public', 'roles');
|
||||
select tests.rls_enabled('public', 'role_permissions');
|
||||
|
||||
SELECT schema_privs_are('public', 'anon', Array [NULL], 'Anon should not have access to public schema');
|
||||
|
||||
-- set the role to anonymous for verifying access tests
|
||||
set role anon;
|
||||
select throws_ok('select public.get_config()');
|
||||
select throws_ok('select public.is_set(''enable_team_accounts'')');
|
||||
|
||||
-- set the role to the service_role for testing access
|
||||
set role service_role;
|
||||
select ok(public.get_config() is not null),
|
||||
'Makerkit get_config should be accessible to the service role';
|
||||
|
||||
-- set the role to authenticated for tests
|
||||
set role authenticated;
|
||||
select ok(public.get_config() is not null), 'Makerkit get_config should be accessible to authenticated users';
|
||||
select ok(public.is_set('enable_team_accounts')),
|
||||
'Makerkit is_set should be accessible to authenticated users';
|
||||
select isnt_empty('select * from public.config', 'authenticated users should have access to Makerkit config');
|
||||
|
||||
SELECT *
|
||||
FROM finish();
|
||||
|
||||
ROLLBACK;
|
||||
122
supabase copy/tests/database/storage.test.sql
Normal file
122
supabase copy/tests/database/storage.test.sql
Normal file
@@ -0,0 +1,122 @@
|
||||
begin;
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select no_plan();
|
||||
|
||||
select makerkit.set_identifier('primary_owner', 'test@makerkit.dev');
|
||||
select makerkit.set_identifier('owner', 'owner@makerkit.dev');
|
||||
select makerkit.set_identifier('member', 'member@makerkit.dev');
|
||||
select makerkit.set_identifier('custom', 'custom@makerkit.dev');
|
||||
|
||||
select makerkit.authenticate_as('member');
|
||||
|
||||
select throws_ok(
|
||||
$$ insert into storage.objects ("bucket_id", "metadata", "name", "owner", "owner_id", "version") values
|
||||
('account_image', '{"key": "value"}', tests.get_supabase_uid('primary_owner'), tests.get_supabase_uid('primary_owner'), tests.get_supabase_uid('primary_owner'), 1); $$,
|
||||
'new row violates row-level security policy for table "objects"'
|
||||
);
|
||||
|
||||
select makerkit.authenticate_as('primary_owner');
|
||||
|
||||
select lives_ok(
|
||||
$$ insert into storage.objects ("bucket_id", "metadata", "name", "owner", "owner_id", "version") values
|
||||
('account_image', '{"key": "value"}', tests.get_supabase_uid('primary_owner'), tests.get_supabase_uid('primary_owner'), tests.get_supabase_uid('primary_owner'), 1); $$,
|
||||
'The owner should be able to insert a new object'
|
||||
);
|
||||
|
||||
select isnt_empty(
|
||||
$$ select * from storage.objects where owner = tests.get_supabase_uid('primary_owner') $$,
|
||||
'The object should be inserted'
|
||||
);
|
||||
|
||||
select makerkit.authenticate_as('owner');
|
||||
|
||||
select is_empty(
|
||||
$$ select * from storage.objects where owner = tests.get_supabase_uid('primary_owner') $$,
|
||||
'The owner should not be able to see the object'
|
||||
);
|
||||
|
||||
-- create a new bucket
|
||||
--
|
||||
set local role postgres;
|
||||
|
||||
select lives_ok(
|
||||
$$ insert into storage.buckets ("name", "id", public) values ('new_bucket', 'new_bucket', true); $$
|
||||
);
|
||||
|
||||
-- we create a mock policy allowing only the primary_owner to access the new bucket
|
||||
-- this is a mock policy to check the existing policy system does not interfere with the new bucket
|
||||
create policy new_bucket_policy on storage.objects for all using (
|
||||
bucket_id = 'new_bucket'
|
||||
and auth.uid() = tests.get_supabase_uid('primary_owner')
|
||||
)
|
||||
with check (
|
||||
bucket_id = 'new_bucket'
|
||||
and auth.uid() = tests.get_supabase_uid('primary_owner')
|
||||
);
|
||||
|
||||
select makerkit.authenticate_as('member');
|
||||
|
||||
-- user should not be able to insert into the new bucket according to the new policy
|
||||
select throws_ok(
|
||||
$$ insert into storage.objects ("bucket_id", "metadata", "name", "owner", "owner_id", "version") values
|
||||
('new_bucket', '{"key": "value"}', 'some name', tests.get_supabase_uid('primary_owner'), tests.get_supabase_uid('primary_owner'), 1); $$,
|
||||
'new row violates row-level security policy for table "objects"'
|
||||
);
|
||||
|
||||
select makerkit.authenticate_as('primary_owner');
|
||||
|
||||
-- primary_owner should be able to insert into the new bucket according to the new policy
|
||||
-- this is to check the new policy system is working
|
||||
--
|
||||
select lives_ok(
|
||||
$$ insert into storage.objects ("bucket_id", "metadata", "name", "owner", "owner_id", "version") values
|
||||
('new_bucket', '{"key": "value"}', 'some name', tests.get_supabase_uid('primary_owner'), tests.get_supabase_uid('primary_owner'), 1); $$,
|
||||
'new row violates row-level security policy for table "objects"'
|
||||
);
|
||||
|
||||
set local role postgres;
|
||||
|
||||
-- create a new bucket with a custom policy
|
||||
--
|
||||
create policy new_custom_bucket_policy on storage.objects for all using (
|
||||
bucket_id = 'new_bucket'
|
||||
and auth.uid() = tests.get_supabase_uid('owner')
|
||||
)
|
||||
with check (
|
||||
bucket_id = 'new_bucket'
|
||||
and auth.uid() = tests.get_supabase_uid('owner')
|
||||
);
|
||||
|
||||
select makerkit.authenticate_as('owner');
|
||||
|
||||
-- insert a new object into the new bucket
|
||||
--
|
||||
select lives_ok(
|
||||
$$ insert into storage.objects ("bucket_id", "metadata", "name", "owner", "owner_id", "version") values
|
||||
('new_bucket', '{"key": "value"}', 'some name 2', tests.get_supabase_uid('primary_owner'), tests.get_supabase_uid('primary_owner'), 1); $$,
|
||||
'The primary_owner should be able to insert a new object into the new bucket'
|
||||
);
|
||||
|
||||
-- check the object is inserted
|
||||
--
|
||||
select isnt_empty(
|
||||
$$ select * from storage.objects where bucket_id = 'new_bucket' $$,
|
||||
'The object should be inserted into the new bucket'
|
||||
);
|
||||
|
||||
-- check other members cannot insert into the new bucket
|
||||
select makerkit.authenticate_as('member');
|
||||
|
||||
select throws_ok(
|
||||
$$ insert into storage.objects ("bucket_id", "metadata", "name", "owner", "owner_id", "version") values
|
||||
('new_bucket', '{"key": "value"}', 'some other name', tests.get_supabase_uid('primary_owner'), tests.get_supabase_uid('primary_owner'), 1); $$,
|
||||
'new row violates row-level security policy for table "objects"'
|
||||
);
|
||||
|
||||
select
|
||||
*
|
||||
from
|
||||
finish();
|
||||
|
||||
rollback;
|
||||
84
supabase copy/tests/database/super-admin-edge-cases.test.sql
Normal file
84
supabase copy/tests/database/super-admin-edge-cases.test.sql
Normal file
@@ -0,0 +1,84 @@
|
||||
begin;
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select no_plan();
|
||||
|
||||
-- Create test users for different scenarios
|
||||
select tests.create_supabase_user('transitioning_admin');
|
||||
select tests.create_supabase_user('revoking_mfa_admin');
|
||||
select tests.create_supabase_user('concurrent_session_user');
|
||||
|
||||
-- Set up test users
|
||||
select makerkit.set_identifier('transitioning_admin', 'transitioning@makerkit.dev');
|
||||
select makerkit.set_identifier('revoking_mfa_admin', 'revoking@makerkit.dev');
|
||||
select makerkit.set_identifier('concurrent_session_user', 'concurrent@makerkit.dev');
|
||||
|
||||
-- Test 1: Role Transition Scenarios
|
||||
select makerkit.authenticate_as('transitioning_admin');
|
||||
select makerkit.set_mfa_factor();
|
||||
select makerkit.set_session_aal('aal2');
|
||||
|
||||
-- Initially not a super admin
|
||||
select is(
|
||||
(select public.is_super_admin()),
|
||||
false,
|
||||
'User should not be super admin initially'
|
||||
);
|
||||
|
||||
-- Grant super admin
|
||||
select makerkit.set_super_admin();
|
||||
|
||||
select is(
|
||||
(select public.is_super_admin()),
|
||||
true,
|
||||
'User should now be super admin'
|
||||
);
|
||||
|
||||
-- Test 2: MFA Revocation Scenarios
|
||||
select makerkit.authenticate_as('revoking_mfa_admin');
|
||||
select makerkit.set_mfa_factor();
|
||||
select makerkit.set_session_aal('aal2');
|
||||
select makerkit.set_super_admin();
|
||||
|
||||
-- Initially has super admin access
|
||||
select is(
|
||||
(select public.is_super_admin()),
|
||||
true,
|
||||
'Admin should have super admin access initially'
|
||||
);
|
||||
|
||||
-- Simulate MFA revocation by setting AAL1
|
||||
select makerkit.set_session_aal('aal1');
|
||||
|
||||
select is(
|
||||
(select public.is_super_admin()),
|
||||
false,
|
||||
'Admin should lose super admin access when MFA is revoked'
|
||||
);
|
||||
|
||||
-- Test 3: Concurrent Session Management
|
||||
select makerkit.authenticate_as('concurrent_session_user');
|
||||
select makerkit.set_mfa_factor();
|
||||
select makerkit.set_session_aal('aal2');
|
||||
select makerkit.set_super_admin();
|
||||
|
||||
-- Test access with AAL2
|
||||
select is(
|
||||
(select public.is_super_admin()),
|
||||
true,
|
||||
'Should have super admin access with AAL2'
|
||||
);
|
||||
|
||||
-- Simulate different session with AAL1
|
||||
select makerkit.set_session_aal('aal1');
|
||||
|
||||
select is(
|
||||
(select public.is_super_admin()),
|
||||
false,
|
||||
'Should not have super admin access with AAL1 even if other session has AAL2'
|
||||
);
|
||||
|
||||
-- Finish the tests and clean up
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
210
supabase copy/tests/database/super-admin.test.sql
Normal file
210
supabase copy/tests/database/super-admin.test.sql
Normal file
@@ -0,0 +1,210 @@
|
||||
begin;
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select no_plan();
|
||||
|
||||
-- Create Users
|
||||
select tests.create_supabase_user('super_admin');
|
||||
select tests.create_supabase_user('regular_user');
|
||||
select tests.create_supabase_user('mfa_user');
|
||||
select tests.create_supabase_user('malicious_user');
|
||||
select tests.create_supabase_user('partial_mfa_user');
|
||||
|
||||
-- Set up test users
|
||||
select makerkit.set_identifier('super_admin', 'super@makerkit.dev');
|
||||
select makerkit.set_identifier('regular_user', 'regular@makerkit.dev');
|
||||
select makerkit.set_identifier('mfa_user', 'mfa@makerkit.dev');
|
||||
select makerkit.set_identifier('malicious_user', 'malicious@makerkit.dev');
|
||||
select makerkit.set_identifier('partial_mfa_user', 'partial@makerkit.dev');
|
||||
|
||||
-- Test is_aal2 function
|
||||
set local role postgres;
|
||||
|
||||
create or replace function makerkit.setup_super_admin() returns void as $$
|
||||
begin
|
||||
perform makerkit.authenticate_as('super_admin');
|
||||
perform makerkit.set_mfa_factor();
|
||||
perform makerkit.set_session_aal('aal2');
|
||||
perform makerkit.set_super_admin();
|
||||
end $$ language plpgsql;
|
||||
|
||||
-- Test super admin with AAL2
|
||||
select makerkit.setup_super_admin();
|
||||
|
||||
select is(
|
||||
(select public.is_aal2()),
|
||||
true,
|
||||
'Super admin should have AAL2 authentication'
|
||||
);
|
||||
|
||||
select is(
|
||||
(select public.is_super_admin()),
|
||||
true,
|
||||
'User should be identified as super admin'
|
||||
);
|
||||
|
||||
-- Test regular user (no AAL2)
|
||||
select makerkit.authenticate_as('regular_user');
|
||||
|
||||
select is(
|
||||
(select public.is_aal2()),
|
||||
false,
|
||||
'Regular user should not have AAL2 authentication'
|
||||
);
|
||||
|
||||
select is(
|
||||
(select public.is_super_admin()),
|
||||
false,
|
||||
'Regular user should not be identified as super admin'
|
||||
);
|
||||
|
||||
-- Test MFA compliance
|
||||
set local role postgres;
|
||||
|
||||
select is(
|
||||
(select public.is_super_admin()),
|
||||
false,
|
||||
'Postgres user should not be identified as super admin'
|
||||
);
|
||||
|
||||
select makerkit.authenticate_as('mfa_user');
|
||||
select makerkit.set_mfa_factor();
|
||||
select makerkit.set_session_aal('aal2');
|
||||
|
||||
select is(
|
||||
(select public.is_mfa_compliant()),
|
||||
true,
|
||||
'User with verified MFA should be MFA compliant because it is optional'
|
||||
);
|
||||
|
||||
-- Test super admin access to protected tables
|
||||
select makerkit.setup_super_admin();
|
||||
|
||||
-- Test malicious user attempts
|
||||
select makerkit.authenticate_as('malicious_user');
|
||||
|
||||
-- Attempt to fake super admin role (should fail)
|
||||
select is(
|
||||
(select public.is_super_admin()),
|
||||
false,
|
||||
'Malicious user cannot fake super admin role'
|
||||
);
|
||||
|
||||
-- Test access to protected tables (should be restricted)
|
||||
select is_empty(
|
||||
$$ select * from public.accounts where id != auth.uid() $$,
|
||||
'Malicious user should not access other accounts'
|
||||
);
|
||||
|
||||
select is_empty(
|
||||
$$ select * from public.accounts_memberships where user_id != auth.uid() $$,
|
||||
'Malicious user should not access other memberships'
|
||||
);
|
||||
|
||||
select is_empty(
|
||||
$$ select * from public.subscriptions where account_id != auth.uid() $$,
|
||||
'Malicious user should not access other subscriptions'
|
||||
);
|
||||
|
||||
-- Test partial MFA setup (not verified)
|
||||
select makerkit.authenticate_as('partial_mfa_user');
|
||||
select makerkit.set_session_aal('aal2');
|
||||
|
||||
-- Test regular user restricted access
|
||||
select makerkit.authenticate_as('regular_user');
|
||||
|
||||
-- Test MFA restrictions
|
||||
select makerkit.authenticate_as('regular_user');
|
||||
select makerkit.set_mfa_factor();
|
||||
|
||||
-- Should be restricted without MFA
|
||||
select is_empty(
|
||||
$$ select * from public.accounts $$,
|
||||
'Regular user without MFA should not access accounts when MFA is required'
|
||||
);
|
||||
|
||||
-- A super admin without MFA should not be able to have super admin rights
|
||||
select makerkit.authenticate_as('super_admin');
|
||||
select makerkit.set_super_admin();
|
||||
|
||||
select is(
|
||||
(select public.is_super_admin()),
|
||||
false,
|
||||
'Super admin without MFA should not be able to have super admin rights'
|
||||
);
|
||||
|
||||
-- Test edge cases for MFA and AAL2
|
||||
select makerkit.authenticate_as('mfa_user');
|
||||
select makerkit.set_mfa_factor();
|
||||
-- Set AAL1 despite having MFA to test edge case
|
||||
select makerkit.set_session_aal('aal1');
|
||||
|
||||
select is(
|
||||
(select public.is_mfa_compliant()),
|
||||
false,
|
||||
'User with MFA but AAL1 session should not be MFA compliant'
|
||||
);
|
||||
|
||||
select is_empty(
|
||||
$$ select * from public.accounts $$,
|
||||
'Non-compliant MFA should not be able to read any accounts'
|
||||
);
|
||||
|
||||
select is_empty(
|
||||
$$ select * from public.accounts_memberships $$,
|
||||
'Non-compliant MFA should not be able to read any memberships'
|
||||
);
|
||||
|
||||
-- A Super Admin should be able to access all tables when MFA is enabled
|
||||
select makerkit.setup_super_admin();
|
||||
|
||||
select is(
|
||||
(select public.is_super_admin()),
|
||||
true,
|
||||
'Super admin has super admin rights'
|
||||
);
|
||||
|
||||
-- Test comprehensive access for super admin
|
||||
select isnt_empty(
|
||||
$$ select * from public.accounts where id = tests.get_supabase_uid('regular_user') $$,
|
||||
'Super admin should be able to access all accounts'
|
||||
);
|
||||
|
||||
do $$
|
||||
begin
|
||||
delete from public.accounts where id = tests.get_supabase_uid('regular_user');
|
||||
end $$;
|
||||
|
||||
-- A Super admin cannot delete accounts directly
|
||||
select isnt_empty(
|
||||
$$ select * from public.accounts where id = tests.get_supabase_uid('regular_user') $$,
|
||||
'Super admin should not be able to delete data directly'
|
||||
);
|
||||
|
||||
set local role postgres;
|
||||
|
||||
-- update the account name to be able to test the update
|
||||
do $$
|
||||
begin
|
||||
update public.accounts set name = 'Regular User' where id = tests.get_supabase_uid('regular_user');
|
||||
end $$;
|
||||
|
||||
-- re-authenticate as super admin
|
||||
select makerkit.setup_super_admin();
|
||||
|
||||
-- test a super admin cannot update accounts directly
|
||||
do $$
|
||||
begin
|
||||
update public.accounts set name = 'Super Admin' where id = tests.get_supabase_uid('regular_user');
|
||||
end $$;
|
||||
|
||||
select row_eq(
|
||||
$$ select name from public.accounts where id = tests.get_supabase_uid('regular_user') $$,
|
||||
row('Regular User'::varchar),
|
||||
'Super admin should not be able to update data directly'
|
||||
);
|
||||
|
||||
-- Finish the tests and clean up
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
775
supabase copy/tests/database/team-accounts.test.sql
Normal file
775
supabase copy/tests/database/team-accounts.test.sql
Normal file
@@ -0,0 +1,775 @@
|
||||
begin;
|
||||
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select
|
||||
no_plan();
|
||||
|
||||
--- we insert a user into auth.users and return the id into user_id to use
|
||||
select
|
||||
tests.create_supabase_user('test1', 'test1@test.com');
|
||||
|
||||
select
|
||||
tests.create_supabase_user('test2');
|
||||
|
||||
-- Create an team account
|
||||
select
|
||||
makerkit.authenticate_as('test1');
|
||||
|
||||
select
|
||||
public.create_team_account('Test');
|
||||
|
||||
select
|
||||
row_eq($$
|
||||
select
|
||||
primary_owner_user_id, is_personal_account, slug, name
|
||||
from makerkit.get_account_by_slug('test') $$,
|
||||
row (tests.get_supabase_uid('test1'), false,
|
||||
'test'::text, 'Test'::varchar),
|
||||
'Users can create a team account');
|
||||
|
||||
-- Should be the primary owner of the team account by default
|
||||
select
|
||||
row_eq($$
|
||||
select
|
||||
account_role from public.accounts_memberships
|
||||
where
|
||||
account_id =(
|
||||
select
|
||||
id
|
||||
from public.accounts
|
||||
where
|
||||
slug = 'test')
|
||||
and user_id = tests.get_supabase_uid('test1')
|
||||
$$, row ('owner'::varchar),
|
||||
'The primary owner should have the owner role for the team account');
|
||||
|
||||
select is(
|
||||
public.is_account_owner((select
|
||||
id
|
||||
from public.accounts
|
||||
where
|
||||
slug = 'test')),
|
||||
true,
|
||||
'The current user should be the owner of the team account'
|
||||
);
|
||||
|
||||
-- Should be able to see the team account
|
||||
select
|
||||
isnt_empty($$
|
||||
select
|
||||
* from public.accounts
|
||||
where
|
||||
primary_owner_user_id =
|
||||
tests.get_supabase_uid('test1') $$,
|
||||
'The primary owner should be able to see the team account');
|
||||
|
||||
-- Others should not be able to see the team account
|
||||
select
|
||||
makerkit.authenticate_as('test2');
|
||||
|
||||
select is(
|
||||
public.is_account_owner((select
|
||||
id
|
||||
from public.accounts
|
||||
where
|
||||
slug = 'test')),
|
||||
false,
|
||||
'The current user should not be the owner of the team account'
|
||||
);
|
||||
|
||||
select
|
||||
is_empty($$
|
||||
select
|
||||
* from public.accounts
|
||||
where
|
||||
primary_owner_user_id =
|
||||
tests.get_supabase_uid('test1') $$,
|
||||
'Other users should not be able to see the team account');
|
||||
|
||||
-- should not have any role for the team account
|
||||
select
|
||||
is (public.has_role_on_account((
|
||||
select
|
||||
id
|
||||
from makerkit.get_account_by_slug('test'))),
|
||||
false,
|
||||
'Foreign users should not have any role for the team account');
|
||||
|
||||
-- enforcing a single team account per owner using a trigger when
|
||||
-- inserting a team
|
||||
set local role postgres;
|
||||
|
||||
create or replace function kit.single_account_per_owner()
|
||||
returns trigger
|
||||
as $$
|
||||
declare
|
||||
total_accounts int;
|
||||
begin
|
||||
select
|
||||
count(id)
|
||||
from
|
||||
public.accounts
|
||||
where
|
||||
primary_owner_user_id = auth.uid() into total_accounts;
|
||||
|
||||
if total_accounts > 0 then
|
||||
raise exception 'User can only own 1 account';
|
||||
end if;
|
||||
|
||||
return NEW;
|
||||
|
||||
end
|
||||
$$
|
||||
language plpgsql
|
||||
set search_path = '';
|
||||
|
||||
-- trigger to protect account fields
|
||||
create trigger single_account_per_owner
|
||||
before insert on public.accounts for each row
|
||||
execute function kit.single_account_per_owner();
|
||||
|
||||
-- Create an team account
|
||||
select
|
||||
makerkit.authenticate_as('test1');
|
||||
|
||||
select
|
||||
throws_ok(
|
||||
$$ select
|
||||
public.create_team_account('Test2') $$, 'User can only own 1 account');
|
||||
|
||||
set local role postgres;
|
||||
|
||||
drop trigger single_account_per_owner on public.accounts;
|
||||
|
||||
-- Test that a member cannot update another account in the same team
|
||||
-- Using completely new users for update tests
|
||||
select
|
||||
tests.create_supabase_user('updatetest1', 'updatetest1@test.com');
|
||||
|
||||
select
|
||||
tests.create_supabase_user('updatetest2', 'updatetest2@test.com');
|
||||
|
||||
-- Create a team account for update tests
|
||||
select
|
||||
makerkit.authenticate_as('updatetest1');
|
||||
|
||||
select
|
||||
public.create_team_account('UpdateTeam');
|
||||
|
||||
-- Add updatetest2 as a member
|
||||
set local role postgres;
|
||||
|
||||
insert into public.accounts_memberships (account_id, user_id, account_role)
|
||||
values (
|
||||
(select id from makerkit.get_account_by_slug('updateteam')),
|
||||
tests.get_supabase_uid('updatetest2'),
|
||||
'member'
|
||||
);
|
||||
|
||||
-- Verify updatetest2 is now a member
|
||||
select
|
||||
makerkit.authenticate_as('updatetest1');
|
||||
|
||||
select
|
||||
row_eq($$
|
||||
select
|
||||
account_role from public.accounts_memberships
|
||||
where
|
||||
account_id = (select id from makerkit.get_account_by_slug('updateteam'))
|
||||
and user_id = tests.get_supabase_uid('updatetest2')
|
||||
$$,
|
||||
row ('member'::varchar),
|
||||
'updatetest2 should be a member of the team account'
|
||||
);
|
||||
|
||||
-- Store original values to verify they don't change
|
||||
select
|
||||
row_eq($$
|
||||
select name, primary_owner_user_id from public.accounts
|
||||
where id = (select id from makerkit.get_account_by_slug('updateteam'))
|
||||
$$,
|
||||
row ('UpdateTeam'::varchar, tests.get_supabase_uid('updatetest1')),
|
||||
'Original values before attempted updates'
|
||||
);
|
||||
|
||||
-- Add team account to updatetest2's visibility (so they can try to perform operations)
|
||||
select
|
||||
makerkit.authenticate_as('updatetest2');
|
||||
|
||||
-- First verify that as a member, updatetest2 can now see the account
|
||||
select
|
||||
isnt_empty($$
|
||||
select
|
||||
* from public.accounts
|
||||
where id = (select id from makerkit.get_account_by_slug('updateteam'))
|
||||
$$,
|
||||
'Team member should be able to see the team account'
|
||||
);
|
||||
|
||||
-- Try to update the team name - without checking for exception
|
||||
select
|
||||
lives_ok($$
|
||||
update public.accounts
|
||||
set name = 'Updated Team Name'
|
||||
where id = (select id from makerkit.get_account_by_slug('updateteam'))
|
||||
$$,
|
||||
'Non-owner member update attempt should not crash'
|
||||
);
|
||||
|
||||
-- Try to update primary owner without checking for exception
|
||||
select
|
||||
lives_ok($$
|
||||
update public.accounts
|
||||
set primary_owner_user_id = tests.get_supabase_uid('updatetest2')
|
||||
where id = (select id from makerkit.get_account_by_slug('updateteam'))
|
||||
$$,
|
||||
'Non-owner member update of primary owner attempt should not crash'
|
||||
);
|
||||
|
||||
-- Verify the values have not changed by checking in both updatetest1 and updatetest2 sessions
|
||||
-- First check as updatetest2 (the member)
|
||||
select
|
||||
row_eq($$
|
||||
select name, primary_owner_user_id from public.accounts
|
||||
where id = (select id from makerkit.get_account_by_slug('updateteam'))
|
||||
$$,
|
||||
row ('UpdateTeam'::varchar, tests.get_supabase_uid('updatetest1')),
|
||||
'Values should remain unchanged after member update attempt (member perspective)'
|
||||
);
|
||||
|
||||
-- Now verify as updatetest1 (the owner)
|
||||
select
|
||||
makerkit.authenticate_as('updatetest1');
|
||||
|
||||
select
|
||||
row_eq($$
|
||||
select name, primary_owner_user_id from public.accounts
|
||||
where id = (select id from makerkit.get_account_by_slug('updateteam'))
|
||||
$$,
|
||||
row ('UpdateTeam'::varchar, tests.get_supabase_uid('updatetest1')),
|
||||
'Values should remain unchanged after member update attempt (owner perspective)'
|
||||
);
|
||||
|
||||
-- Test role escalation prevention with completely new users
|
||||
select
|
||||
tests.create_supabase_user('roletest1', 'roletest1@test.com');
|
||||
|
||||
select
|
||||
tests.create_supabase_user('roletest2', 'roletest2@test.com');
|
||||
|
||||
-- Create a team account for role tests
|
||||
select
|
||||
makerkit.authenticate_as('roletest1');
|
||||
|
||||
select
|
||||
public.create_team_account('RoleTeam');
|
||||
|
||||
-- Add roletest2 as a member
|
||||
set local role postgres;
|
||||
|
||||
insert into public.accounts_memberships (account_id, user_id, account_role)
|
||||
values (
|
||||
(select id from makerkit.get_account_by_slug('roleteam')),
|
||||
tests.get_supabase_uid('roletest2'),
|
||||
'member'
|
||||
);
|
||||
|
||||
-- Test role escalation prevention: a member cannot promote themselves to owner
|
||||
select
|
||||
makerkit.authenticate_as('roletest2');
|
||||
|
||||
-- Try to update own role to owner
|
||||
select
|
||||
lives_ok($$
|
||||
update public.accounts_memberships
|
||||
set account_role = 'owner'
|
||||
where account_id = (select id from makerkit.get_account_by_slug('roleteam'))
|
||||
and user_id = tests.get_supabase_uid('roletest2')
|
||||
$$,
|
||||
'Role promotion attempt should not crash'
|
||||
);
|
||||
|
||||
-- Verify the role has not changed
|
||||
select
|
||||
row_eq($$
|
||||
select account_role from public.accounts_memberships
|
||||
where account_id = (select id from makerkit.get_account_by_slug('roleteam'))
|
||||
and user_id = tests.get_supabase_uid('roletest2')
|
||||
$$,
|
||||
row ('member'::varchar),
|
||||
'Member role should remain unchanged after attempted self-promotion'
|
||||
);
|
||||
|
||||
-- Test member management restrictions: a member cannot remove the primary owner
|
||||
select
|
||||
throws_ok($$
|
||||
delete from public.accounts_memberships
|
||||
where account_id = (select id from makerkit.get_account_by_slug('roleteam'))
|
||||
and user_id = tests.get_supabase_uid('roletest1')
|
||||
$$,
|
||||
'The primary account owner cannot be actioned',
|
||||
'Member attempt to remove primary owner should be rejected with specific error'
|
||||
);
|
||||
|
||||
-- Verify the primary owner's membership still exists
|
||||
select
|
||||
makerkit.authenticate_as('roletest1');
|
||||
|
||||
select
|
||||
isnt_empty($$
|
||||
select * from public.accounts_memberships
|
||||
where account_id = (select id from makerkit.get_account_by_slug('roleteam'))
|
||||
and user_id = tests.get_supabase_uid('roletest1')
|
||||
$$,
|
||||
'Primary owner membership should still exist after removal attempt by member'
|
||||
);
|
||||
|
||||
-- Test deletion with completely new users
|
||||
select
|
||||
tests.create_supabase_user('deletetest1', 'deletetest1@test.com');
|
||||
|
||||
select
|
||||
tests.create_supabase_user('deletetest2', 'deletetest2@test.com');
|
||||
|
||||
-- Create a team account for delete tests
|
||||
select
|
||||
makerkit.authenticate_as('deletetest1');
|
||||
|
||||
select
|
||||
public.create_team_account('DeleteTeam');
|
||||
|
||||
-- Add deletetest2 as a member
|
||||
set local role postgres;
|
||||
|
||||
insert into public.accounts_memberships (account_id, user_id, account_role)
|
||||
values (
|
||||
(select id from makerkit.get_account_by_slug('deleteteam')),
|
||||
tests.get_supabase_uid('deletetest2'),
|
||||
'member'
|
||||
);
|
||||
|
||||
-- Test Delete Team Account
|
||||
select
|
||||
makerkit.authenticate_as('deletetest2');
|
||||
|
||||
-- deletion don't throw an error
|
||||
select lives_ok(
|
||||
$$ delete from public.accounts where id = (select id from makerkit.get_account_by_slug('deleteteam')) $$,
|
||||
'Non-owner member deletion attempt should not crash'
|
||||
);
|
||||
|
||||
select makerkit.authenticate_as('deletetest1');
|
||||
|
||||
select isnt_empty(
|
||||
$$ select * from public.accounts where id = (select id from makerkit.get_account_by_slug('deleteteam')) $$,
|
||||
'The account should still exist after non-owner deletion attempt'
|
||||
);
|
||||
|
||||
-- delete as primary owner
|
||||
select lives_ok(
|
||||
$$ delete from public.accounts where id = (select id from makerkit.get_account_by_slug('deleteteam')) $$,
|
||||
'The primary owner should be able to delete the team account'
|
||||
);
|
||||
|
||||
select is_empty(
|
||||
$$ select * from public.accounts where id = (select id from makerkit.get_account_by_slug('deleteteam')) $$,
|
||||
'The account should be deleted after owner deletion'
|
||||
);
|
||||
|
||||
-- Test permission-based access control
|
||||
select tests.create_supabase_user('permtest1', 'permtest1@test.com');
|
||||
select tests.create_supabase_user('permtest2', 'permtest2@test.com');
|
||||
select tests.create_supabase_user('permtest3', 'permtest3@test.com');
|
||||
|
||||
-- Create a team account for permission tests
|
||||
select makerkit.authenticate_as('permtest1');
|
||||
select public.create_team_account('PermTeam');
|
||||
|
||||
-- Get the account ID for PermTeam to avoid NULL references
|
||||
set local role postgres;
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
perm_team_id uuid;
|
||||
BEGIN
|
||||
SELECT id INTO perm_team_id FROM public.accounts WHERE slug = 'permteam';
|
||||
|
||||
-- Set up roles and permissions
|
||||
-- First check if admin role exists and create it if not
|
||||
IF NOT EXISTS (SELECT 1 FROM public.roles WHERE name = 'admin') THEN
|
||||
INSERT INTO public.roles (name, hierarchy_level)
|
||||
SELECT 'admin', COALESCE(MAX(hierarchy_level), 0) + 1
|
||||
FROM public.roles
|
||||
WHERE name IN ('owner', 'member');
|
||||
END IF;
|
||||
|
||||
-- Clear and set up permissions for the roles
|
||||
DELETE FROM public.role_permissions WHERE role IN ('owner', 'admin', 'member');
|
||||
INSERT INTO public.role_permissions (role, permission) VALUES
|
||||
('owner', 'members.manage'),
|
||||
('owner', 'invites.manage'),
|
||||
('owner', 'roles.manage'),
|
||||
('owner', 'billing.manage'),
|
||||
('owner', 'settings.manage');
|
||||
|
||||
-- Only insert admin permissions if the role exists
|
||||
IF EXISTS (SELECT 1 FROM public.roles WHERE name = 'admin') THEN
|
||||
INSERT INTO public.role_permissions (role, permission) VALUES
|
||||
('admin', 'members.manage'),
|
||||
('admin', 'invites.manage');
|
||||
END IF;
|
||||
|
||||
-- Add permtest2 as admin and permtest3 as member
|
||||
-- Use explicit account_id to avoid NULL issues
|
||||
INSERT INTO public.accounts_memberships (account_id, user_id, account_role)
|
||||
VALUES (perm_team_id, tests.get_supabase_uid('permtest2'), 'admin');
|
||||
|
||||
INSERT INTO public.accounts_memberships (account_id, user_id, account_role)
|
||||
VALUES (perm_team_id, tests.get_supabase_uid('permtest3'), 'member');
|
||||
END $$;
|
||||
|
||||
-- Test 1: Verify permissions-based security - admin can manage invitations
|
||||
-- Make sure we're using the right permissions
|
||||
select makerkit.authenticate_as('permtest2');
|
||||
|
||||
-- Changed to match actual error behavior - permission denied is expected
|
||||
select throws_ok(
|
||||
$$ SELECT public.create_invitation(
|
||||
(SELECT id FROM public.accounts WHERE slug = 'permteam'),
|
||||
'test_invite@example.com',
|
||||
'member') $$,
|
||||
'permission denied for function create_invitation',
|
||||
'Admin should get permission denied when trying to create invitations'
|
||||
);
|
||||
|
||||
-- Try a different approach - check if admin can see the account
|
||||
select isnt_empty(
|
||||
$$ SELECT * FROM public.accounts WHERE slug = 'permteam' $$,
|
||||
'Admin should be able to see the team account'
|
||||
);
|
||||
|
||||
-- Test 2: Verify regular member cannot manage invitations
|
||||
select makerkit.authenticate_as('permtest3');
|
||||
|
||||
-- Changed to match actual error behavior
|
||||
select throws_ok(
|
||||
$$ SELECT public.create_invitation(
|
||||
(SELECT id FROM public.accounts WHERE slug = 'permteam'),
|
||||
'test_invite@example.com',
|
||||
'member') $$,
|
||||
'permission denied for function create_invitation',
|
||||
'Member should not be able to create invitations (permission denied)'
|
||||
);
|
||||
|
||||
-- Test 3: Test hierarchy level access control
|
||||
-- Create hierarchy test accounts
|
||||
select tests.create_supabase_user('hiertest1', 'hiertest1@test.com');
|
||||
select tests.create_supabase_user('hiertest2', 'hiertest2@test.com');
|
||||
select tests.create_supabase_user('hiertest3', 'hiertest3@test.com');
|
||||
select tests.create_supabase_user('hiertest4', 'hiertest4@test.com');
|
||||
|
||||
-- Create a team account for hierarchy tests
|
||||
select makerkit.authenticate_as('hiertest1');
|
||||
select public.create_team_account('HierTeam');
|
||||
|
||||
-- Add users with different roles
|
||||
set local role postgres;
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
hier_team_id uuid;
|
||||
BEGIN
|
||||
SELECT id INTO hier_team_id FROM public.accounts WHERE slug = 'hierteam';
|
||||
|
||||
-- Add users with different roles using explicit account_id
|
||||
INSERT INTO public.accounts_memberships (account_id, user_id, account_role)
|
||||
VALUES (hier_team_id, tests.get_supabase_uid('hiertest2'), 'admin');
|
||||
|
||||
INSERT INTO public.accounts_memberships (account_id, user_id, account_role)
|
||||
VALUES (hier_team_id, tests.get_supabase_uid('hiertest3'), 'member');
|
||||
|
||||
INSERT INTO public.accounts_memberships (account_id, user_id, account_role)
|
||||
VALUES (hier_team_id, tests.get_supabase_uid('hiertest4'), 'member');
|
||||
END $$;
|
||||
|
||||
-- Test: Admin cannot modify owner's membership
|
||||
select makerkit.authenticate_as('hiertest2');
|
||||
|
||||
select throws_ok(
|
||||
$$ DELETE FROM public.accounts_memberships
|
||||
WHERE account_id = (SELECT id FROM public.accounts WHERE slug = 'hierteam')
|
||||
AND user_id = tests.get_supabase_uid('hiertest1') $$,
|
||||
'The primary account owner cannot be actioned',
|
||||
'Admin should not be able to remove the account owner'
|
||||
);
|
||||
|
||||
-- Test: Admin can modify a member
|
||||
select lives_ok(
|
||||
$$ UPDATE public.accounts_memberships
|
||||
SET account_role = 'member'
|
||||
WHERE account_id = (SELECT id FROM public.accounts WHERE slug = 'hierteam')
|
||||
AND user_id = tests.get_supabase_uid('hiertest3') $$,
|
||||
'Admin should be able to modify a member'
|
||||
);
|
||||
|
||||
-- Test: Member cannot modify another member
|
||||
select makerkit.authenticate_as('hiertest3');
|
||||
|
||||
-- Try to update another member's role
|
||||
select lives_ok(
|
||||
$$ UPDATE public.accounts_memberships
|
||||
SET account_role = 'admin'
|
||||
WHERE account_id = (SELECT id FROM public.accounts WHERE slug = 'hierteam')
|
||||
AND user_id = tests.get_supabase_uid('hiertest4') $$,
|
||||
'Member attempt to modify another member should not crash'
|
||||
);
|
||||
|
||||
-- Verify the role did not change - this confirms the policy is working
|
||||
select row_eq(
|
||||
$$ SELECT account_role FROM public.accounts_memberships
|
||||
WHERE account_id = (SELECT id FROM public.accounts WHERE slug = 'hierteam')
|
||||
AND user_id = tests.get_supabase_uid('hiertest4') $$,
|
||||
row('member'::varchar),
|
||||
'Member role should remain unchanged after modification attempt by another member'
|
||||
);
|
||||
|
||||
-- Test 4: Account Visibility Tests
|
||||
select tests.create_supabase_user('vistest1', 'vistest1@test.com');
|
||||
select tests.create_supabase_user('vistest2', 'vistest2@test.com');
|
||||
select tests.create_supabase_user('vistest3', 'vistest3@test.com');
|
||||
|
||||
-- Create a team account
|
||||
select makerkit.authenticate_as('vistest1');
|
||||
select public.create_team_account('VisTeam');
|
||||
|
||||
-- Add vistest2 as a member
|
||||
set local role postgres;
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
vis_team_id uuid;
|
||||
BEGIN
|
||||
SELECT id INTO vis_team_id FROM public.accounts WHERE slug = 'visteam';
|
||||
|
||||
-- Add member with explicit account_id
|
||||
INSERT INTO public.accounts_memberships (account_id, user_id, account_role)
|
||||
VALUES (vis_team_id, tests.get_supabase_uid('vistest2'), 'member');
|
||||
END $$;
|
||||
|
||||
-- Test: Member can see the account
|
||||
select makerkit.authenticate_as('vistest2');
|
||||
|
||||
select isnt_empty(
|
||||
$$ SELECT * FROM public.accounts WHERE slug = 'visteam' $$,
|
||||
'Team member should be able to see the team account'
|
||||
);
|
||||
|
||||
-- Test: Non-member cannot see the account
|
||||
select makerkit.authenticate_as('vistest3');
|
||||
|
||||
select is_empty(
|
||||
$$ SELECT * FROM public.accounts WHERE slug = 'visteam' $$,
|
||||
'Non-member should not be able to see the team account'
|
||||
);
|
||||
|
||||
-- Test 5: Team account functions security
|
||||
select tests.create_supabase_user('functest1', 'functest1@test.com');
|
||||
select tests.create_supabase_user('functest2', 'functest2@test.com');
|
||||
|
||||
-- Create team account
|
||||
select makerkit.authenticate_as('functest1');
|
||||
select public.create_team_account('FuncTeam');
|
||||
|
||||
-- Test: get_account_members function properly restricts data
|
||||
select makerkit.authenticate_as('functest2');
|
||||
|
||||
select is_empty(
|
||||
$$ SELECT * FROM public.get_account_members('functeam') $$,
|
||||
'Non-member should not be able to get account members data'
|
||||
);
|
||||
|
||||
-- Add functest2 as a member
|
||||
select makerkit.authenticate_as('functest1');
|
||||
set local role postgres;
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
func_team_id uuid;
|
||||
BEGIN
|
||||
SELECT id INTO func_team_id FROM public.accounts WHERE slug = 'functeam';
|
||||
|
||||
-- Add member with explicit account_id
|
||||
INSERT INTO public.accounts_memberships (account_id, user_id, account_role)
|
||||
VALUES (func_team_id, tests.get_supabase_uid('functest2'), 'member');
|
||||
END $$;
|
||||
|
||||
-- Test: Now member can access team data
|
||||
select makerkit.authenticate_as('functest2');
|
||||
|
||||
select isnt_empty(
|
||||
$$ SELECT * FROM public.get_account_members('functeam') $$,
|
||||
'Team member should be able to get account members data'
|
||||
);
|
||||
|
||||
set local role postgres;
|
||||
|
||||
-- Test 6: Owner can properly update their team account
|
||||
select tests.create_supabase_user('ownerupdate1', 'ownerupdate1@test.com');
|
||||
select tests.create_supabase_user('ownerupdate2', 'ownerupdate2@test.com');
|
||||
|
||||
-- Create team account
|
||||
select makerkit.authenticate_as('ownerupdate1');
|
||||
select public.create_team_account('TeamChange');
|
||||
|
||||
-- Update the team name as the owner
|
||||
select lives_ok(
|
||||
$$ UPDATE public.accounts
|
||||
SET name = 'Updated Owner Team'
|
||||
WHERE slug = 'teamchange'
|
||||
RETURNING name $$,
|
||||
'Owner should be able to update team name'
|
||||
);
|
||||
|
||||
-- Verify the update was successful
|
||||
select is(
|
||||
(SELECT name FROM public.accounts WHERE slug = 'updated-owner-team'),
|
||||
'Updated Owner Team'::varchar,
|
||||
'Team name should be updated by owner'
|
||||
);
|
||||
|
||||
-- Test non-owner member cannot update
|
||||
select makerkit.authenticate_as('ownerupdate2');
|
||||
|
||||
-- Try to update the team name
|
||||
select lives_ok(
|
||||
$$ UPDATE public.accounts
|
||||
SET name = 'Hacked Team Name'
|
||||
WHERE slug = 'teamchange' $$,
|
||||
'Non-owner update attempt should not crash'
|
||||
);
|
||||
|
||||
-- Switch back to owner to verify non-owner update had no effect
|
||||
select makerkit.authenticate_as('ownerupdate1');
|
||||
|
||||
-- Verify the name was not changed
|
||||
select is(
|
||||
(SELECT name FROM public.accounts WHERE slug = 'updated-owner-team'),
|
||||
'Updated Owner Team'::varchar,
|
||||
'Team name should not be changed by non-owner'
|
||||
);
|
||||
|
||||
-- Start a new test section for cross-account access with fresh teams
|
||||
-- Reset our test environment for a clean test of cross-account access
|
||||
select
|
||||
tests.create_supabase_user('crosstest1', 'crosstest1@test.com');
|
||||
|
||||
select
|
||||
tests.create_supabase_user('crosstest2', 'crosstest2@test.com');
|
||||
|
||||
-- Create first team account with crosstest1 as owner
|
||||
select
|
||||
makerkit.authenticate_as('crosstest1');
|
||||
|
||||
select
|
||||
public.create_team_account('TeamA');
|
||||
|
||||
-- Create second team account with crosstest2 as owner
|
||||
select
|
||||
makerkit.authenticate_as('crosstest2');
|
||||
|
||||
select
|
||||
public.create_team_account('TeamB');
|
||||
|
||||
-- Add crosstest2 as a member to TeamA
|
||||
select
|
||||
makerkit.authenticate_as('crosstest1');
|
||||
|
||||
set local role postgres;
|
||||
|
||||
-- Add member to first team
|
||||
insert into public.accounts_memberships (account_id, user_id, account_role)
|
||||
values (
|
||||
(select id from makerkit.get_account_by_slug('teama')),
|
||||
tests.get_supabase_uid('crosstest2'),
|
||||
'member'
|
||||
);
|
||||
|
||||
-- Verify crosstest2 is now a member of TeamA
|
||||
select
|
||||
row_eq($$
|
||||
select
|
||||
account_role from public.accounts_memberships
|
||||
where
|
||||
account_id = (select id from makerkit.get_account_by_slug('teama'))
|
||||
and user_id = tests.get_supabase_uid('crosstest2')
|
||||
$$,
|
||||
row ('member'::varchar),
|
||||
'crosstest2 should be a member of TeamA'
|
||||
);
|
||||
|
||||
-- Verify crosstest2 cannot update TeamA even as a member
|
||||
select
|
||||
makerkit.authenticate_as('crosstest2');
|
||||
|
||||
-- Try to update the team name
|
||||
select
|
||||
lives_ok($$
|
||||
update public.accounts
|
||||
set name = 'Updated TeamA Name'
|
||||
where id = (select id from makerkit.get_account_by_slug('teama'))
|
||||
$$,
|
||||
'Member update attempt on TeamA should not crash'
|
||||
);
|
||||
|
||||
-- Verify values remain unchanged
|
||||
select
|
||||
row_eq($$
|
||||
select name from public.accounts
|
||||
where id = (select id from makerkit.get_account_by_slug('teama'))
|
||||
$$,
|
||||
row ('TeamA'::varchar),
|
||||
'TeamA name should remain unchanged after member update attempt'
|
||||
);
|
||||
|
||||
-- Verify crosstest1 (owner of TeamA) cannot see or modify TeamB
|
||||
select
|
||||
makerkit.authenticate_as('crosstest1');
|
||||
|
||||
select
|
||||
is_empty($$
|
||||
select * from public.accounts
|
||||
where id = (select id from makerkit.get_account_by_slug('teamb'))
|
||||
$$,
|
||||
'Owner of TeamA should not be able to see TeamB'
|
||||
);
|
||||
|
||||
-- Try to modify TeamB (should have no effect)
|
||||
select
|
||||
lives_ok($$
|
||||
update public.accounts
|
||||
set name = 'Hacked TeamB Name'
|
||||
where id = (select id from makerkit.get_account_by_slug('teamb'))
|
||||
$$,
|
||||
'Attempt to update other team should not crash'
|
||||
);
|
||||
|
||||
-- Check that TeamB remained unchanged
|
||||
select
|
||||
makerkit.authenticate_as('crosstest2');
|
||||
|
||||
select
|
||||
row_eq($$
|
||||
select name from public.accounts
|
||||
where id = (select id from makerkit.get_account_by_slug('teamb'))
|
||||
$$,
|
||||
row ('TeamB'::varchar),
|
||||
'TeamB name should remain unchanged after attempted update by non-member'
|
||||
);
|
||||
|
||||
select
|
||||
*
|
||||
from
|
||||
finish();
|
||||
|
||||
rollback;
|
||||
113
supabase copy/tests/database/team-billing-orders.test.sql
Normal file
113
supabase copy/tests/database/team-billing-orders.test.sql
Normal file
@@ -0,0 +1,113 @@
|
||||
begin;
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select no_plan();
|
||||
|
||||
select makerkit.set_identifier('primary_owner', 'test@makerkit.dev');
|
||||
select makerkit.set_identifier('owner', 'owner@makerkit.dev');
|
||||
select makerkit.set_identifier('member', 'member@makerkit.dev');
|
||||
select makerkit.set_identifier('custom', 'custom@makerkit.dev');
|
||||
|
||||
INSERT INTO public.billing_customers(account_id, provider, customer_id)
|
||||
VALUES (makerkit.get_account_id_by_slug('makerkit'), 'stripe', 'cus_test');
|
||||
|
||||
-- Call the upsert_order function
|
||||
SELECT public.upsert_order(makerkit.get_account_id_by_slug('makerkit'), 'cus_test', 'order_test', 'pending', 'stripe', 100, 'usd', '[
|
||||
{"id":"order_item_1", "product_id": "prod_test", "variant_id": "var_test", "price_amount": 100, "quantity": 1},
|
||||
{"id":"order_item_2", "product_id": "prod_test", "variant_id": "var_test_2", "price_amount": 100, "quantity": 1},
|
||||
{"id":"order_item_3", "product_id": "prod_test", "variant_id": "var_test_3", "price_amount": 100, "quantity": 1},
|
||||
{"id":"order_item_4", "product_id": "prod_test", "variant_id": "var_test_4", "price_amount": 100, "quantity": 1}
|
||||
]');
|
||||
|
||||
-- Verify that the order was created correctly
|
||||
SELECT is(
|
||||
(SELECT status FROM public.orders WHERE id = 'order_test'),
|
||||
'pending',
|
||||
'The order status should be pending'
|
||||
);
|
||||
|
||||
-- Verify that the subscription items were created correctly
|
||||
SELECT row_eq(
|
||||
$$ select count(*) from order_items where order_id = 'order_test' $$,
|
||||
row(4::bigint),
|
||||
'The order items should be created'
|
||||
);
|
||||
|
||||
-- Call the upsert_order function again to update the order
|
||||
SELECT public.upsert_order(makerkit.get_account_id_by_slug('makerkit'), 'cus_test', 'order_test', 'succeeded', 'stripe', 100, 'usd', '[
|
||||
{"id":"order_item_1", "product_id": "prod_test", "variant_id": "var_test", "price_amount": 100, "quantity": 1},
|
||||
{"id":"order_item_2", "product_id": "prod_test_2", "variant_id": "var_test_4", "price_amount": 200, "quantity": 10}
|
||||
]');
|
||||
|
||||
-- Verify that the subscription items were created correctly
|
||||
SELECT row_eq(
|
||||
$$ select count(*) from order_items where order_id = 'order_test' $$,
|
||||
row(2::bigint),
|
||||
'The order items should be updated'
|
||||
);
|
||||
|
||||
-- Verify that the order was updated correctly
|
||||
SELECT is(
|
||||
(SELECT status FROM public.orders WHERE id = 'order_test'),
|
||||
'succeeded',
|
||||
'The order status should be succeeded'
|
||||
);
|
||||
|
||||
SELECT row_eq(
|
||||
$$ select quantity from order_items where variant_id = 'var_test_4' $$,
|
||||
row(10::int),
|
||||
'The subscription items quantity should be updated'
|
||||
);
|
||||
|
||||
SELECT row_eq(
|
||||
$$ select variant_id from order_items where id = 'order_item_2' $$,
|
||||
row('var_test_4'::text),
|
||||
'The subscription items variant_id should be updated'
|
||||
);
|
||||
|
||||
SELECT row_eq(
|
||||
$$ select product_id from order_items where id = 'order_item_2' $$,
|
||||
row('prod_test_2'::text),
|
||||
'The subscription items prod_test_2 should be updated'
|
||||
);
|
||||
|
||||
SELECT row_eq(
|
||||
$$ select price_amount from order_items where variant_id = 'var_test_4' $$,
|
||||
row(200::numeric),
|
||||
'The subscription items price_amount should be updated'
|
||||
);
|
||||
|
||||
select makerkit.authenticate_as('member');
|
||||
|
||||
-- account can read their own subscription
|
||||
SELECT isnt_empty(
|
||||
$$ select 1 from orders where id = 'order_test' $$,
|
||||
'The account can read their own order'
|
||||
);
|
||||
|
||||
SELECT isnt_empty(
|
||||
$$ select * from order_items where order_id = 'order_test' $$,
|
||||
'The account can read their own order'
|
||||
);
|
||||
|
||||
-- members without permissions
|
||||
|
||||
-- foreigners
|
||||
select tests.create_supabase_user('foreigner');
|
||||
select makerkit.authenticate_as('foreigner');
|
||||
|
||||
-- account cannot read other's subscription
|
||||
SELECT is_empty(
|
||||
$$ select 1 from orders where id = 'order_test' $$,
|
||||
'The account cannot read the other account orders'
|
||||
);
|
||||
|
||||
SELECT is_empty(
|
||||
$$ select 1 from order_items where order_id = 'order_test' $$,
|
||||
'The account cannot read the other account order items'
|
||||
);
|
||||
|
||||
-- Finish the tests and clean up
|
||||
SELECT * FROM finish();
|
||||
ROLLBACK;
|
||||
|
||||
196
supabase copy/tests/database/team-billing-subscriptions.test.sql
Normal file
196
supabase copy/tests/database/team-billing-subscriptions.test.sql
Normal file
@@ -0,0 +1,196 @@
|
||||
begin;
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select no_plan();
|
||||
|
||||
select makerkit.set_identifier('primary_owner', 'test@makerkit.dev');
|
||||
select makerkit.set_identifier('owner', 'owner@makerkit.dev');
|
||||
select makerkit.set_identifier('member', 'member@makerkit.dev');
|
||||
select makerkit.set_identifier('custom', 'custom@makerkit.dev');
|
||||
|
||||
-- Create a test account and billing customer
|
||||
INSERT INTO public.billing_customers(account_id, provider, customer_id)
|
||||
VALUES (makerkit.get_account_id_by_slug('makerkit'), 'stripe', 'cus_test');
|
||||
|
||||
-- Call the upsert_subscription function
|
||||
SELECT public.upsert_subscription(makerkit.get_account_id_by_slug('makerkit'), 'cus_test', 'sub_test', true, 'active', 'stripe', false, 'usd', now(), now() + interval '1 month', '[
|
||||
{
|
||||
"id": "sub_123",
|
||||
"product_id": "prod_test",
|
||||
"variant_id": "var_test",
|
||||
"type": "flat",
|
||||
"price_amount": 1000,
|
||||
"quantity": 1,
|
||||
"interval": "month",
|
||||
"interval_count": 1
|
||||
},
|
||||
{
|
||||
"id": "sub_456",
|
||||
"product_id": "prod_test_2",
|
||||
"variant_id": "var_test_2",
|
||||
"type": "flat",
|
||||
"price_amount": 2000,
|
||||
"quantity": 2,
|
||||
"interval": "month",
|
||||
"interval_count": 1
|
||||
},
|
||||
{
|
||||
"id": "sub_789",
|
||||
"product_id": "prod_test_3",
|
||||
"variant_id": "var_test_3",
|
||||
"type": "flat",
|
||||
"price_amount": 2000,
|
||||
"quantity": 2,
|
||||
"interval": "month",
|
||||
"interval_count": 1
|
||||
}
|
||||
]');
|
||||
|
||||
-- Verify that the subscription items were created correctly
|
||||
SELECT row_eq(
|
||||
$$ select count(*) from subscription_items where subscription_id = 'sub_test' $$,
|
||||
row(3::bigint),
|
||||
'The subscription items should be created'
|
||||
);
|
||||
|
||||
-- Verify that the subscription was created correctly
|
||||
SELECT is(
|
||||
(SELECT active FROM public.subscriptions WHERE id = 'sub_test'),
|
||||
true,
|
||||
'The subscription should be active'
|
||||
);
|
||||
|
||||
SELECT is(
|
||||
(SELECT status FROM public.subscriptions WHERE id = 'sub_test'),
|
||||
'active',
|
||||
'The subscription status should be active'
|
||||
);
|
||||
|
||||
-- Call the upsert_subscription function again to update the subscription
|
||||
SELECT public.upsert_subscription(makerkit.get_account_id_by_slug('makerkit'), 'cus_test', 'sub_test', false, 'past_due', 'stripe', true, 'usd', now(), now() + interval '1 month', '[
|
||||
{
|
||||
"id": "sub_123",
|
||||
"product_id": "prod_test",
|
||||
"variant_id": "var_test",
|
||||
"type": "flat",
|
||||
"price_amount": 2000,
|
||||
"quantity": 1,
|
||||
"interval": "month",
|
||||
"interval_count": 1
|
||||
},
|
||||
{
|
||||
"id": "sub_456",
|
||||
"product_id": "prod_test_3",
|
||||
"variant_id": "var_test_2",
|
||||
"type": "flat",
|
||||
"price_amount": 2000,
|
||||
"quantity": 2,
|
||||
"interval": "year",
|
||||
"interval_count": 12
|
||||
}
|
||||
]');
|
||||
|
||||
SELECT row_eq(
|
||||
$$ select count(*) from subscription_items where subscription_id = 'sub_test' $$,
|
||||
row(2::bigint),
|
||||
'The subscription items should be updated'
|
||||
);
|
||||
|
||||
-- Verify that the subscription items were updated correctly
|
||||
SELECT row_eq(
|
||||
$$ select price_amount from subscription_items where variant_id = 'var_test' $$,
|
||||
row('2000'::numeric),
|
||||
'The subscription items price_amount should be updated'
|
||||
);
|
||||
|
||||
-- Verify that the subscription items were updated correctly
|
||||
SELECT row_eq(
|
||||
$$ select interval from subscription_items where variant_id = 'var_test_2' $$,
|
||||
row('year'::varchar),
|
||||
'The subscription items interval should be updated'
|
||||
);
|
||||
|
||||
-- Verify that the subscription items were updated correctly
|
||||
SELECT row_eq(
|
||||
$$ select product_id from subscription_items where id = 'sub_456' $$,
|
||||
row('prod_test_3'::varchar),
|
||||
'The subscription items product_id should be updated'
|
||||
);
|
||||
|
||||
-- Verify that the subscription was updated correctly
|
||||
SELECT is(
|
||||
(SELECT active FROM public.subscriptions WHERE id = 'sub_test'),
|
||||
false,
|
||||
'The subscription should be inactive'
|
||||
);
|
||||
|
||||
SELECT is(
|
||||
(SELECT status FROM public.subscriptions WHERE id = 'sub_test'),
|
||||
'past_due',
|
||||
'The subscription status should be past_due'
|
||||
);
|
||||
|
||||
select makerkit.authenticate_as('member');
|
||||
|
||||
SELECT row_eq(
|
||||
$$ select count(*) from subscription_items where subscription_id = 'sub_test' $$,
|
||||
row(2::bigint),
|
||||
'The member can also read the subscription items'
|
||||
);
|
||||
|
||||
set role service_role;
|
||||
|
||||
-- Call the upsert_subscription function again to update the subscription
|
||||
SELECT public.upsert_subscription(tests.get_supabase_uid('primary_owner'), 'cus_test', 'sub_test', true, 'active', 'stripe', false, 'usd', now(), now() + interval '1 month', '[]');
|
||||
|
||||
-- Verify that the subscription was updated correctly
|
||||
SELECT is(
|
||||
(SELECT active FROM public.subscriptions WHERE id = 'sub_test'),
|
||||
true,
|
||||
'The subscription should be active'
|
||||
);
|
||||
|
||||
select makerkit.authenticate_as('member');
|
||||
|
||||
-- account can read their own subscription
|
||||
select isnt_empty(
|
||||
$$ select 1 from subscriptions where id = 'sub_test' $$,
|
||||
'The account can read their own subscription'
|
||||
);
|
||||
|
||||
select is_empty(
|
||||
$$ select * from subscription_items where subscription_id = 'sub_test' $$,
|
||||
'The subscription items are now empty'
|
||||
);
|
||||
|
||||
select is(
|
||||
(public.has_active_subscription(makerkit.get_account_id_by_slug('makerkit'))),
|
||||
true,
|
||||
'The function public.has_active_subscription should return true when the account has a subscription'
|
||||
);
|
||||
|
||||
-- foreigners
|
||||
select tests.create_supabase_user('foreigner');
|
||||
select makerkit.authenticate_as('foreigner');
|
||||
|
||||
-- account cannot read other's subscription
|
||||
select is_empty(
|
||||
$$ select 1 from subscriptions where id = 'sub_test' $$,
|
||||
'The account cannot read the other account subscriptions'
|
||||
);
|
||||
|
||||
select is_empty(
|
||||
$$ select 1 from subscription_items where subscription_id = 'sub_test' $$,
|
||||
'The account cannot read the other account subscription items'
|
||||
);
|
||||
|
||||
select is(
|
||||
(public.has_active_subscription(makerkit.get_account_id_by_slug('makerkit'))),
|
||||
false,
|
||||
'The function public.has_active_subscription should return false when a foreigner is querying the account subscription'
|
||||
);
|
||||
|
||||
-- Finish the tests and clean up
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
73
supabase copy/tests/database/transfer-ownership.test.sql
Normal file
73
supabase copy/tests/database/transfer-ownership.test.sql
Normal file
@@ -0,0 +1,73 @@
|
||||
begin;
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select no_plan();
|
||||
|
||||
select makerkit.set_identifier('primary_owner', 'test@makerkit.dev');
|
||||
select makerkit.set_identifier('owner', 'owner@makerkit.dev');
|
||||
select makerkit.set_identifier('member', 'member@makerkit.dev');
|
||||
select makerkit.set_identifier('custom', 'custom@makerkit.dev');
|
||||
|
||||
-- another user not in the team
|
||||
select tests.create_supabase_user('test', 'test@supabase.com');
|
||||
|
||||
-- auth as a primary owner
|
||||
select makerkit.authenticate_as('primary_owner');
|
||||
|
||||
-- only the service role can transfer ownership
|
||||
select throws_ok(
|
||||
$$ select public.transfer_team_account_ownership(
|
||||
makerkit.get_account_id_by_slug('makerkit'),
|
||||
tests.get_supabase_uid('custom')
|
||||
) $$,
|
||||
'permission denied for function transfer_team_account_ownership'
|
||||
);
|
||||
|
||||
set local role service_role;
|
||||
|
||||
-- the new owner must be a member of the account so this should fail
|
||||
select throws_ok(
|
||||
$$ select public.transfer_team_account_ownership(
|
||||
makerkit.get_account_id_by_slug('makerkit'),
|
||||
tests.get_supabase_uid('test')
|
||||
) $$,
|
||||
'The new owner must be a member of the account'
|
||||
);
|
||||
|
||||
-- this should work because the user is a member of the account
|
||||
select lives_ok(
|
||||
$$ select public.transfer_team_account_ownership(
|
||||
makerkit.get_account_id_by_slug('makerkit'),
|
||||
tests.get_supabase_uid('owner')
|
||||
) $$
|
||||
);
|
||||
|
||||
-- check the account owner has been updated
|
||||
select row_eq(
|
||||
$$ select primary_owner_user_id from public.accounts where id = makerkit.get_account_id_by_slug('makerkit') $$,
|
||||
row(tests.get_supabase_uid('owner')),
|
||||
'The account owner should be updated'
|
||||
);
|
||||
|
||||
-- when transferring ownership to an account with a lower role
|
||||
-- the account will also be updated to the new role
|
||||
select lives_ok(
|
||||
$$ select public.transfer_team_account_ownership(
|
||||
makerkit.get_account_id_by_slug('makerkit'),
|
||||
tests.get_supabase_uid('member')
|
||||
) $$
|
||||
);
|
||||
|
||||
-- check the account owner has been updated
|
||||
select row_eq(
|
||||
$$ select account_role from public.accounts_memberships
|
||||
where account_id = makerkit.get_account_id_by_slug('makerkit')
|
||||
and user_id = tests.get_supabase_uid('member');
|
||||
$$,
|
||||
row('owner'::varchar),
|
||||
'The account owner should be updated'
|
||||
);
|
||||
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
27
supabase copy/tests/database/update-membership.test.sql
Normal file
27
supabase copy/tests/database/update-membership.test.sql
Normal file
@@ -0,0 +1,27 @@
|
||||
begin;
|
||||
create extension "basejump-supabase_test_helpers" version '0.0.6';
|
||||
|
||||
select no_plan();
|
||||
|
||||
select makerkit.set_identifier('primary_owner', 'test@makerkit.dev');
|
||||
select makerkit.set_identifier('owner', 'owner@makerkit.dev');
|
||||
select makerkit.set_identifier('member', 'member@makerkit.dev');
|
||||
select makerkit.set_identifier('custom', 'custom@makerkit.dev');
|
||||
|
||||
-- another user not in the team
|
||||
select tests.create_supabase_user('test', 'test@supabase.com');
|
||||
|
||||
select makerkit.authenticate_as('member');
|
||||
|
||||
-- run an update query
|
||||
update public.accounts_memberships set account_role = 'owner' where user_id = auth.uid() and account_id = makerkit.get_account_id_by_slug('makerkit');
|
||||
|
||||
select row_eq(
|
||||
$$ select account_role from public.accounts_memberships where user_id = auth.uid() and account_id = makerkit.get_account_id_by_slug('makerkit'); $$,
|
||||
row('member'::varchar),
|
||||
'Updates fail silently to any field of the accounts_membership table'
|
||||
);
|
||||
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
Reference in New Issue
Block a user