This is barely functionnal, but at least it can display some data without crashing Most other functionnalities other than displaying events are broken, and so are the relative date operations, they need to be fixed asap
278 lines
9.8 KiB
PL/PgSQL
278 lines
9.8 KiB
PL/PgSQL
--
|
|
-- For use with https://github.com/supabase/supabase/tree/master/examples/slack-clone/nextjs-slack-clone
|
|
--
|
|
|
|
-- Custom types
|
|
create type public.app_permission as enum ('events.see.hidden', 'users.ban');
|
|
create type public.app_role as enum ('sa', 'admin', 'moderator', 'user');
|
|
create type public.app_colors as enum ('red', 'orange', 'amber', 'yellow', 'lime', 'green', 'emerald', 'teal', 'cyan', 'sky', 'blue', 'indigo', 'violet', 'purple', 'fuchsia', 'pink', 'rose', 'black', 'white');
|
|
|
|
-- DATA STRUCTURES
|
|
-- Users
|
|
create table public.users (
|
|
id uuid references auth.users not null primary key, -- UUID from auth.users
|
|
username text
|
|
);
|
|
comment on table public.users is 'Profile data for each user.';
|
|
comment on column public.users.id is 'References the internal Supabase Auth user.';
|
|
|
|
-- Roles
|
|
create table public.user_roles (
|
|
id bigint generated by default as identity primary key,
|
|
role app_role not null,
|
|
user_id uuid references public.users on delete cascade not null,
|
|
unique (user_id, role)
|
|
);
|
|
comment on table public.user_roles is 'Application roles for each user.';
|
|
|
|
-- Permissions
|
|
create table public.role_permissions (
|
|
id bigint generated by default as identity primary key,
|
|
role app_role not null,
|
|
permission app_permission not null,
|
|
unique (role, permission)
|
|
);
|
|
comment on table public.role_permissions is 'Application permissions for each role.';
|
|
|
|
-- Worlds
|
|
create table public.worlds (
|
|
id bigint generated by default as identity primary key,
|
|
name text not null,
|
|
description text,
|
|
color app_colors,
|
|
gm_id uuid references public.users on delete cascade
|
|
);
|
|
comment on table public.worlds is 'Worlds belonging to a single user ; a game master.';
|
|
|
|
-- World Calendars
|
|
create table public.world_calendars (
|
|
id bigint generated by default as identity primary key,
|
|
world_id bigint references public.worlds on delete cascade not null
|
|
);
|
|
comment on table public.world_calendars is 'Calendar settings and configuration attached to a single world.';
|
|
|
|
-- Calendar Months
|
|
create table public.calendar_months (
|
|
id bigint generated by default as identity primary key,
|
|
name text not null,
|
|
days int not null,
|
|
position int not null,
|
|
calendar_id bigint references public.world_calendars on delete cascade not null
|
|
);
|
|
comment on table public.calendar_months is 'A calendar month.';
|
|
|
|
-- Event categories
|
|
create table public.calendar_events_category (
|
|
id bigint generated by default as identity primary key,
|
|
name text not null,
|
|
unique (name)
|
|
);
|
|
comment on table public.calendar_events_category is 'Categories describing events';
|
|
|
|
-- Events
|
|
create table public.calendar_events (
|
|
id bigint generated by default as identity primary key,
|
|
title text not null,
|
|
description text,
|
|
start_date json not null,
|
|
end_date json,
|
|
category bigint references public.calendar_events_category on delete cascade,
|
|
hidden boolean,
|
|
wiki text,
|
|
calendar_id bigint references public.world_calendars on delete cascade not null
|
|
);
|
|
comment on table public.calendar_events is 'Events linked to a world';
|
|
|
|
-- Character categories
|
|
create table public.characters_category (
|
|
id bigint generated by default as identity primary key,
|
|
name text not null,
|
|
unique (name)
|
|
);
|
|
comment on table public.characters_category is 'Categories describing characters';
|
|
|
|
-- Characters
|
|
create table public.characters (
|
|
id bigint generated by default as identity primary key,
|
|
name text not null,
|
|
description text,
|
|
birth json not null,
|
|
death json,
|
|
category bigint references public.characters_category on delete cascade,
|
|
hidden_birth boolean,
|
|
hidden_death boolean,
|
|
wiki text,
|
|
world_id bigint references public.worlds on delete cascade not null
|
|
);
|
|
comment on table public.characters is 'Characters linked to a world';
|
|
|
|
-- authorize with role-based access control (RBAC)
|
|
create function public.authorize(
|
|
requested_permission app_permission
|
|
)
|
|
returns boolean as $$
|
|
declare
|
|
bind_permissions int;
|
|
begin
|
|
select count(*)
|
|
from public.role_permissions
|
|
where role_permissions.permission = authorize.requested_permission
|
|
and role_permissions.role = (auth.jwt() ->> 'user_role')::public.app_role
|
|
into bind_permissions;
|
|
|
|
return bind_permissions > 0;
|
|
end;
|
|
$$ language plpgsql security definer set search_path = public;
|
|
|
|
-- Secure the tables
|
|
alter table public.users enable row level security;
|
|
alter table public.user_roles enable row level security;
|
|
alter table public.role_permissions enable row level security;
|
|
alter table public.worlds enable row level security;
|
|
alter table public.characters_category enable row level security;
|
|
alter table public.characters enable row level security;
|
|
alter table public.world_calendars enable row level security;
|
|
alter table public.calendar_months enable row level security;
|
|
alter table public.calendar_events enable row level security;
|
|
alter table public.calendar_events_category enable row level security;
|
|
|
|
-- User policies
|
|
create policy "Allow logged-in read access" on public.users for select using ( auth.role() = 'authenticated' );
|
|
create policy "Allow individual insert access" on public.users for insert with check ( auth.uid() = id );
|
|
create policy "Allow individual update access" on public.users for update using ( auth.uid() = id );
|
|
create policy "Allow individual read access" on public.user_roles for select using ( auth.uid() = user_id );
|
|
|
|
-- World policies
|
|
create policy "Allow individual read access for GMs" on public.worlds for select using ( ( auth.uid() = gm_id ) );
|
|
create policy "Allow individual insert access for GMs" on public.worlds for insert with check ( auth.uid() = gm_id );
|
|
create policy "Allow individual update access for GMs" on public.worlds for update using ( auth.uid() = gm_id );
|
|
|
|
-- Calendar policies
|
|
create policy "Allow individual read access for GMs" on public.world_calendars for select using (
|
|
exists (
|
|
select 1 from worlds
|
|
where worlds.id = world_calendars.world_id
|
|
)
|
|
);
|
|
create policy "Allow individual insert access for GMs" on public.world_calendars for insert with check (
|
|
exists (
|
|
select 1 from worlds
|
|
where worlds.id = world_calendars.world_id
|
|
)
|
|
);
|
|
create policy "Allow individual update access for GMs" on public.world_calendars for update with check (
|
|
exists (
|
|
select 1 from worlds
|
|
where worlds.id = world_calendars.world_id
|
|
)
|
|
);
|
|
|
|
-- Month policies
|
|
create policy "Allow individual read access for GMs" on public.calendar_months for select using (
|
|
exists (
|
|
select 1 from world_calendars
|
|
where world_calendars.id = calendar_months.calendar_id
|
|
)
|
|
);
|
|
create policy "Allow individual insert access for GMs" on public.calendar_months for insert with check (
|
|
exists (
|
|
select 1 from world_calendars
|
|
where world_calendars.id = calendar_months.calendar_id
|
|
)
|
|
);
|
|
create policy "Allow individual update access for GMs" on public.calendar_months for update with check (
|
|
exists (
|
|
select 1 from world_calendars
|
|
where world_calendars.id = calendar_months.calendar_id
|
|
)
|
|
);
|
|
|
|
-- Event policies
|
|
create policy "Allow individual read access for GMs" on public.calendar_events for select using (
|
|
exists (
|
|
select 1 from world_calendars
|
|
where world_calendars.id = calendar_events.calendar_id
|
|
)
|
|
);
|
|
create policy "Allow individual insert access for GMs" on public.calendar_events for insert with check (
|
|
exists (
|
|
select 1 from world_calendars
|
|
where world_calendars.id = calendar_events.calendar_id
|
|
)
|
|
);
|
|
create policy "Allow individual update access for GMs" on public.calendar_events for update with check (
|
|
exists (
|
|
select 1 from world_calendars
|
|
where world_calendars.id = calendar_events.calendar_id
|
|
)
|
|
);
|
|
|
|
-- Character policies
|
|
create policy "Allow individual read access for GMs" on public.characters for select using (
|
|
exists (
|
|
select 1 from worlds
|
|
where worlds.id = characters.world_id
|
|
)
|
|
);
|
|
create policy "Allow individual insert access for GMs" on public.characters for insert with check (
|
|
exists (
|
|
select 1 from worlds
|
|
where worlds.id = characters.world_id
|
|
)
|
|
);
|
|
create policy "Allow individual update access for GMs" on public.characters for update with check (
|
|
exists (
|
|
select 1 from worlds
|
|
where worlds.id = characters.world_id
|
|
)
|
|
);
|
|
|
|
-- Categories are public to view but not to insert
|
|
create policy "Allow logged-in read access" on public.calendar_events_category for select using ( auth.role() = 'authenticated' );
|
|
create policy "Allow logged-in read access" on public.characters_category for select using ( auth.role() = 'authenticated' );
|
|
|
|
-- Send "previous data" on change
|
|
alter table public.users replica identity full;
|
|
|
|
-- inserts a row into public.users and assigns roles
|
|
create function public.handle_new_user()
|
|
returns trigger as $$
|
|
declare is_admin boolean;
|
|
begin
|
|
insert into public.users (id, username)
|
|
values (new.id, new.email);
|
|
insert into public.user_roles (role, user_id)
|
|
values ('user', new.id);
|
|
|
|
return new;
|
|
end;
|
|
$$ language plpgsql security definer set search_path = auth, public;
|
|
|
|
-- trigger the function every time a user is created
|
|
create trigger on_auth_user_created
|
|
after insert on auth.users
|
|
for each row execute procedure public.handle_new_user();
|
|
|
|
/**
|
|
* HELPER FUNCTIONS
|
|
* Create test user helper method.
|
|
*/
|
|
create or replace function public.create_user(
|
|
email text
|
|
) returns uuid
|
|
security definer
|
|
set search_path = auth
|
|
as $$
|
|
declare
|
|
user_id uuid;
|
|
begin
|
|
user_id := extensions.uuid_generate_v4();
|
|
|
|
insert into auth.users (id, email)
|
|
values (user_id, email)
|
|
returning id into user_id;
|
|
|
|
return user_id;
|
|
end;
|
|
$$ language plpgsql;
|