Fixed bugs in memberships management
This commit is contained in:
3
.github/workflows/workflow.yml
vendored
3
.github/workflows/workflow.yml
vendored
@@ -113,6 +113,9 @@ jobs:
|
|||||||
- name: Run Playwright tests
|
- name: Run Playwright tests
|
||||||
run: pnpm run test
|
run: pnpm run test
|
||||||
|
|
||||||
|
- name: Run Supabase tests
|
||||||
|
run: pnpm --filter web supabase:test
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -17,6 +17,7 @@
|
|||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"with-env": "dotenv -e ./.env.local --",
|
"with-env": "dotenv -e ./.env.local --",
|
||||||
"with-env:test": "dotenv -e ./.env.test --",
|
"with-env:test": "dotenv -e ./.env.test --",
|
||||||
|
"supabase": "supabase",
|
||||||
"supabase:start": "supabase status || supabase start",
|
"supabase:start": "supabase status || supabase start",
|
||||||
"supabase:stop": "supabase stop",
|
"supabase:stop": "supabase stop",
|
||||||
"supabase:reset": "supabase db reset",
|
"supabase:reset": "supabase db reset",
|
||||||
|
|||||||
@@ -670,9 +670,9 @@ create policy roles_read on public.roles
|
|||||||
or public.has_role_on_account(account_id)
|
or public.has_role_on_account(account_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Function to check if a user can remove a member from an account
|
-- Function to check if a user can perform management actions on an account member
|
||||||
create or replace function
|
create or replace function
|
||||||
kit.can_remove_account_member(target_team_account_id uuid,
|
public.can_action_account_member(target_team_account_id uuid,
|
||||||
user_id uuid)
|
user_id uuid)
|
||||||
returns boolean
|
returns boolean
|
||||||
as $$
|
as $$
|
||||||
@@ -680,7 +680,20 @@ declare
|
|||||||
permission_granted boolean;
|
permission_granted boolean;
|
||||||
target_user_hierarchy_level int;
|
target_user_hierarchy_level int;
|
||||||
current_user_hierarchy_level int;
|
current_user_hierarchy_level int;
|
||||||
|
is_account_owner boolean;
|
||||||
begin
|
begin
|
||||||
|
select public.is_account_owner(target_team_account_id) into is_account_owner;
|
||||||
|
|
||||||
|
-- a primary owner of the account can never be actioned
|
||||||
|
if is_account_owner and user_id = auth.uid() then
|
||||||
|
raise exception 'You cannot action the primary owner of the account';
|
||||||
|
end if;
|
||||||
|
|
||||||
|
-- an account owner can action any member of the account
|
||||||
|
if is_account_owner then
|
||||||
|
return true;
|
||||||
|
end if;
|
||||||
|
|
||||||
-- validate the auth user has the required permission on the account
|
-- validate the auth user has the required permission on the account
|
||||||
-- to manage members of the account
|
-- to manage members of the account
|
||||||
select
|
select
|
||||||
@@ -689,13 +702,12 @@ begin
|
|||||||
permission_granted;
|
permission_granted;
|
||||||
|
|
||||||
if not permission_granted then
|
if not permission_granted then
|
||||||
raise exception 'You do not have permission to remove a member from this account';
|
raise exception 'You do not have permission to action a member from this account';
|
||||||
|
|
||||||
end if;
|
end if;
|
||||||
-- users cannot remove themselves from the account with this function
|
-- users cannot remove themselves from the account with this function
|
||||||
if can_remove_account_member.user_id = auth.uid() then
|
if can_action_account_member.user_id = auth.uid() then
|
||||||
raise exception 'You cannot remove yourself from the account';
|
raise exception 'You cannot update your own account membership with this function';
|
||||||
|
|
||||||
end if;
|
end if;
|
||||||
|
|
||||||
select
|
select
|
||||||
@@ -718,13 +730,15 @@ begin
|
|||||||
where
|
where
|
||||||
account_id = target_team_account_id
|
account_id = target_team_account_id
|
||||||
and user_id = auth.uid());
|
and user_id = auth.uid());
|
||||||
|
|
||||||
-- check if the current user has a higher hierarchy level than the
|
-- check if the current user has a higher hierarchy level than the
|
||||||
-- target user
|
-- target user. Lower hierarchy levels have higher permissions than higher hierarchy levels
|
||||||
if current_user_hierarchy_level <= target_user_hierarchy_level then
|
if current_user_hierarchy_level <= target_user_hierarchy_level then
|
||||||
raise exception 'You do not have permission to remove this user from the account';
|
raise exception 'You do not have permission to action this user';
|
||||||
|
|
||||||
end if;
|
end if;
|
||||||
|
|
||||||
|
-- return true if the user has the required permission
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
end;
|
end;
|
||||||
@@ -732,11 +746,11 @@ end;
|
|||||||
$$
|
$$
|
||||||
language plpgsql;
|
language plpgsql;
|
||||||
|
|
||||||
grant execute on function kit.can_remove_account_member(uuid, uuid)
|
grant execute on function public.can_action_account_member(uuid, uuid)
|
||||||
to authenticated, service_role;
|
to authenticated, service_role;
|
||||||
|
|
||||||
-- RLS
|
-- RLS
|
||||||
-- SELECT: Users can read their team members account memberships
|
-- SELECT: Users can read their account memberships
|
||||||
create policy accounts_memberships_read_self on public.accounts_memberships
|
create policy accounts_memberships_read_self on public.accounts_memberships
|
||||||
for select to authenticated
|
for select to authenticated
|
||||||
using (user_id = auth.uid());
|
using (user_id = auth.uid());
|
||||||
@@ -760,7 +774,7 @@ create policy accounts_memberships_delete_self on public.accounts_memberships
|
|||||||
-- DELETE: Users with the required role can remove members from an account
|
-- DELETE: Users with the required role can remove members from an account
|
||||||
create policy accounts_memberships_delete on public.accounts_memberships
|
create policy accounts_memberships_delete on public.accounts_memberships
|
||||||
for delete to authenticated
|
for delete to authenticated
|
||||||
using (kit.can_remove_account_member(account_id, user_id));
|
using (public.can_action_account_member(account_id, user_id));
|
||||||
|
|
||||||
-- SELECT (public.accounts): Team members can read accounts of the team
|
-- SELECT (public.accounts): Team members can read accounts of the team
|
||||||
-- they are a member of
|
-- they are a member of
|
||||||
@@ -939,7 +953,7 @@ grant select, insert, update, delete on table public.invitations to
|
|||||||
-- Enable RLS on the invitations table
|
-- Enable RLS on the invitations table
|
||||||
alter table public.invitations enable row level security;
|
alter table public.invitations enable row level security;
|
||||||
|
|
||||||
create or replace function check_team_account()
|
create or replace function kit.check_team_account()
|
||||||
returns trigger
|
returns trigger
|
||||||
as $$
|
as $$
|
||||||
begin
|
begin
|
||||||
@@ -963,7 +977,7 @@ language plpgsql;
|
|||||||
|
|
||||||
create trigger only_team_accounts_check
|
create trigger only_team_accounts_check
|
||||||
before insert or update on public.invitations for each row
|
before insert or update on public.invitations for each row
|
||||||
execute procedure check_team_account();
|
execute procedure kit.check_team_account();
|
||||||
|
|
||||||
-- RLS
|
-- RLS
|
||||||
-- SELECT: Users can read invitations to users of an account they
|
-- SELECT: Users can read invitations to users of an account they
|
||||||
@@ -971,7 +985,7 @@ create trigger only_team_accounts_check
|
|||||||
-- a member of
|
-- a member of
|
||||||
create policy invitations_read_self on public.invitations
|
create policy invitations_read_self on public.invitations
|
||||||
for select to authenticated
|
for select to authenticated
|
||||||
using (has_role_on_account(account_id));
|
using (public.has_role_on_account(account_id));
|
||||||
|
|
||||||
-- INSERT: Users can create invitations to users of an account they are
|
-- INSERT: Users can create invitations to users of an account they are
|
||||||
-- a member of
|
-- a member of
|
||||||
|
|||||||
@@ -44,51 +44,6 @@ execute function "supabase_functions"."http_request"(
|
|||||||
-- DATA SEED
|
-- DATA SEED
|
||||||
-- This is a data dump for testing purposes. It should be used to seed the database with data for testing.
|
-- This is a data dump for testing purposes. It should be used to seed the database with data for testing.
|
||||||
|
|
||||||
SET session_replication_role = replica;
|
|
||||||
|
|
||||||
pg_dump: warning: there are circular foreign-key constraints on this table:
|
|
||||||
pg_dump: detail: key
|
|
||||||
pg_dump: hint: You might not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints.
|
|
||||||
pg_dump: hint: Consider using a full dump instead of a --data-only dump to avoid this problem.
|
|
||||||
--
|
|
||||||
-- PostgreSQL database dump
|
|
||||||
--
|
|
||||||
|
|
||||||
-- Dumped from database version 15.1 (Ubuntu 15.1-1.pgdg20.04+1)
|
|
||||||
-- Dumped by pg_dump version 15.5 (Ubuntu 15.5-1.pgdg20.04+1)
|
|
||||||
|
|
||||||
SET statement_timeout = 0;
|
|
||||||
SET lock_timeout = 0;
|
|
||||||
SET idle_in_transaction_session_timeout = 0;
|
|
||||||
SET client_encoding = 'UTF8';
|
|
||||||
SET standard_conforming_strings = on;
|
|
||||||
SELECT pg_catalog.set_config('search_path', '', false);
|
|
||||||
SET check_function_bodies = false;
|
|
||||||
SET xmloption = content;
|
|
||||||
SET client_min_messages = warning;
|
|
||||||
SET row_security = off;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Data for Name: audit_log_entries; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin
|
|
||||||
--
|
|
||||||
|
|
||||||
INSERT INTO "auth"."audit_log_entries" ("instance_id", "id", "payload", "created_at", "ip_address") VALUES
|
|
||||||
('00000000-0000-0000-0000-000000000000', '45d9f5dc-ae78-45aa-8dcf-7777639eafb3', '{"action":"user_confirmation_requested","actor_id":"31a03e74-1639-45b6-bfa7-77447f1a4762","actor_username":"test@makerkit.dev","actor_via_sso":false,"log_type":"user","traits":{"provider":"email"}}', '2024-04-20 08:20:34.463864+00', ''),
|
|
||||||
('00000000-0000-0000-0000-000000000000', 'a15ab66b-f291-427b-9f15-5364840b7cfb', '{"action":"user_signedup","actor_id":"31a03e74-1639-45b6-bfa7-77447f1a4762","actor_username":"test@makerkit.dev","actor_via_sso":false,"log_type":"team"}', '2024-04-20 08:20:38.163624+00', ''),
|
|
||||||
('00000000-0000-0000-0000-000000000000', 'c625f494-d041-44a9-8487-88f71cec3ca2', '{"action":"login","actor_id":"31a03e74-1639-45b6-bfa7-77447f1a4762","actor_username":"test@makerkit.dev","actor_via_sso":false,"log_type":"account","traits":{"provider_type":"email"}}', '2024-04-20 08:20:39.086702+00', ''),
|
|
||||||
('00000000-0000-0000-0000-000000000000', 'c579eedc-4089-4752-b5da-869d62b63c94', '{"action":"user_confirmation_requested","actor_id":"5c064f1b-78ee-4e1c-ac3b-e99aa97c99bf","actor_username":"owner@makerkit.dev","actor_via_sso":false,"log_type":"user","traits":{"provider":"email"}}', '2024-04-20 08:36:27.638468+00', ''),
|
|
||||||
('00000000-0000-0000-0000-000000000000', '9f0349fd-fd4a-4a78-926d-3045c086055d', '{"action":"user_signedup","actor_id":"5c064f1b-78ee-4e1c-ac3b-e99aa97c99bf","actor_username":"owner@makerkit.dev","actor_via_sso":false,"log_type":"team"}', '2024-04-20 08:36:37.517553+00', ''),
|
|
||||||
('00000000-0000-0000-0000-000000000000', '0a7673c0-8a4c-4984-a1f2-2b02d829fc85', '{"action":"login","actor_id":"5c064f1b-78ee-4e1c-ac3b-e99aa97c99bf","actor_username":"owner@makerkit.dev","actor_via_sso":false,"log_type":"account","traits":{"provider_type":"email"}}', '2024-04-20 08:36:37.613295+00', ''),
|
|
||||||
('00000000-0000-0000-0000-000000000000', '4f1b635c-f00b-46a4-80de-1edb48250f8b', '{"action":"logout","actor_id":"5c064f1b-78ee-4e1c-ac3b-e99aa97c99bf","actor_username":"owner@makerkit.dev","actor_via_sso":false,"log_type":"account"}', '2024-04-20 08:36:50.193801+00', ''),
|
|
||||||
('00000000-0000-0000-0000-000000000000', 'f0ec2dd0-b870-4822-a606-4208b7e17add', '{"action":"logout","actor_id":"31a03e74-1639-45b6-bfa7-77447f1a4762","actor_username":"test@makerkit.dev","actor_via_sso":false,"log_type":"account"}', '2024-04-20 08:37:13.584462+00', ''),
|
|
||||||
('00000000-0000-0000-0000-000000000000', '9b0174f5-c278-4e28-ba16-d3a845345abb', '{"action":"user_confirmation_requested","actor_id":"b73eb03e-fb7a-424d-84ff-18e2791ce0b4","actor_username":"custom@makerkit.dev","actor_via_sso":false,"log_type":"user","traits":{"provider":"email"}}', '2024-04-20 08:37:43.343197+00', ''),
|
|
||||||
('00000000-0000-0000-0000-000000000000', '26275d69-7031-45e3-a0b4-9d48dbeab82a', '{"action":"user_signedup","actor_id":"b73eb03e-fb7a-424d-84ff-18e2791ce0b4","actor_username":"custom@makerkit.dev","actor_via_sso":false,"log_type":"team"}', '2024-04-20 08:38:00.859507+00', ''),
|
|
||||||
('00000000-0000-0000-0000-000000000000', '498c38bf-318b-4653-b26b-d4e6daaca4f1', '{"action":"login","actor_id":"b73eb03e-fb7a-424d-84ff-18e2791ce0b4","actor_username":"custom@makerkit.dev","actor_via_sso":false,"log_type":"account","traits":{"provider_type":"email"}}', '2024-04-20 08:38:00.937756+00', ''),
|
|
||||||
('00000000-0000-0000-0000-000000000000', 'f53e4bd9-87d3-42f7-8b83-5fa9a9b914fe', '{"action":"login","actor_id":"31a03e74-1639-45b6-bfa7-77447f1a4762","actor_username":"test@makerkit.dev","actor_via_sso":false,"log_type":"account","traits":{"provider":"email"}}', '2024-04-20 08:40:08.032833+00', ''),
|
|
||||||
('00000000-0000-0000-0000-000000000000', '24e13912-f40c-457c-a380-ee079b904df8', '{"action":"user_confirmation_requested","actor_id":"6b83d656-e4ab-48e3-a062-c0c54a427368","actor_username":"member@makerkit.dev","actor_via_sso":false,"log_type":"user","traits":{"provider":"email"}}', '2024-04-20 08:41:08.68908+00', ''),
|
|
||||||
('00000000-0000-0000-0000-000000000000', '4341c565-778d-4ea1-8ccc-1b99674c1c40', '{"action":"user_signedup","actor_id":"6b83d656-e4ab-48e3-a062-c0c54a427368","actor_username":"member@makerkit.dev","actor_via_sso":false,"log_type":"team"}', '2024-04-20 08:41:15.376428+00', ''),
|
|
||||||
('00000000-0000-0000-0000-000000000000', 'bb38e0c7-4cb1-4eea-8a06-5ee4b34b261a', '{"action":"login","actor_id":"6b83d656-e4ab-48e3-a062-c0c54a427368","actor_username":"member@makerkit.dev","actor_via_sso":false,"log_type":"account","traits":{"provider_type":"email"}}', '2024-04-20 08:41:15.484323+00', '');
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Data for Name: flow_state; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin
|
-- Data for Name: flow_state; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin
|
||||||
@@ -102,47 +57,32 @@ INSERT INTO "auth"."audit_log_entries" ("instance_id", "id", "payload", "created
|
|||||||
|
|
||||||
INSERT INTO "auth"."users" ("instance_id", "id", "aud", "role", "email", "encrypted_password", "email_confirmed_at", "invited_at", "confirmation_token", "confirmation_sent_at", "recovery_token", "recovery_sent_at", "email_change_token_new", "email_change", "email_change_sent_at", "last_sign_in_at", "raw_app_meta_data", "raw_user_meta_data", "is_super_admin", "created_at", "updated_at", "phone", "phone_confirmed_at", "phone_change", "phone_change_token", "phone_change_sent_at", "email_change_token_current", "email_change_confirm_status", "banned_until", "reauthentication_token", "reauthentication_sent_at", "is_sso_user", "deleted_at", "is_anonymous") VALUES
|
INSERT INTO "auth"."users" ("instance_id", "id", "aud", "role", "email", "encrypted_password", "email_confirmed_at", "invited_at", "confirmation_token", "confirmation_sent_at", "recovery_token", "recovery_sent_at", "email_change_token_new", "email_change", "email_change_sent_at", "last_sign_in_at", "raw_app_meta_data", "raw_user_meta_data", "is_super_admin", "created_at", "updated_at", "phone", "phone_confirmed_at", "phone_change", "phone_change_token", "phone_change_sent_at", "email_change_token_current", "email_change_confirm_status", "banned_until", "reauthentication_token", "reauthentication_sent_at", "is_sso_user", "deleted_at", "is_anonymous") VALUES
|
||||||
('00000000-0000-0000-0000-000000000000', 'b73eb03e-fb7a-424d-84ff-18e2791ce0b4', 'authenticated', 'authenticated', 'custom@makerkit.dev', '$2a$10$b3ZPpU6TU3or30QzrXnZDuATPAx2pPq3JW.sNaneVY3aafMSuR4yi', '2024-04-20 08:38:00.860548+00', NULL, '', '2024-04-20 08:37:43.343769+00', '', NULL, '', '', NULL, '2024-04-20 08:38:00.93864+00', '{"provider": "email", "providers": ["email"]}', '{"sub": "b73eb03e-fb7a-424d-84ff-18e2791ce0b4", "email": "custom@makerkit.dev", "email_verified": false, "phone_verified": false}', NULL, '2024-04-20 08:37:43.3385+00', '2024-04-20 08:38:00.942809+00', NULL, NULL, '', '', NULL, '', 0, NULL, '', NULL, false, NULL, false),
|
('00000000-0000-0000-0000-000000000000', 'b73eb03e-fb7a-424d-84ff-18e2791ce0b4', 'authenticated', 'authenticated', 'custom@makerkit.dev', '$2a$10$b3ZPpU6TU3or30QzrXnZDuATPAx2pPq3JW.sNaneVY3aafMSuR4yi', '2024-04-20 08:38:00.860548+00', NULL, '', '2024-04-20 08:37:43.343769+00', '', NULL, '', '', NULL, '2024-04-20 08:38:00.93864+00', '{"provider": "email", "providers": ["email"]}', '{"sub": "b73eb03e-fb7a-424d-84ff-18e2791ce0b4", "email": "custom@makerkit.dev", "email_verified": false, "phone_verified": false}', NULL, '2024-04-20 08:37:43.3385+00', '2024-04-20 08:38:00.942809+00', NULL, NULL, '', '', NULL, '', 0, NULL, '', NULL, false, NULL, false),
|
||||||
('00000000-0000-0000-0000-000000000000', '31a03e74-1639-45b6-bfa7-77447f1a4762', 'authenticated', 'authenticated', 'test@makerkit.dev', '$2a$10$uC911UKLmGbkp2Di7K2UfO9QYUQFz5et.gPpjhxWEob1jeXkLrNZu', '2024-04-20 08:20:38.165331+00', NULL, '', '2024-04-20 08:20:34.464746+00', '', NULL, '', '', NULL, '2024-04-20 08:40:08.033665+00', '{"provider": "email", "providers": ["email"]}', '{"sub": "31a03e74-1639-45b6-bfa7-77447f1a4762", "email": "test@makerkit.dev", "email_verified": false, "phone_verified": false}', NULL, '2024-04-20 08:20:34.459113+00', '2024-04-20 08:40:08.035046+00', NULL, NULL, '', '', NULL, '', 0, NULL, '', NULL, false, NULL, false),
|
('00000000-0000-0000-0000-000000000000', '31a03e74-1639-45b6-bfa7-77447f1a4762', 'authenticated', 'authenticated', 'test@makerkit.dev', '$2a$10$NaMVRrI7NyfwP.AfAVWt6O/abulGnf9BBqwa6DqdMwXMvOCGpAnVO', '2024-04-20 08:20:38.165331+00', NULL, '', NULL, '', NULL, '', '', NULL, '2024-04-20 09:36:02.521776+00', '{"provider": "email", "providers": ["email"]}', '{"sub": "31a03e74-1639-45b6-bfa7-77447f1a4762", "email": "test@makerkit.dev", "email_verified": false, "phone_verified": false}', NULL, '2024-04-20 08:20:34.459113+00', '2024-04-20 10:07:48.554125+00', NULL, NULL, '', '', NULL, '', 0, NULL, '', NULL, false, NULL, false),
|
||||||
('00000000-0000-0000-0000-000000000000', '5c064f1b-78ee-4e1c-ac3b-e99aa97c99bf', 'authenticated', 'authenticated', 'owner@makerkit.dev', '$2a$10$D6arGxWJShy8q4RTW18z7eW0vEm2hOxEUovUCj5f3NblyHfamm5/a', '2024-04-20 08:36:37.517993+00', NULL, '', '2024-04-20 08:36:27.639648+00', '', NULL, '', '', NULL, '2024-04-20 08:36:37.614337+00', '{"provider": "email", "providers": ["email"]}', '{"sub": "5c064f1b-78ee-4e1c-ac3b-e99aa97c99bf", "email": "owner@makerkit.dev", "email_verified": false, "phone_verified": false}', NULL, '2024-04-20 08:36:27.630379+00', '2024-04-20 08:36:37.617955+00', NULL, NULL, '', '', NULL, '', 0, NULL, '', NULL, false, NULL, false),
|
('00000000-0000-0000-0000-000000000000', '5c064f1b-78ee-4e1c-ac3b-e99aa97c99bf', 'authenticated', 'authenticated', 'owner@makerkit.dev', '$2a$10$D6arGxWJShy8q4RTW18z7eW0vEm2hOxEUovUCj5f3NblyHfamm5/a', '2024-04-20 08:36:37.517993+00', NULL, '', '2024-04-20 08:36:27.639648+00', '', NULL, '', '', NULL, '2024-04-20 08:36:37.614337+00', '{"provider": "email", "providers": ["email"]}', '{"sub": "5c064f1b-78ee-4e1c-ac3b-e99aa97c99bf", "email": "owner@makerkit.dev", "email_verified": false, "phone_verified": false}', NULL, '2024-04-20 08:36:27.630379+00', '2024-04-20 08:36:37.617955+00', NULL, NULL, '', '', NULL, '', 0, NULL, '', NULL, false, NULL, false),
|
||||||
('00000000-0000-0000-0000-000000000000', '6b83d656-e4ab-48e3-a062-c0c54a427368', 'authenticated', 'authenticated', 'member@makerkit.dev', '$2a$10$6h/x.AX.6zzphTfDXIJMzuYx13hIYEi/Iods9FXH19J2VxhsLycfa', '2024-04-20 08:41:15.376778+00', NULL, '', '2024-04-20 08:41:08.689674+00', '', NULL, '', '', NULL, '2024-04-20 08:41:15.484606+00', '{"provider": "email", "providers": ["email"]}', '{"sub": "6b83d656-e4ab-48e3-a062-c0c54a427368", "email": "member@makerkit.dev", "email_verified": false, "phone_verified": false}', NULL, '2024-04-20 08:41:08.683395+00', '2024-04-20 08:41:15.485494+00', NULL, NULL, '', '', NULL, '', 0, NULL, '', NULL, false, NULL, false);
|
('00000000-0000-0000-0000-000000000000', '6b83d656-e4ab-48e3-a062-c0c54a427368', 'authenticated', 'authenticated', 'member@makerkit.dev', '$2a$10$6h/x.AX.6zzphTfDXIJMzuYx13hIYEi/Iods9FXH19J2VxhsLycfa', '2024-04-20 08:41:15.376778+00', NULL, '', '2024-04-20 08:41:08.689674+00', '', NULL, '', '', NULL, '2024-04-20 08:41:15.484606+00', '{"provider": "email", "providers": ["email"]}', '{"sub": "6b83d656-e4ab-48e3-a062-c0c54a427368", "email": "member@makerkit.dev", "email_verified": false, "phone_verified": false}', NULL, '2024-04-20 08:41:08.683395+00', '2024-04-20 08:41:15.485494+00', NULL, NULL, '', '', NULL, '', 0, NULL, '', NULL, false, NULL, false);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Data for Name: identities; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin
|
-- Data for Name: identities; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin
|
||||||
--
|
--
|
||||||
|
|
||||||
INSERT INTO "auth"."identities" ("provider_id", "user_id", "identity_data", "provider", "last_sign_in_at", "created_at", "updated_at", "id") VALUES
|
INSERT INTO "auth"."identities" ("provider_id", "user_id", "identity_data", "provider", "last_sign_in_at", "created_at", "updated_at", "id") VALUES
|
||||||
('31a03e74-1639-45b6-bfa7-77447f1a4762', '31a03e74-1639-45b6-bfa7-77447f1a4762', '{"sub": "31a03e74-1639-45b6-bfa7-77447f1a4762", "email": "test@makerkit.dev", "email_verified": false, "phone_verified": false}', 'email', '2024-04-20 08:20:34.46275+00', '2024-04-20 08:20:34.462773+00', '2024-04-20 08:20:34.462773+00', '9bb58bad-24a4-41a8-9742-1b5b4e2d8abd'),
|
('31a03e74-1639-45b6-bfa7-77447f1a4762', '31a03e74-1639-45b6-bfa7-77447f1a4762', '{"sub": "31a03e74-1639-45b6-bfa7-77447f1a4762", "email": "test@makerkit.dev", "email_verified": false, "phone_verified": false}', 'email', '2024-04-20 08:20:34.46275+00', '2024-04-20 08:20:34.462773+00', '2024-04-20 08:20:34.462773+00', '9bb58bad-24a4-41a8-9742-1b5b4e2d8abd'), ('5c064f1b-78ee-4e1c-ac3b-e99aa97c99bf', '5c064f1b-78ee-4e1c-ac3b-e99aa97c99bf', '{"sub": "5c064f1b-78ee-4e1c-ac3b-e99aa97c99bf", "email": "owner@makerkit.dev", "email_verified": false, "phone_verified": false}', 'email', '2024-04-20 08:36:27.637388+00', '2024-04-20 08:36:27.637409+00', '2024-04-20 08:36:27.637409+00', '090598a1-ebba-4879-bbe3-38d517d5066f'),
|
||||||
('5c064f1b-78ee-4e1c-ac3b-e99aa97c99bf', '5c064f1b-78ee-4e1c-ac3b-e99aa97c99bf', '{"sub": "5c064f1b-78ee-4e1c-ac3b-e99aa97c99bf", "email": "owner@makerkit.dev", "email_verified": false, "phone_verified": false}', 'email', '2024-04-20 08:36:27.637388+00', '2024-04-20 08:36:27.637409+00', '2024-04-20 08:36:27.637409+00', '090598a1-ebba-4879-bbe3-38d517d5066f'),
|
|
||||||
('b73eb03e-fb7a-424d-84ff-18e2791ce0b4', 'b73eb03e-fb7a-424d-84ff-18e2791ce0b4', '{"sub": "b73eb03e-fb7a-424d-84ff-18e2791ce0b4", "email": "custom@makerkit.dev", "email_verified": false, "phone_verified": false}', 'email', '2024-04-20 08:37:43.342194+00', '2024-04-20 08:37:43.342218+00', '2024-04-20 08:37:43.342218+00', '4392e228-a6d8-4295-a7d6-baed50c33e7c'),
|
('b73eb03e-fb7a-424d-84ff-18e2791ce0b4', 'b73eb03e-fb7a-424d-84ff-18e2791ce0b4', '{"sub": "b73eb03e-fb7a-424d-84ff-18e2791ce0b4", "email": "custom@makerkit.dev", "email_verified": false, "phone_verified": false}', 'email', '2024-04-20 08:37:43.342194+00', '2024-04-20 08:37:43.342218+00', '2024-04-20 08:37:43.342218+00', '4392e228-a6d8-4295-a7d6-baed50c33e7c'),
|
||||||
('6b83d656-e4ab-48e3-a062-c0c54a427368', '6b83d656-e4ab-48e3-a062-c0c54a427368', '{"sub": "6b83d656-e4ab-48e3-a062-c0c54a427368", "email": "member@makerkit.dev", "email_verified": false, "phone_verified": false}', 'email', '2024-04-20 08:41:08.687948+00', '2024-04-20 08:41:08.687982+00', '2024-04-20 08:41:08.687982+00', 'd122aca5-4f29-43f0-b1b1-940b000638db');
|
('6b83d656-e4ab-48e3-a062-c0c54a427368', '6b83d656-e4ab-48e3-a062-c0c54a427368', '{"sub": "6b83d656-e4ab-48e3-a062-c0c54a427368", "email": "member@makerkit.dev", "email_verified": false, "phone_verified": false}', 'email', '2024-04-20 08:41:08.687948+00', '2024-04-20 08:41:08.687982+00', '2024-04-20 08:41:08.687982+00', 'd122aca5-4f29-43f0-b1b1-940b000638db');
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Data for Name: instances; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin
|
-- Data for Name: instances; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin
|
||||||
--
|
--
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Data for Name: sessions; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin
|
-- Data for Name: sessions; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin
|
||||||
--
|
--
|
||||||
|
|
||||||
INSERT INTO "auth"."sessions" ("id", "user_id", "created_at", "updated_at", "factor_id", "aal", "not_after", "refreshed_at", "user_agent", "ip", "tag") VALUES
|
|
||||||
('6110bfeb-31e9-4c70-9c54-d723abd52048', 'b73eb03e-fb7a-424d-84ff-18e2791ce0b4', '2024-04-20 08:38:00.938815+00', '2024-04-20 08:38:00.938815+00', NULL, 'aal1', NULL, NULL, 'node', '192.168.228.1', NULL),
|
|
||||||
('d5086cc6-9897-47e0-874b-7f8f11649560', '31a03e74-1639-45b6-bfa7-77447f1a4762', '2024-04-20 08:40:08.033701+00', '2024-04-20 08:40:08.033701+00', NULL, 'aal1', NULL, NULL, 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36', '192.168.228.1', NULL),
|
|
||||||
('469be37b-b21f-46aa-b30b-52f828b1baad', '6b83d656-e4ab-48e3-a062-c0c54a427368', '2024-04-20 08:41:15.484635+00', '2024-04-20 08:41:15.484635+00', NULL, 'aal1', NULL, NULL, 'node', '192.168.228.1', NULL);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Data for Name: mfa_amr_claims; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin
|
-- Data for Name: mfa_amr_claims; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin
|
||||||
--
|
--
|
||||||
|
|
||||||
INSERT INTO "auth"."mfa_amr_claims" ("session_id", "created_at", "updated_at", "authentication_method", "id") VALUES
|
|
||||||
('6110bfeb-31e9-4c70-9c54-d723abd52048', '2024-04-20 08:38:00.943514+00', '2024-04-20 08:38:00.943514+00', 'email/signup', '42337df0-b3d0-42f5-bff5-3740ccad191b'),
|
|
||||||
('d5086cc6-9897-47e0-874b-7f8f11649560', '2024-04-20 08:40:08.035464+00', '2024-04-20 08:40:08.035464+00', 'password', 'e0d54b55-9813-44b5-b502-06b081c3a44b'),
|
|
||||||
('469be37b-b21f-46aa-b30b-52f828b1baad', '2024-04-20 08:41:15.485666+00', '2024-04-20 08:41:15.485666+00', 'email/signup', '55704b9a-6a31-48e6-ae2b-662f6b7ce302');
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Data for Name: mfa_factors; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin
|
-- Data for Name: mfa_factors; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin
|
||||||
@@ -160,12 +100,6 @@ INSERT INTO "auth"."mfa_amr_claims" ("session_id", "created_at", "updated_at", "
|
|||||||
-- Data for Name: refresh_tokens; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin
|
-- Data for Name: refresh_tokens; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin
|
||||||
--
|
--
|
||||||
|
|
||||||
INSERT INTO "auth"."refresh_tokens" ("instance_id", "id", "token", "user_id", "revoked", "created_at", "updated_at", "parent", "session_id") VALUES
|
|
||||||
('00000000-0000-0000-0000-000000000000', 3, 'lRWBgOYDQHq2GfqAc5iPpw', 'b73eb03e-fb7a-424d-84ff-18e2791ce0b4', false, '2024-04-20 08:38:00.940523+00', '2024-04-20 08:38:00.940523+00', NULL, '6110bfeb-31e9-4c70-9c54-d723abd52048'),
|
|
||||||
('00000000-0000-0000-0000-000000000000', 4, 'XuRjZ1Ipdh8Eb19lPRUJhw', '31a03e74-1639-45b6-bfa7-77447f1a4762', false, '2024-04-20 08:40:08.03433+00', '2024-04-20 08:40:08.03433+00', NULL, 'd5086cc6-9897-47e0-874b-7f8f11649560'),
|
|
||||||
('00000000-0000-0000-0000-000000000000', 5, 'iQ0e1JiLv3R29gkHZYZggw', '6b83d656-e4ab-48e3-a062-c0c54a427368', false, '2024-04-20 08:41:15.484992+00', '2024-04-20 08:41:15.484992+00', NULL, '469be37b-b21f-46aa-b30b-52f828b1baad');
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Data for Name: sso_providers; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin
|
-- Data for Name: sso_providers; Type: TABLE DATA; Schema: auth; Owner: supabase_auth_admin
|
||||||
--
|
--
|
||||||
@@ -201,23 +135,15 @@ INSERT INTO "auth"."refresh_tokens" ("instance_id", "id", "token", "user_id", "r
|
|||||||
--
|
--
|
||||||
|
|
||||||
INSERT INTO "public"."accounts" ("id", "primary_owner_user_id", "name", "slug", "email", "is_personal_account", "updated_at", "created_at", "created_by", "updated_by", "picture_url", "public_data") VALUES
|
INSERT INTO "public"."accounts" ("id", "primary_owner_user_id", "name", "slug", "email", "is_personal_account", "updated_at", "created_at", "created_by", "updated_by", "picture_url", "public_data") VALUES
|
||||||
('31a03e74-1639-45b6-bfa7-77447f1a4762', '31a03e74-1639-45b6-bfa7-77447f1a4762', 'test', NULL, 'test@makerkit.dev', true, NULL, NULL, NULL, NULL, NULL, '{}'),
|
('5deaa894-2094-4da3-b4fd-1fada0809d1c', '31a03e74-1639-45b6-bfa7-77447f1a4762', 'Makerkit', 'makerkit', NULL, false, NULL, NULL, NULL, NULL, NULL, '{}');
|
||||||
('5deaa894-2094-4da3-b4fd-1fada0809d1c', '31a03e74-1639-45b6-bfa7-77447f1a4762', 'Makerkit', 'makerkit', NULL, false, NULL, NULL, NULL, NULL, NULL, '{}'),
|
|
||||||
('5c064f1b-78ee-4e1c-ac3b-e99aa97c99bf', '5c064f1b-78ee-4e1c-ac3b-e99aa97c99bf', 'owner', NULL, 'owner@makerkit.dev', true, NULL, NULL, NULL, NULL, NULL, '{}'),
|
|
||||||
('b73eb03e-fb7a-424d-84ff-18e2791ce0b4', 'b73eb03e-fb7a-424d-84ff-18e2791ce0b4', 'custom', NULL, 'custom@makerkit.dev', true, NULL, NULL, NULL, NULL, NULL, '{}'),
|
|
||||||
('6b83d656-e4ab-48e3-a062-c0c54a427368', '6b83d656-e4ab-48e3-a062-c0c54a427368', 'member', NULL, 'member@makerkit.dev', true, NULL, NULL, NULL, NULL, NULL, '{}');
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Data for Name: roles; Type: TABLE DATA; Schema: public; Owner: postgres
|
-- Data for Name: roles; Type: TABLE DATA; Schema: public; Owner: postgres
|
||||||
--
|
--
|
||||||
|
|
||||||
INSERT INTO "public"."roles" ("name", "hierarchy_level", "account_id") VALUES
|
INSERT INTO "public"."roles" ("name", "hierarchy_level", "account_id") VALUES
|
||||||
('owner', 1, NULL),
|
|
||||||
('member', 2, NULL),
|
|
||||||
('custom-role', 4, '5deaa894-2094-4da3-b4fd-1fada0809d1c');
|
('custom-role', 4, '5deaa894-2094-4da3-b4fd-1fada0809d1c');
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Data for Name: accounts_memberships; Type: TABLE DATA; Schema: public; Owner: postgres
|
-- Data for Name: accounts_memberships; Type: TABLE DATA; Schema: public; Owner: postgres
|
||||||
--
|
--
|
||||||
@@ -235,14 +161,6 @@ INSERT INTO "public"."accounts_memberships" ("user_id", "account_id", "account_r
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Data for Name: config; Type: TABLE DATA; Schema: public; Owner: postgres
|
|
||||||
--
|
|
||||||
|
|
||||||
INSERT INTO "public"."config" ("enable_team_accounts", "enable_account_billing", "enable_team_account_billing", "billing_provider") VALUES
|
|
||||||
(true, true, true, 'stripe');
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Data for Name: invitations; Type: TABLE DATA; Schema: public; Owner: postgres
|
-- Data for Name: invitations; Type: TABLE DATA; Schema: public; Owner: postgres
|
||||||
--
|
--
|
||||||
@@ -260,21 +178,6 @@ INSERT INTO "public"."config" ("enable_team_accounts", "enable_account_billing",
|
|||||||
--
|
--
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Data for Name: role_permissions; Type: TABLE DATA; Schema: public; Owner: postgres
|
|
||||||
--
|
|
||||||
|
|
||||||
INSERT INTO "public"."role_permissions" ("id", "role", "permission") VALUES
|
|
||||||
(1, 'owner', 'roles.manage'),
|
|
||||||
(2, 'owner', 'billing.manage'),
|
|
||||||
(3, 'owner', 'settings.manage'),
|
|
||||||
(4, 'owner', 'members.manage'),
|
|
||||||
(5, 'owner', 'invites.manage'),
|
|
||||||
(6, 'member', 'settings.manage'),
|
|
||||||
(7, 'member', 'invites.manage');
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Data for Name: subscriptions; Type: TABLE DATA; Schema: public; Owner: postgres
|
-- Data for Name: subscriptions; Type: TABLE DATA; Schema: public; Owner: postgres
|
||||||
--
|
--
|
||||||
@@ -286,15 +189,10 @@ INSERT INTO "public"."role_permissions" ("id", "role", "permission") VALUES
|
|||||||
--
|
--
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Data for Name: buckets; Type: TABLE DATA; Schema: storage; Owner: supabase_storage_admin
|
-- Data for Name: buckets; Type: TABLE DATA; Schema: storage; Owner: supabase_storage_admin
|
||||||
--
|
--
|
||||||
|
|
||||||
INSERT INTO "storage"."buckets" ("id", "name", "owner", "created_at", "updated_at", "public", "avif_autodetection", "file_size_limit", "allowed_mime_types", "owner_id") VALUES
|
|
||||||
('account_image', 'account_image', NULL, '2024-04-20 08:18:41.364926+00', '2024-04-20 08:18:41.364926+00', true, false, NULL, NULL, NULL);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Data for Name: objects; Type: TABLE DATA; Schema: storage; Owner: supabase_storage_admin
|
-- Data for Name: objects; Type: TABLE DATA; Schema: storage; Owner: supabase_storage_admin
|
||||||
--
|
--
|
||||||
@@ -317,34 +215,10 @@ INSERT INTO "storage"."buckets" ("id", "name", "owner", "created_at", "updated_a
|
|||||||
-- Data for Name: hooks; Type: TABLE DATA; Schema: supabase_functions; Owner: supabase_functions_admin
|
-- Data for Name: hooks; Type: TABLE DATA; Schema: supabase_functions; Owner: supabase_functions_admin
|
||||||
--
|
--
|
||||||
|
|
||||||
INSERT INTO "supabase_functions"."hooks" ("id", "hook_table_id", "hook_name", "created_at", "request_id") VALUES
|
|
||||||
(1, 17811, 'invitations_insert', '2024-04-20 08:22:15.403223+00', 1),
|
|
||||||
(2, 17811, 'invitations_insert', '2024-04-20 08:22:15.403223+00', 2),
|
|
||||||
(3, 17811, 'invitations_insert', '2024-04-20 08:22:15.403223+00', 3),
|
|
||||||
(4, 17811, 'invitations_insert', '2024-04-20 08:25:04.593848+00', 4),
|
|
||||||
(5, 17811, 'invitations_insert', '2024-04-20 08:25:04.593848+00', 5),
|
|
||||||
(6, 17811, 'invitations_insert', '2024-04-20 08:25:04.593848+00', 6),
|
|
||||||
(7, 17811, 'invitations_insert', '2024-04-20 08:26:43.163367+00', 7),
|
|
||||||
(8, 17811, 'invitations_insert', '2024-04-20 08:26:43.163367+00', 8),
|
|
||||||
(9, 17811, 'invitations_insert', '2024-04-20 08:26:43.163367+00', 9),
|
|
||||||
(10, 17811, 'invitations_insert', '2024-04-20 08:28:46.753609+00', 10),
|
|
||||||
(11, 17811, 'invitations_insert', '2024-04-20 08:28:46.753609+00', 11),
|
|
||||||
(12, 17811, 'invitations_insert', '2024-04-20 08:28:46.753609+00', 12),
|
|
||||||
(13, 17811, 'invitations_insert', '2024-04-20 08:30:12.356719+00', 13),
|
|
||||||
(14, 17811, 'invitations_insert', '2024-04-20 08:33:11.210097+00', 14),
|
|
||||||
(15, 17811, 'invitations_insert', '2024-04-20 08:33:52.113026+00', 15),
|
|
||||||
(16, 17811, 'invitations_insert', '2024-04-20 08:35:21.557382+00', 16),
|
|
||||||
(17, 17811, 'invitations_insert', '2024-04-20 08:35:21.557382+00', 17),
|
|
||||||
(18, 17811, 'invitations_insert', '2024-04-20 08:35:21.557382+00', 18),
|
|
||||||
(19, 17811, 'invitations_insert', '2024-04-20 08:40:20.99569+00', 19);
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Data for Name: secrets; Type: TABLE DATA; Schema: vault; Owner: supabase_admin
|
-- Data for Name: secrets; Type: TABLE DATA; Schema: vault; Owner: supabase_admin
|
||||||
--
|
--
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: refresh_tokens_id_seq; Type: SEQUENCE SET; Schema: auth; Owner: supabase_auth_admin
|
-- Name: refresh_tokens_id_seq; Type: SEQUENCE SET; Schema: auth; Owner: supabase_auth_admin
|
||||||
--
|
--
|
||||||
@@ -385,10 +259,3 @@ SELECT pg_catalog.setval('"public"."role_permissions_id_seq"', 7, true);
|
|||||||
--
|
--
|
||||||
|
|
||||||
SELECT pg_catalog.setval('"supabase_functions"."hooks_id_seq"', 19, true);
|
SELECT pg_catalog.setval('"supabase_functions"."hooks_id_seq"', 19, true);
|
||||||
|
|
||||||
|
|
||||||
--
|
|
||||||
-- PostgreSQL database dump complete
|
|
||||||
--
|
|
||||||
|
|
||||||
RESET ALL;
|
|
||||||
|
|||||||
@@ -56,16 +56,23 @@ export function AccountMembersTable({
|
|||||||
const { t } = useTranslation('teams');
|
const { t } = useTranslation('teams');
|
||||||
|
|
||||||
const permissions = {
|
const permissions = {
|
||||||
canUpdateRole: (targetRole: number) =>
|
canUpdateRole: (targetRole: number) => {
|
||||||
canManageRoles && targetRole < userRoleHierarchy,
|
return (
|
||||||
canRemoveFromAccount: (targetRole: number) =>
|
isPrimaryOwner || (canManageRoles && userRoleHierarchy < targetRole)
|
||||||
canManageRoles && targetRole < userRoleHierarchy,
|
);
|
||||||
|
},
|
||||||
|
canRemoveFromAccount: (targetRole: number) => {
|
||||||
|
return (
|
||||||
|
isPrimaryOwner || (canManageRoles && userRoleHierarchy < targetRole)
|
||||||
|
);
|
||||||
|
},
|
||||||
canTransferOwnership: isPrimaryOwner,
|
canTransferOwnership: isPrimaryOwner,
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns = useGetColumns(permissions, {
|
const columns = useGetColumns(permissions, {
|
||||||
currentUserId,
|
currentUserId,
|
||||||
currentAccountId,
|
currentAccountId,
|
||||||
|
currentRoleHierarchy: userRoleHierarchy,
|
||||||
});
|
});
|
||||||
|
|
||||||
const filteredMembers = members.filter((member) => {
|
const filteredMembers = members.filter((member) => {
|
||||||
@@ -96,6 +103,7 @@ function useGetColumns(
|
|||||||
params: {
|
params: {
|
||||||
currentUserId: string;
|
currentUserId: string;
|
||||||
currentAccountId: string;
|
currentAccountId: string;
|
||||||
|
currentRoleHierarchy: number;
|
||||||
},
|
},
|
||||||
): ColumnDef<Members[0]>[] {
|
): ColumnDef<Members[0]>[] {
|
||||||
const { t } = useTranslation('teams');
|
const { t } = useTranslation('teams');
|
||||||
@@ -173,6 +181,7 @@ function useGetColumns(
|
|||||||
member={row.original}
|
member={row.original}
|
||||||
currentUserId={params.currentUserId}
|
currentUserId={params.currentUserId}
|
||||||
accountId={params.currentAccountId}
|
accountId={params.currentAccountId}
|
||||||
|
currentRoleHierarchy={params.currentRoleHierarchy}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -185,12 +194,13 @@ function ActionsDropdown({
|
|||||||
permissions,
|
permissions,
|
||||||
member,
|
member,
|
||||||
currentUserId,
|
currentUserId,
|
||||||
accountId,
|
currentRoleHierarchy,
|
||||||
}: {
|
}: {
|
||||||
permissions: Permissions;
|
permissions: Permissions;
|
||||||
member: Members[0];
|
member: Members[0];
|
||||||
currentUserId: string;
|
currentUserId: string;
|
||||||
accountId: string;
|
accountId: string;
|
||||||
|
currentRoleHierarchy: number;
|
||||||
}) {
|
}) {
|
||||||
const [isRemoving, setIsRemoving] = useState(false);
|
const [isRemoving, setIsRemoving] = useState(false);
|
||||||
const [isTransferring, setIsTransferring] = useState(false);
|
const [isTransferring, setIsTransferring] = useState(false);
|
||||||
@@ -262,10 +272,10 @@ function ActionsDropdown({
|
|||||||
<UpdateMemberRoleDialog
|
<UpdateMemberRoleDialog
|
||||||
isOpen
|
isOpen
|
||||||
setIsOpen={setIsUpdatingRole}
|
setIsOpen={setIsUpdatingRole}
|
||||||
accountId={member.id}
|
|
||||||
userId={member.user_id}
|
userId={member.user_id}
|
||||||
userRole={member.role}
|
userRole={member.role}
|
||||||
userRoleHierarchy={memberRoleHierarchy}
|
teamAccountId={member.account_id}
|
||||||
|
userRoleHierarchy={currentRoleHierarchy}
|
||||||
/>
|
/>
|
||||||
</If>
|
</If>
|
||||||
|
|
||||||
@@ -274,7 +284,7 @@ function ActionsDropdown({
|
|||||||
isOpen
|
isOpen
|
||||||
setIsOpen={setIsTransferring}
|
setIsOpen={setIsTransferring}
|
||||||
targetDisplayName={member.name ?? member.email}
|
targetDisplayName={member.name ?? member.email}
|
||||||
accountId={accountId}
|
accountId={member.account_id}
|
||||||
userId={member.user_id}
|
userId={member.user_id}
|
||||||
/>
|
/>
|
||||||
</If>
|
</If>
|
||||||
|
|||||||
@@ -36,14 +36,14 @@ export const UpdateMemberRoleDialog: React.FC<{
|
|||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
setIsOpen: (isOpen: boolean) => void;
|
setIsOpen: (isOpen: boolean) => void;
|
||||||
userId: string;
|
userId: string;
|
||||||
accountId: string;
|
teamAccountId: string;
|
||||||
userRole: Role;
|
userRole: Role;
|
||||||
userRoleHierarchy: number;
|
userRoleHierarchy: number;
|
||||||
}> = ({
|
}> = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
setIsOpen,
|
setIsOpen,
|
||||||
userId,
|
userId,
|
||||||
accountId,
|
teamAccountId,
|
||||||
userRole,
|
userRole,
|
||||||
userRoleHierarchy,
|
userRoleHierarchy,
|
||||||
}) => {
|
}) => {
|
||||||
@@ -61,14 +61,14 @@ export const UpdateMemberRoleDialog: React.FC<{
|
|||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<RolesDataProvider
|
<RolesDataProvider
|
||||||
accountId={accountId}
|
accountId={teamAccountId}
|
||||||
maxRoleHierarchy={userRoleHierarchy}
|
maxRoleHierarchy={userRoleHierarchy}
|
||||||
>
|
>
|
||||||
{(data) => (
|
{(data) => (
|
||||||
<UpdateMemberForm
|
<UpdateMemberForm
|
||||||
setIsOpen={setIsOpen}
|
setIsOpen={setIsOpen}
|
||||||
userId={userId}
|
userId={userId}
|
||||||
accountId={accountId}
|
teamAccountId={teamAccountId}
|
||||||
userRole={userRole}
|
userRole={userRole}
|
||||||
roles={data}
|
roles={data}
|
||||||
/>
|
/>
|
||||||
@@ -82,13 +82,13 @@ export const UpdateMemberRoleDialog: React.FC<{
|
|||||||
function UpdateMemberForm({
|
function UpdateMemberForm({
|
||||||
userId,
|
userId,
|
||||||
userRole,
|
userRole,
|
||||||
accountId,
|
teamAccountId,
|
||||||
setIsOpen,
|
setIsOpen,
|
||||||
roles,
|
roles,
|
||||||
}: React.PropsWithChildren<{
|
}: React.PropsWithChildren<{
|
||||||
userId: string;
|
userId: string;
|
||||||
userRole: Role;
|
userRole: Role;
|
||||||
accountId: string;
|
teamAccountId: string;
|
||||||
setIsOpen: (isOpen: boolean) => void;
|
setIsOpen: (isOpen: boolean) => void;
|
||||||
roles: Role[];
|
roles: Role[];
|
||||||
}>) {
|
}>) {
|
||||||
@@ -99,7 +99,11 @@ function UpdateMemberForm({
|
|||||||
const onSubmit = ({ role }: { role: Role }) => {
|
const onSubmit = ({ role }: { role: Role }) => {
|
||||||
startTransition(async () => {
|
startTransition(async () => {
|
||||||
try {
|
try {
|
||||||
await updateMemberRoleAction({ accountId, userId, role });
|
await updateMemberRoleAction({
|
||||||
|
accountId: teamAccountId,
|
||||||
|
userId,
|
||||||
|
role,
|
||||||
|
});
|
||||||
|
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ export async function removeMemberFromAccountAction(
|
|||||||
userId,
|
userId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// revalidate all pages that depend on the account
|
||||||
|
revalidatePath('/home/[account]', 'layout');
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,12 +47,20 @@ export async function updateMemberRoleAction(
|
|||||||
await assertSession(client);
|
await assertSession(client);
|
||||||
|
|
||||||
const service = new AccountMembersService(client);
|
const service = new AccountMembersService(client);
|
||||||
|
const adminClient = getSupabaseServerActionClient({ admin: true });
|
||||||
|
|
||||||
await service.updateMemberRole({
|
// update the role of the member
|
||||||
|
await service.updateMemberRole(
|
||||||
|
{
|
||||||
accountId: params.accountId,
|
accountId: params.accountId,
|
||||||
userId: params.userId,
|
userId: params.userId,
|
||||||
role: params.role,
|
role: params.role,
|
||||||
});
|
},
|
||||||
|
adminClient,
|
||||||
|
);
|
||||||
|
|
||||||
|
// revalidate all pages that depend on the account
|
||||||
|
revalidatePath('/home/[account]', 'layout');
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,10 @@ export class AccountMembersService {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateMemberRole(params: z.infer<typeof UpdateMemberRoleSchema>) {
|
async updateMemberRole(
|
||||||
|
params: z.infer<typeof UpdateMemberRoleSchema>,
|
||||||
|
adminClient: SupabaseClient<Database>,
|
||||||
|
) {
|
||||||
const logger = await getLogger();
|
const logger = await getLogger();
|
||||||
|
|
||||||
const ctx = {
|
const ctx = {
|
||||||
@@ -67,9 +70,33 @@ export class AccountMembersService {
|
|||||||
...params,
|
...params,
|
||||||
};
|
};
|
||||||
|
|
||||||
logger.info(ctx, `Updating member role...`);
|
logger.info(ctx, `Validating permissions to update member role...`);
|
||||||
|
|
||||||
const { data, error } = await this.client
|
const { data: canActionAccountMember, error: accountError } =
|
||||||
|
await this.client.rpc('can_action_account_member', {
|
||||||
|
user_id: params.userId,
|
||||||
|
target_team_account_id: params.accountId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (accountError ?? !canActionAccountMember) {
|
||||||
|
logger.error(
|
||||||
|
{
|
||||||
|
...ctx,
|
||||||
|
accountError,
|
||||||
|
},
|
||||||
|
`Failed to validate permissions to update member role`,
|
||||||
|
);
|
||||||
|
|
||||||
|
throw new Error(`Failed to validate permissions to update member role`);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(ctx, `Permissions validated. Updating member role...`);
|
||||||
|
|
||||||
|
// we use the Admin client to update the role
|
||||||
|
// since we do not set any RLS policies on the accounts_memberships table
|
||||||
|
// for updating accounts_memberships. Instead, we use the can_action_account_member
|
||||||
|
// RPC to validate permissions to update the role
|
||||||
|
const { data, error } = await adminClient
|
||||||
.from('accounts_memberships')
|
.from('accounts_memberships')
|
||||||
.update({
|
.update({
|
||||||
account_role: params.role,
|
account_role: params.role,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user