chore: bump version to 2.23.2 and enhance team account creation (#440)
* chore: bump version to 2.23.2 and enhance team account creation - Updated application version from 2.23.1 to 2.23.2 in package.json. - Enhanced team account creation to support slugs for non-Latin names, including validation and UI updates. - Updated localization files to reflect new slug requirements and error messages. - Refactored related schemas and server actions to accommodate slug handling in team account creation and updates. * refactor: remove old trigger and function for adding current user to new account - Dropped the trigger "add_current_user_to_new_account" and the associated function from the database schema. - Updated permissions for the function public.create_team_account to ensure proper access control.
This commit is contained in:
committed by
GitHub
parent
e1bfbc8106
commit
0636f8cf11
@@ -0,0 +1,67 @@
|
||||
drop policy "create_org_account" on "public"."accounts";
|
||||
|
||||
drop function if exists "public"."create_team_account"(text);
|
||||
|
||||
set check_function_bodies = off;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.create_team_account(account_name text, user_id uuid, account_slug text DEFAULT NULL::text)
|
||||
RETURNS public.accounts
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path TO ''
|
||||
AS $function$
|
||||
declare
|
||||
new_account public.accounts;
|
||||
owner_role varchar(50);
|
||||
begin
|
||||
if (not public.is_set('enable_team_accounts')) then
|
||||
raise exception 'Team accounts are not enabled';
|
||||
end if;
|
||||
|
||||
-- Get the highest system role for the owner
|
||||
select public.get_upper_system_role() into owner_role;
|
||||
|
||||
-- Insert the new team account
|
||||
-- The slug will be auto-generated from name by the "set_slug_from_account_name"
|
||||
-- trigger if account_slug is null
|
||||
insert into public.accounts(
|
||||
name,
|
||||
slug,
|
||||
is_personal_account,
|
||||
primary_owner_user_id)
|
||||
values (
|
||||
account_name,
|
||||
account_slug,
|
||||
false,
|
||||
user_id)
|
||||
returning * into new_account;
|
||||
|
||||
-- Create membership for the owner (atomic with account creation)
|
||||
insert into public.accounts_memberships(
|
||||
account_id,
|
||||
user_id,
|
||||
account_role)
|
||||
values (
|
||||
new_account.id,
|
||||
user_id,
|
||||
coalesce(owner_role, 'owner'));
|
||||
|
||||
return new_account;
|
||||
|
||||
end;
|
||||
|
||||
$function$
|
||||
;
|
||||
|
||||
|
||||
|
||||
-- Revoke from all roles first to ensure exclusivity
|
||||
revoke all on function public.create_team_account(text, uuid, text) from public;
|
||||
revoke all on function public.create_team_account(text, uuid, text) from authenticated;
|
||||
|
||||
-- Grant only to service_role
|
||||
grant execute on function public.create_team_account(text, uuid, text) to service_role;
|
||||
|
||||
-- Drop trigger (handled by the new function)
|
||||
drop trigger if exists "add_current_user_to_new_account" on "public"."accounts";
|
||||
drop function if exists "kit"."add_current_user_to_new_account"();
|
||||
@@ -223,37 +223,6 @@ $$ language plpgsql;
|
||||
grant
|
||||
execute on function public.get_upper_system_role () to service_role;
|
||||
|
||||
-- Function "kit.add_current_user_to_new_account"
|
||||
-- Trigger to add the current user to a new account as the primary owner
|
||||
create
|
||||
or replace function kit.add_current_user_to_new_account () returns trigger language plpgsql security definer
|
||||
set
|
||||
search_path = '' as $$
|
||||
begin
|
||||
if new.primary_owner_user_id = auth.uid() then
|
||||
insert into public.accounts_memberships(
|
||||
account_id,
|
||||
user_id,
|
||||
account_role)
|
||||
values(
|
||||
new.id,
|
||||
auth.uid(),
|
||||
public.get_upper_system_role());
|
||||
|
||||
end if;
|
||||
|
||||
return NEW;
|
||||
|
||||
end;
|
||||
|
||||
$$;
|
||||
|
||||
-- trigger the function whenever a new account is created
|
||||
create trigger "add_current_user_to_new_account"
|
||||
after insert on public.accounts for each row
|
||||
when (new.is_personal_account = false)
|
||||
execute function kit.add_current_user_to_new_account ();
|
||||
|
||||
-- create a trigger to update the account email when the primary owner email is updated
|
||||
create
|
||||
or replace function kit.handle_update_user_email () returns trigger language plpgsql security definer
|
||||
@@ -470,36 +439,62 @@ execute procedure kit.setup_new_user ();
|
||||
* -------------------------------------------------------
|
||||
*/
|
||||
-- Function "public.create_team_account"
|
||||
-- Create a team account if team accounts are enabled
|
||||
-- Create a team account with membership in a single transaction
|
||||
-- Called by service_role only (Policies API enforced in application layer)
|
||||
create
|
||||
or replace function public.create_team_account (account_name text) returns public.accounts
|
||||
or replace function public.create_team_account (
|
||||
account_name text,
|
||||
user_id uuid,
|
||||
account_slug text default null
|
||||
) returns public.accounts
|
||||
language plpgsql
|
||||
security definer
|
||||
set
|
||||
search_path = '' as $$
|
||||
declare
|
||||
new_account public.accounts;
|
||||
owner_role varchar(50);
|
||||
begin
|
||||
if (not public.is_set('enable_team_accounts')) then
|
||||
raise exception 'Team accounts are not enabled';
|
||||
end if;
|
||||
|
||||
-- Get the highest system role for the owner
|
||||
select public.get_upper_system_role() into owner_role;
|
||||
|
||||
-- Insert the new team account
|
||||
-- The slug will be auto-generated from name by the "set_slug_from_account_name"
|
||||
-- trigger if account_slug is null
|
||||
insert into public.accounts(
|
||||
name,
|
||||
is_personal_account)
|
||||
slug,
|
||||
is_personal_account,
|
||||
primary_owner_user_id)
|
||||
values (
|
||||
account_name,
|
||||
false)
|
||||
returning
|
||||
* into new_account;
|
||||
account_slug,
|
||||
false,
|
||||
user_id)
|
||||
returning * into new_account;
|
||||
|
||||
-- Create membership for the owner (atomic with account creation)
|
||||
insert into public.accounts_memberships(
|
||||
account_id,
|
||||
user_id,
|
||||
account_role)
|
||||
values (
|
||||
new_account.id,
|
||||
user_id,
|
||||
coalesce(owner_role, 'owner'));
|
||||
|
||||
return new_account;
|
||||
|
||||
end;
|
||||
|
||||
$$ language plpgsql;
|
||||
$$;
|
||||
|
||||
grant
|
||||
execute on function public.create_team_account (text) to authenticated,
|
||||
service_role;
|
||||
execute on function public.create_team_account (text, uuid, text) to service_role;
|
||||
|
||||
-- RLS(public.accounts)
|
||||
-- Authenticated users can delete team accounts
|
||||
|
||||
@@ -9,12 +9,14 @@ select tests.create_supabase_user('test1', 'test1@test.com');
|
||||
|
||||
select tests.create_supabase_user('test2');
|
||||
|
||||
-- Create an team account
|
||||
-- Create team account using service_role (function is now service_role only)
|
||||
set local role service_role;
|
||||
|
||||
select public.create_team_account('Test', tests.get_supabase_uid('test1'));
|
||||
|
||||
-- Switch back to authenticated user for testing
|
||||
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(
|
||||
|
||||
@@ -9,14 +9,16 @@ select tests.create_supabase_user('test1', 'test1@test.com');
|
||||
|
||||
select tests.create_supabase_user('test2');
|
||||
|
||||
-- Create an team account
|
||||
-- Create team accounts using service_role (function is now service_role only)
|
||||
set local role service_role;
|
||||
|
||||
select public.create_team_account('Test', tests.get_supabase_uid('test1'));
|
||||
select public.create_team_account('Test', tests.get_supabase_uid('test1'));
|
||||
select public.create_team_account('Test', tests.get_supabase_uid('test1'));
|
||||
|
||||
-- Switch back to authenticated user for testing
|
||||
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' $$,
|
||||
|
||||
@@ -12,12 +12,19 @@ select
|
||||
select
|
||||
tests.create_supabase_user('test2');
|
||||
|
||||
-- Create an team account
|
||||
select
|
||||
makerkit.authenticate_as('test1');
|
||||
-- Create an team account (without explicit slug, should auto-generate)
|
||||
-- DON'T authenticate first - the add_current_user_to_new_account trigger
|
||||
-- would also create a membership if auth.uid() = primary_owner_user_id
|
||||
-- The function already creates the membership, so we avoid duplicate by keeping auth.uid() NULL
|
||||
set local role service_role;
|
||||
|
||||
select
|
||||
public.create_team_account('Test');
|
||||
public.create_team_account('Test', tests.get_supabase_uid('test1'));
|
||||
|
||||
-- Reset to postgres and then authenticate as test1 for proper RLS context
|
||||
set local role postgres;
|
||||
select
|
||||
makerkit.authenticate_as('test1');
|
||||
|
||||
select
|
||||
row_eq($$
|
||||
@@ -28,6 +35,44 @@ select
|
||||
'test'::text, 'Test'::varchar),
|
||||
'Users can create a team account');
|
||||
|
||||
-- Test creating team account with explicit slug parameter
|
||||
select
|
||||
tests.create_supabase_user('slugtest1', 'slugtest1@test.com');
|
||||
|
||||
-- Switch to service_role to call the function
|
||||
set local role service_role;
|
||||
|
||||
select
|
||||
public.create_team_account('Custom Team Name', tests.get_supabase_uid('slugtest1'), 'custom-slug-123');
|
||||
|
||||
-- Switch back to authenticated user for testing
|
||||
select
|
||||
makerkit.authenticate_as('slugtest1');
|
||||
|
||||
select
|
||||
row_eq($$
|
||||
select
|
||||
primary_owner_user_id, is_personal_account, slug, name
|
||||
from makerkit.get_account_by_slug('custom-slug-123') $$,
|
||||
row (tests.get_supabase_uid('slugtest1'), false,
|
||||
'custom-slug-123'::text, 'Custom Team Name'::varchar),
|
||||
'Users can create a team account with custom slug');
|
||||
|
||||
-- Verify membership is created for custom slug team
|
||||
select
|
||||
row_eq($$
|
||||
select
|
||||
account_role from public.accounts_memberships
|
||||
where
|
||||
account_id = (select id from public.accounts where slug = 'custom-slug-123')
|
||||
and user_id = tests.get_supabase_uid('slugtest1')
|
||||
$$, row ('owner'::varchar),
|
||||
'The primary owner should have the owner role for team with custom slug');
|
||||
|
||||
-- Switch back to test1 for testing the original 'test' account
|
||||
select
|
||||
makerkit.authenticate_as('test1');
|
||||
|
||||
-- Should be the primary owner of the team account by default
|
||||
select
|
||||
row_eq($$
|
||||
@@ -106,12 +151,13 @@ create or replace function kit.single_account_per_owner()
|
||||
declare
|
||||
total_accounts int;
|
||||
begin
|
||||
-- Check if this user already owns an account by checking NEW.primary_owner_user_id
|
||||
select
|
||||
count(id)
|
||||
from
|
||||
public.accounts
|
||||
where
|
||||
primary_owner_user_id = auth.uid() into total_accounts;
|
||||
primary_owner_user_id = NEW.primary_owner_user_id into total_accounts;
|
||||
|
||||
if total_accounts > 0 then
|
||||
raise exception 'User can only own 1 account';
|
||||
@@ -129,14 +175,13 @@ 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');
|
||||
-- Try to create another team account for the same owner (should fail due to trigger)
|
||||
set local role service_role;
|
||||
|
||||
select
|
||||
throws_ok(
|
||||
$$ select
|
||||
public.create_team_account('Test2') $$, 'User can only own 1 account');
|
||||
public.create_team_account('Test2', tests.get_supabase_uid('test1')) $$, 'User can only own 1 account');
|
||||
|
||||
set local role postgres;
|
||||
|
||||
@@ -151,11 +196,10 @@ select
|
||||
tests.create_supabase_user('updatetest2', 'updatetest2@test.com');
|
||||
|
||||
-- Create a team account for update tests
|
||||
select
|
||||
makerkit.authenticate_as('updatetest1');
|
||||
set local role service_role;
|
||||
|
||||
select
|
||||
public.create_team_account('UpdateTeam');
|
||||
public.create_team_account('UpdateTeam', tests.get_supabase_uid('updatetest1'));
|
||||
|
||||
-- Add updatetest2 as a member
|
||||
set local role postgres;
|
||||
@@ -259,11 +303,10 @@ select
|
||||
tests.create_supabase_user('roletest2', 'roletest2@test.com');
|
||||
|
||||
-- Create a team account for role tests
|
||||
select
|
||||
makerkit.authenticate_as('roletest1');
|
||||
set local role service_role;
|
||||
|
||||
select
|
||||
public.create_team_account('RoleTeam');
|
||||
public.create_team_account('RoleTeam', tests.get_supabase_uid('roletest1'));
|
||||
|
||||
-- Add roletest2 as a member
|
||||
set local role postgres;
|
||||
@@ -333,11 +376,10 @@ select
|
||||
tests.create_supabase_user('deletetest2', 'deletetest2@test.com');
|
||||
|
||||
-- Create a team account for delete tests
|
||||
select
|
||||
makerkit.authenticate_as('deletetest1');
|
||||
set local role service_role;
|
||||
|
||||
select
|
||||
public.create_team_account('DeleteTeam');
|
||||
public.create_team_account('DeleteTeam', tests.get_supabase_uid('deletetest1'));
|
||||
|
||||
-- Add deletetest2 as a member
|
||||
set local role postgres;
|
||||
@@ -383,8 +425,8 @@ 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');
|
||||
set local role service_role;
|
||||
select public.create_team_account('PermTeam', tests.get_supabase_uid('permtest1'));
|
||||
|
||||
-- Get the account ID for PermTeam to avoid NULL references
|
||||
set local role postgres;
|
||||
@@ -470,8 +512,8 @@ 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');
|
||||
set local role service_role;
|
||||
select public.create_team_account('HierTeam', tests.get_supabase_uid('hiertest1'));
|
||||
|
||||
-- Add users with different roles
|
||||
set local role postgres;
|
||||
@@ -540,8 +582,8 @@ 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');
|
||||
set local role service_role;
|
||||
select public.create_team_account('VisTeam', tests.get_supabase_uid('vistest1'));
|
||||
|
||||
-- Add vistest2 as a member
|
||||
set local role postgres;
|
||||
@@ -578,8 +620,8 @@ 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');
|
||||
set local role service_role;
|
||||
select public.create_team_account('FuncTeam', tests.get_supabase_uid('functest1'));
|
||||
|
||||
-- Test: get_account_members function properly restricts data
|
||||
select makerkit.authenticate_as('functest2');
|
||||
@@ -619,10 +661,11 @@ 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');
|
||||
set local role service_role;
|
||||
select public.create_team_account('TeamChange', tests.get_supabase_uid('ownerupdate1'));
|
||||
|
||||
-- Update the team name as the owner
|
||||
select makerkit.authenticate_as('ownerupdate1');
|
||||
select lives_ok(
|
||||
$$ UPDATE public.accounts
|
||||
SET name = 'Updated Owner Team'
|
||||
@@ -668,23 +711,16 @@ select
|
||||
tests.create_supabase_user('crosstest2', 'crosstest2@test.com');
|
||||
|
||||
-- Create first team account with crosstest1 as owner
|
||||
select
|
||||
makerkit.authenticate_as('crosstest1');
|
||||
set local role service_role;
|
||||
|
||||
select
|
||||
public.create_team_account('TeamA');
|
||||
public.create_team_account('TeamA', tests.get_supabase_uid('crosstest1'));
|
||||
|
||||
-- Create second team account with crosstest2 as owner
|
||||
select
|
||||
makerkit.authenticate_as('crosstest2');
|
||||
|
||||
select
|
||||
public.create_team_account('TeamB');
|
||||
public.create_team_account('TeamB', tests.get_supabase_uid('crosstest2'));
|
||||
|
||||
-- Add crosstest2 as a member to TeamA
|
||||
select
|
||||
makerkit.authenticate_as('crosstest1');
|
||||
|
||||
set local role postgres;
|
||||
|
||||
-- Add member to first team
|
||||
@@ -767,6 +803,31 @@ select
|
||||
'TeamB name should remain unchanged after attempted update by non-member'
|
||||
);
|
||||
|
||||
-- Test 7: Security - Public/anon role cannot execute create_team_account
|
||||
select
|
||||
tests.create_supabase_user('securitytest1', 'securitytest1@test.com');
|
||||
|
||||
-- Test as anon role (public) - should get permission denied (either for schema or function)
|
||||
set local role anon;
|
||||
|
||||
select
|
||||
throws_ok(
|
||||
$$ select public.create_team_account('SecurityTeam', tests.get_supabase_uid('securitytest1')) $$,
|
||||
'permission denied for schema public',
|
||||
'Anonymous/public role should not be able to execute create_team_account'
|
||||
);
|
||||
|
||||
-- Test as authenticated role (still should fail - only service_role is allowed)
|
||||
select
|
||||
makerkit.authenticate_as('securitytest1');
|
||||
|
||||
select
|
||||
throws_ok(
|
||||
$$ select public.create_team_account('SecurityTeam', tests.get_supabase_uid('securitytest1')) $$,
|
||||
'permission denied for function create_team_account',
|
||||
'Authenticated role should not be able to execute create_team_account directly'
|
||||
);
|
||||
|
||||
select
|
||||
*
|
||||
from
|
||||
|
||||
@@ -9,15 +9,15 @@ select plan(12);
|
||||
--- Create test users
|
||||
select tests.create_supabase_user('trigger_test_user1', 'test1@example.com');
|
||||
|
||||
-- Authenticate as test user
|
||||
select makerkit.authenticate_as('trigger_test_user1');
|
||||
|
||||
------------
|
||||
--- Test accounts table timestamp triggers - INSERT
|
||||
------------
|
||||
|
||||
INSERT INTO public.accounts (name, is_personal_account)
|
||||
VALUES ('Test Account', false);
|
||||
-- Use service_role to insert (create_org_account policy was removed)
|
||||
set role service_role;
|
||||
|
||||
INSERT INTO public.accounts (name, is_personal_account, primary_owner_user_id)
|
||||
VALUES ('Test Account', false, tests.get_supabase_uid('trigger_test_user1'));
|
||||
|
||||
SELECT ok(
|
||||
(SELECT created_at IS NOT NULL FROM public.accounts WHERE name = 'Test Account'),
|
||||
@@ -38,9 +38,9 @@ SELECT ok(
|
||||
--- Test invitations table timestamp triggers - INSERT
|
||||
------------
|
||||
|
||||
-- Create a team account for invitation testing
|
||||
INSERT INTO public.accounts (name, is_personal_account)
|
||||
VALUES ('Invitation Test Team', false);
|
||||
-- Create a team account for invitation testing (still in service_role from above)
|
||||
INSERT INTO public.accounts (name, is_personal_account, primary_owner_user_id)
|
||||
VALUES ('Invitation Test Team', false, tests.get_supabase_uid('trigger_test_user1'));
|
||||
|
||||
-- Switch to service_role to insert invitations (INSERT policy removed, handled by server action)
|
||||
set role service_role;
|
||||
@@ -56,9 +56,7 @@ VALUES (
|
||||
now() + interval '7 days'
|
||||
);
|
||||
|
||||
-- Switch back to authenticated user for assertion
|
||||
select makerkit.authenticate_as('trigger_test_user1');
|
||||
|
||||
-- Stay in service_role for assertions (testing triggers, not RLS)
|
||||
SELECT ok(
|
||||
(SELECT created_at IS NOT NULL FROM public.invitations WHERE email = 'invitee@example.com'),
|
||||
'invitations: created_at should be set automatically on insert'
|
||||
|
||||
@@ -13,12 +13,19 @@ select tests.create_supabase_user('user_tracking_test1', 'tracking1@example.com'
|
||||
--- Test accounts table user tracking triggers - INSERT
|
||||
------------
|
||||
|
||||
-- Authenticate as first user for insert
|
||||
-- Authenticate first to set JWT claims (for auth.uid() in triggers)
|
||||
select makerkit.authenticate_as('user_tracking_test1');
|
||||
|
||||
-- Switch to service_role for INSERT (create_org_account policy was removed)
|
||||
-- but JWT claims are preserved so auth.uid() still works in triggers
|
||||
set local role service_role;
|
||||
|
||||
-- Test INSERT: created_by and updated_by should be set to current user
|
||||
INSERT INTO public.accounts (name, is_personal_account)
|
||||
VALUES ('User Tracking Test Account', false);
|
||||
INSERT INTO public.accounts (name, is_personal_account, primary_owner_user_id)
|
||||
VALUES ('User Tracking Test Account', false, tests.get_supabase_uid('user_tracking_test1'));
|
||||
|
||||
-- Switch back to authenticated for assertions
|
||||
select makerkit.authenticate_as('user_tracking_test1');
|
||||
|
||||
SELECT ok(
|
||||
(SELECT created_by = tests.get_supabase_uid('user_tracking_test1')
|
||||
|
||||
@@ -7,15 +7,15 @@ select no_plan();
|
||||
select tests.create_supabase_user('update_test_owner', 'update-owner@test.com');
|
||||
select tests.create_supabase_user('update_test_member', 'update-member@test.com');
|
||||
|
||||
-- Authenticate as owner to create team account
|
||||
select makerkit.authenticate_as('update_test_owner');
|
||||
-- Create team account using service_role and create_team_account function
|
||||
-- DON'T authenticate first - the add_current_user_to_new_account trigger
|
||||
-- would also create a membership if auth.uid() = primary_owner_user_id
|
||||
-- The function already creates the membership, so we avoid duplicate by keeping auth.uid() NULL
|
||||
set local role service_role;
|
||||
|
||||
-- Create a team account (owner is added automatically via trigger)
|
||||
insert into public.accounts (name, is_personal_account)
|
||||
values ('Update Test Team', false);
|
||||
select public.create_team_account('Update Test Team', tests.get_supabase_uid('update_test_owner'));
|
||||
|
||||
-- Add member to the team with 'member' role using service_role
|
||||
set role service_role;
|
||||
-- Add member to the team with 'member' role (still in service_role)
|
||||
|
||||
insert into public.accounts_memberships (account_id, user_id, account_role)
|
||||
values (
|
||||
|
||||
Reference in New Issue
Block a user