chattyness/db/schema/policies/001_rls.sql
Evan Carroll 60a6680eaf fix: guests
* make guest status a flag on users
* add logout handlers
* add logout notification for other users
2026-01-23 08:18:09 -06:00

1018 lines
31 KiB
PL/PgSQL

-- Chattyness Row-Level Security Policies
-- PostgreSQL 18
--
-- RLS policies for data isolation and access control
-- Load via: psql -f schema/policies/001_rls.sql
--
-- IMPORTANT: The application must set session variables before queries:
--
-- For authenticated users:
-- SELECT public.set_current_user_id('user-uuid-here');
-- SELECT public.set_current_realm_id('realm-uuid-here'); -- when in a realm
--
-- For guest users:
-- SELECT public.set_current_user_id(NULL);
-- SELECT public.set_current_guest_session_id('guest-session-uuid-here');
-- SELECT public.set_current_realm_id('realm-uuid-here'); -- when in a realm
--
-- The chattyness_app role is used by the application backend.
\set ON_ERROR_STOP on
BEGIN;
-- =============================================================================
-- Grant Usage on Schemas to Application Role
-- =============================================================================
GRANT USAGE ON SCHEMA server TO chattyness_app;
GRANT USAGE ON SCHEMA auth TO chattyness_app;
GRANT USAGE ON SCHEMA realm TO chattyness_app;
GRANT USAGE ON SCHEMA scene TO chattyness_app;
GRANT USAGE ON SCHEMA chat TO chattyness_app;
GRANT USAGE ON SCHEMA audit TO chattyness_app;
-- =============================================================================
-- SERVER SCHEMA POLICIES
-- =============================================================================
-- Server data is readable by all, writable only by server admins.
-- =============================================================================
-- server.config
ALTER TABLE server.config ENABLE ROW LEVEL SECURITY;
CREATE POLICY server_config_select ON server.config
FOR SELECT TO chattyness_app
USING (true);
GRANT SELECT ON server.config TO chattyness_app;
-- server.staff
ALTER TABLE server.staff ENABLE ROW LEVEL SECURITY;
CREATE POLICY server_staff_select ON server.staff
FOR SELECT TO chattyness_app
USING (true);
GRANT SELECT ON server.staff TO chattyness_app;
-- server.props
ALTER TABLE server.props ENABLE ROW LEVEL SECURITY;
CREATE POLICY server_props_select ON server.props
FOR SELECT TO chattyness_app
USING (true);
CREATE POLICY server_props_insert ON server.props
FOR INSERT TO chattyness_app
WITH CHECK (public.is_server_admin());
CREATE POLICY server_props_update ON server.props
FOR UPDATE TO chattyness_app
USING (public.is_server_admin())
WITH CHECK (public.is_server_admin());
CREATE POLICY server_props_delete ON server.props
FOR DELETE TO chattyness_app
USING (public.is_server_admin());
GRANT SELECT ON server.props TO chattyness_app;
GRANT INSERT, UPDATE, DELETE ON server.props TO chattyness_app;
-- server.avatars
ALTER TABLE server.avatars ENABLE ROW LEVEL SECURITY;
CREATE POLICY server_avatars_select ON server.avatars
FOR SELECT TO chattyness_app
USING (true);
CREATE POLICY server_avatars_insert ON server.avatars
FOR INSERT TO chattyness_app
WITH CHECK (public.is_server_admin());
CREATE POLICY server_avatars_update ON server.avatars
FOR UPDATE TO chattyness_app
USING (public.is_server_admin())
WITH CHECK (public.is_server_admin());
CREATE POLICY server_avatars_delete ON server.avatars
FOR DELETE TO chattyness_app
USING (public.is_server_admin());
GRANT SELECT ON server.avatars TO chattyness_app;
GRANT INSERT, UPDATE, DELETE ON server.avatars TO chattyness_app;
-- server.audio
ALTER TABLE server.audio ENABLE ROW LEVEL SECURITY;
CREATE POLICY server_audio_select ON server.audio
FOR SELECT TO chattyness_app
USING (true);
CREATE POLICY server_audio_modify ON server.audio
FOR ALL TO chattyness_app
USING (public.is_server_admin())
WITH CHECK (public.is_server_admin());
GRANT SELECT, INSERT, UPDATE, DELETE ON server.audio TO chattyness_app;
-- server.reserved_names
ALTER TABLE server.reserved_names ENABLE ROW LEVEL SECURITY;
CREATE POLICY server_reserved_names_select ON server.reserved_names
FOR SELECT TO chattyness_app
USING (true);
CREATE POLICY server_reserved_names_modify ON server.reserved_names
FOR ALL TO chattyness_app
USING (public.is_server_admin())
WITH CHECK (public.is_server_admin());
GRANT SELECT, INSERT, UPDATE, DELETE ON server.reserved_names TO chattyness_app;
-- server.scripts
ALTER TABLE server.scripts ENABLE ROW LEVEL SECURITY;
CREATE POLICY server_scripts_select ON server.scripts
FOR SELECT TO chattyness_app
USING (true);
CREATE POLICY server_scripts_modify ON server.scripts
FOR ALL TO chattyness_app
USING (public.is_server_admin())
WITH CHECK (public.is_server_admin());
GRANT SELECT, INSERT, UPDATE, DELETE ON server.scripts TO chattyness_app;
-- server.ip_bans
ALTER TABLE server.ip_bans ENABLE ROW LEVEL SECURITY;
CREATE POLICY server_ip_bans_select ON server.ip_bans
FOR SELECT TO chattyness_app
USING (true);
CREATE POLICY server_ip_bans_modify ON server.ip_bans
FOR ALL TO chattyness_app
USING (public.is_server_admin())
WITH CHECK (public.is_server_admin());
GRANT SELECT, INSERT, UPDATE, DELETE ON server.ip_bans TO chattyness_app;
-- server.bans
ALTER TABLE server.bans ENABLE ROW LEVEL SECURITY;
CREATE POLICY server_bans_select ON server.bans
FOR SELECT TO chattyness_app
USING (true);
CREATE POLICY server_bans_modify ON server.bans
FOR ALL TO chattyness_app
USING (public.is_server_moderator())
WITH CHECK (public.is_server_moderator());
GRANT SELECT, INSERT, UPDATE, DELETE ON server.bans TO chattyness_app;
-- server.mutes
ALTER TABLE server.mutes ENABLE ROW LEVEL SECURITY;
CREATE POLICY server_mutes_select ON server.mutes
FOR SELECT TO chattyness_app
USING (true);
CREATE POLICY server_mutes_modify ON server.mutes
FOR ALL TO chattyness_app
USING (public.is_server_moderator())
WITH CHECK (public.is_server_moderator());
GRANT SELECT, INSERT, UPDATE, DELETE ON server.mutes TO chattyness_app;
-- server.content_filters
ALTER TABLE server.content_filters ENABLE ROW LEVEL SECURITY;
CREATE POLICY server_content_filters_select ON server.content_filters
FOR SELECT TO chattyness_app
USING (true);
CREATE POLICY server_content_filters_modify ON server.content_filters
FOR ALL TO chattyness_app
USING (public.is_server_admin())
WITH CHECK (public.is_server_admin());
GRANT SELECT, INSERT, UPDATE, DELETE ON server.content_filters TO chattyness_app;
-- server.moderation_actions
ALTER TABLE server.moderation_actions ENABLE ROW LEVEL SECURITY;
CREATE POLICY server_moderation_actions_target ON server.moderation_actions
FOR SELECT TO chattyness_app
USING (target_user_id = public.current_user_id());
CREATE POLICY server_moderation_actions_mod ON server.moderation_actions
FOR ALL TO chattyness_app
USING (public.is_server_moderator())
WITH CHECK (public.is_server_moderator());
GRANT SELECT, INSERT ON server.moderation_actions TO chattyness_app;
-- =============================================================================
-- AUTH SCHEMA POLICIES
-- =============================================================================
-- auth.users
ALTER TABLE auth.users ENABLE ROW LEVEL SECURITY;
CREATE POLICY auth_users_select ON auth.users
FOR SELECT TO chattyness_app
USING (
id = public.current_user_id()
OR NOT auth.has_blocked(id, public.current_user_id())
);
CREATE POLICY auth_users_update ON auth.users
FOR UPDATE TO chattyness_app
USING (id = public.current_user_id())
WITH CHECK (id = public.current_user_id());
CREATE POLICY auth_users_insert ON auth.users
FOR INSERT TO chattyness_app
WITH CHECK (true);
CREATE POLICY auth_users_admin ON auth.users
FOR ALL TO chattyness_app
USING (public.is_server_admin())
WITH CHECK (public.is_server_admin());
GRANT SELECT, UPDATE ON auth.users TO chattyness_app;
GRANT INSERT, DELETE ON auth.users TO chattyness_app;
-- auth.sessions
ALTER TABLE auth.sessions ENABLE ROW LEVEL SECURITY;
CREATE POLICY auth_sessions_own ON auth.sessions
FOR ALL TO chattyness_app
USING (user_id = public.current_user_id())
WITH CHECK (user_id = public.current_user_id());
CREATE POLICY auth_sessions_admin ON auth.sessions
FOR SELECT TO chattyness_app
USING (public.is_server_admin());
GRANT SELECT, INSERT, UPDATE, DELETE ON auth.sessions TO chattyness_app;
-- auth.tower_sessions
ALTER TABLE auth.tower_sessions ENABLE ROW LEVEL SECURITY;
CREATE POLICY auth_tower_sessions_all ON auth.tower_sessions
FOR ALL TO chattyness_app
USING (true)
WITH CHECK (true);
GRANT SELECT, INSERT, UPDATE, DELETE ON auth.tower_sessions TO chattyness_app;
-- auth.friendships
ALTER TABLE auth.friendships ENABLE ROW LEVEL SECURITY;
CREATE POLICY auth_friendships_own ON auth.friendships
FOR ALL TO chattyness_app
USING (
friend_a = public.current_user_id()
OR friend_b = public.current_user_id()
)
WITH CHECK (
friend_a = public.current_user_id()
OR friend_b = public.current_user_id()
);
GRANT SELECT, INSERT, UPDATE, DELETE ON auth.friendships TO chattyness_app;
-- auth.blocks
ALTER TABLE auth.blocks ENABLE ROW LEVEL SECURITY;
CREATE POLICY auth_blocks_own ON auth.blocks
FOR ALL TO chattyness_app
USING (blocker_id = public.current_user_id())
WITH CHECK (blocker_id = public.current_user_id());
CREATE POLICY auth_blocks_mod ON auth.blocks
FOR SELECT TO chattyness_app
USING (public.is_server_moderator());
GRANT SELECT, INSERT, DELETE ON auth.blocks TO chattyness_app;
-- auth.mutes (user personal mutes)
ALTER TABLE auth.mutes ENABLE ROW LEVEL SECURITY;
CREATE POLICY auth_mutes_own ON auth.mutes
FOR ALL TO chattyness_app
USING (muter_id = public.current_user_id())
WITH CHECK (muter_id = public.current_user_id());
GRANT SELECT, INSERT, DELETE ON auth.mutes TO chattyness_app;
-- auth.user_scripts
ALTER TABLE auth.user_scripts ENABLE ROW LEVEL SECURITY;
CREATE POLICY auth_user_scripts_own ON auth.user_scripts
FOR ALL TO chattyness_app
USING (user_id = public.current_user_id())
WITH CHECK (user_id = public.current_user_id());
CREATE POLICY auth_user_scripts_admin ON auth.user_scripts
FOR SELECT TO chattyness_app
USING (public.is_server_admin());
GRANT SELECT, INSERT, UPDATE, DELETE ON auth.user_scripts TO chattyness_app;
-- auth.inventory
ALTER TABLE auth.inventory ENABLE ROW LEVEL SECURITY;
CREATE POLICY auth_inventory_own ON auth.inventory
FOR ALL TO chattyness_app
USING (user_id = public.current_user_id())
WITH CHECK (user_id = public.current_user_id());
CREATE POLICY auth_inventory_view ON auth.inventory
FOR SELECT TO chattyness_app
USING (true);
GRANT SELECT, INSERT, UPDATE, DELETE ON auth.inventory TO chattyness_app;
-- auth.avatars
ALTER TABLE auth.avatars ENABLE ROW LEVEL SECURITY;
CREATE POLICY auth_avatars_own ON auth.avatars
FOR ALL TO chattyness_app
USING (user_id = public.current_user_id())
WITH CHECK (user_id = public.current_user_id());
CREATE POLICY auth_avatars_view ON auth.avatars
FOR SELECT TO chattyness_app
USING (true);
GRANT SELECT, INSERT, UPDATE, DELETE ON auth.avatars TO chattyness_app;
-- auth.active_avatars
ALTER TABLE auth.active_avatars ENABLE ROW LEVEL SECURITY;
CREATE POLICY auth_active_avatars_own ON auth.active_avatars
FOR ALL TO chattyness_app
USING (user_id = public.current_user_id())
WITH CHECK (user_id = public.current_user_id());
CREATE POLICY auth_active_avatars_view ON auth.active_avatars
FOR SELECT TO chattyness_app
USING (true);
-- Allow realm moderators to update forced avatar columns on any user in their realm
CREATE POLICY auth_active_avatars_mod ON auth.active_avatars
FOR UPDATE TO chattyness_app
USING (public.is_realm_moderator(realm_id))
WITH CHECK (public.is_realm_moderator(realm_id));
GRANT SELECT, INSERT, UPDATE, DELETE ON auth.active_avatars TO chattyness_app;
-- =============================================================================
-- REALM SCHEMA POLICIES
-- =============================================================================
-- realm.realms
ALTER TABLE realm.realms ENABLE ROW LEVEL SECURITY;
CREATE POLICY realm_realms_select ON realm.realms
FOR SELECT TO chattyness_app
USING (
privacy = 'public'
OR privacy = 'unlisted'
OR owner_id = public.current_user_id()
OR public.has_realm_membership(id)
OR public.is_server_admin()
);
CREATE POLICY realm_realms_owner ON realm.realms
FOR UPDATE TO chattyness_app
USING (owner_id = public.current_user_id())
WITH CHECK (owner_id = public.current_user_id());
CREATE POLICY realm_realms_insert ON realm.realms
FOR INSERT TO chattyness_app
WITH CHECK (owner_id = public.current_user_id());
CREATE POLICY realm_realms_delete ON realm.realms
FOR DELETE TO chattyness_app
USING (owner_id = public.current_user_id() OR public.is_server_admin());
CREATE POLICY realm_realms_admin ON realm.realms
FOR ALL TO chattyness_app
USING (public.is_server_admin())
WITH CHECK (public.is_server_admin());
GRANT SELECT, INSERT, UPDATE, DELETE ON realm.realms TO chattyness_app;
-- realm.scenes
ALTER TABLE realm.scenes ENABLE ROW LEVEL SECURITY;
CREATE POLICY realm_scenes_select ON realm.scenes
FOR SELECT TO chattyness_app
USING (
EXISTS (
SELECT 1 FROM realm.realms r
WHERE r.id = realm_id
AND (
r.privacy IN ('public', 'unlisted')
OR r.owner_id = public.current_user_id()
OR public.has_realm_membership(r.id)
OR public.is_server_admin()
)
)
);
CREATE POLICY realm_scenes_modify ON realm.scenes
FOR ALL TO chattyness_app
USING (
EXISTS (
SELECT 1 FROM realm.memberships m
WHERE m.realm_id = realm.scenes.realm_id
AND m.user_id = public.current_user_id()
AND m.role IN ('owner', 'builder')
)
OR public.is_server_admin()
)
WITH CHECK (
EXISTS (
SELECT 1 FROM realm.memberships m
WHERE m.realm_id = realm.scenes.realm_id
AND m.user_id = public.current_user_id()
AND m.role IN ('owner', 'builder')
)
OR public.is_server_admin()
);
GRANT SELECT, INSERT, UPDATE, DELETE ON realm.scenes TO chattyness_app;
-- realm.memberships
ALTER TABLE realm.memberships ENABLE ROW LEVEL SECURITY;
CREATE POLICY realm_memberships_select ON realm.memberships
FOR SELECT TO chattyness_app
USING (
user_id = public.current_user_id()
OR EXISTS (
SELECT 1 FROM realm.realms r
WHERE r.id = realm_id
AND (r.privacy = 'public' OR public.has_realm_membership(r.id))
)
OR public.is_server_admin()
);
CREATE POLICY realm_memberships_own ON realm.memberships
FOR ALL TO chattyness_app
USING (user_id = public.current_user_id())
WITH CHECK (user_id = public.current_user_id());
CREATE POLICY realm_memberships_mod ON realm.memberships
FOR ALL TO chattyness_app
USING (public.is_realm_moderator(realm_id))
WITH CHECK (public.is_realm_moderator(realm_id));
GRANT SELECT, INSERT, UPDATE, DELETE ON realm.memberships TO chattyness_app;
-- realm.realm_scripts
ALTER TABLE realm.realm_scripts ENABLE ROW LEVEL SECURITY;
CREATE POLICY realm_realm_scripts_select ON realm.realm_scripts
FOR SELECT TO chattyness_app
USING (
EXISTS (
SELECT 1 FROM realm.realms r
WHERE r.id = realm_id
)
);
CREATE POLICY realm_realm_scripts_modify ON realm.realm_scripts
FOR ALL TO chattyness_app
USING (
EXISTS (
SELECT 1 FROM realm.memberships m
WHERE m.realm_id = realm.realm_scripts.realm_id
AND m.user_id = public.current_user_id()
AND m.role IN ('owner', 'builder')
)
OR public.is_server_admin()
);
GRANT SELECT, INSERT, UPDATE, DELETE ON realm.realm_scripts TO chattyness_app;
-- realm.props
ALTER TABLE realm.props ENABLE ROW LEVEL SECURITY;
CREATE POLICY realm_props_select ON realm.props
FOR SELECT TO chattyness_app
USING (
public.has_realm_membership(realm_id)
OR public.is_server_admin()
);
CREATE POLICY realm_props_modify ON realm.props
FOR ALL TO chattyness_app
USING (
EXISTS (
SELECT 1 FROM realm.memberships m
WHERE m.realm_id = realm.props.realm_id
AND m.user_id = public.current_user_id()
AND m.role IN ('owner', 'builder')
)
OR public.is_server_admin()
);
GRANT SELECT, INSERT, UPDATE, DELETE ON realm.props TO chattyness_app;
-- realm.avatars
ALTER TABLE realm.avatars ENABLE ROW LEVEL SECURITY;
CREATE POLICY realm_avatars_select ON realm.avatars
FOR SELECT TO chattyness_app
USING (
public.has_realm_membership(realm_id)
OR public.is_server_admin()
);
CREATE POLICY realm_avatars_modify ON realm.avatars
FOR ALL TO chattyness_app
USING (
EXISTS (
SELECT 1 FROM realm.memberships m
WHERE m.realm_id = realm.avatars.realm_id
AND m.user_id = public.current_user_id()
AND m.role IN ('owner', 'builder')
)
OR public.is_server_admin()
);
GRANT SELECT, INSERT, UPDATE, DELETE ON realm.avatars TO chattyness_app;
-- realm.reports
ALTER TABLE realm.reports ENABLE ROW LEVEL SECURITY;
CREATE POLICY realm_reports_own ON realm.reports
FOR SELECT TO chattyness_app
USING (reporter_id = public.current_user_id());
CREATE POLICY realm_reports_insert ON realm.reports
FOR INSERT TO chattyness_app
WITH CHECK (reporter_id = public.current_user_id());
CREATE POLICY realm_reports_mod ON realm.reports
FOR ALL TO chattyness_app
USING (
public.is_server_moderator()
OR public.is_realm_moderator(realm_id)
);
GRANT SELECT, INSERT, UPDATE ON realm.reports TO chattyness_app;
-- realm.bans
ALTER TABLE realm.bans ENABLE ROW LEVEL SECURITY;
CREATE POLICY realm_bans_select ON realm.bans
FOR SELECT TO chattyness_app
USING (true);
CREATE POLICY realm_bans_modify ON realm.bans
FOR ALL TO chattyness_app
USING (
public.is_server_moderator()
OR public.is_realm_moderator(realm_id)
);
GRANT SELECT, INSERT, UPDATE, DELETE ON realm.bans TO chattyness_app;
-- realm.mutes (realm moderation mutes)
ALTER TABLE realm.mutes ENABLE ROW LEVEL SECURITY;
CREATE POLICY realm_mutes_select ON realm.mutes
FOR SELECT TO chattyness_app
USING (true);
CREATE POLICY realm_mutes_modify ON realm.mutes
FOR ALL TO chattyness_app
USING (
public.is_server_moderator()
OR public.is_realm_moderator(realm_id)
);
GRANT SELECT, INSERT, UPDATE, DELETE ON realm.mutes TO chattyness_app;
-- realm.content_filters
ALTER TABLE realm.content_filters ENABLE ROW LEVEL SECURITY;
CREATE POLICY realm_content_filters_select ON realm.content_filters
FOR SELECT TO chattyness_app
USING (
public.has_realm_membership(realm_id)
OR public.is_server_admin()
);
CREATE POLICY realm_content_filters_modify ON realm.content_filters
FOR ALL TO chattyness_app
USING (
EXISTS (
SELECT 1 FROM realm.memberships m
WHERE m.realm_id = realm.content_filters.realm_id
AND m.user_id = public.current_user_id()
AND m.role IN ('owner', 'moderator')
)
OR public.is_server_admin()
);
GRANT SELECT, INSERT, UPDATE, DELETE ON realm.content_filters TO chattyness_app;
-- realm.moderation_actions
ALTER TABLE realm.moderation_actions ENABLE ROW LEVEL SECURITY;
CREATE POLICY realm_moderation_actions_target ON realm.moderation_actions
FOR SELECT TO chattyness_app
USING (target_user_id = public.current_user_id());
CREATE POLICY realm_moderation_actions_mod ON realm.moderation_actions
FOR ALL TO chattyness_app
USING (
public.is_server_moderator()
OR public.is_realm_moderator(realm_id)
);
GRANT SELECT, INSERT ON realm.moderation_actions TO chattyness_app;
-- =============================================================================
-- SCENE SCHEMA POLICIES
-- =============================================================================
-- scene.instances
ALTER TABLE scene.instances ENABLE ROW LEVEL SECURITY;
CREATE POLICY scene_instances_select ON scene.instances
FOR SELECT TO chattyness_app
USING (
instance_type = 'public'
AND EXISTS (
SELECT 1 FROM realm.scenes s
JOIN realm.realms r ON r.id = s.realm_id
WHERE s.id = scene_id
AND (
r.privacy IN ('public', 'unlisted')
OR r.owner_id = public.current_user_id()
OR public.has_realm_membership(r.id)
)
)
OR
(
instance_type = 'private'
AND (
created_by = public.current_user_id()
OR EXISTS (
SELECT 1 FROM scene.instance_invites ii
WHERE ii.instance_id = scene.instances.id
AND ii.invited_user_id = public.current_user_id()
AND ii.accepted_at IS NOT NULL
)
)
)
OR public.is_server_admin()
);
CREATE POLICY scene_instances_insert ON scene.instances
FOR INSERT TO chattyness_app
WITH CHECK (true);
CREATE POLICY scene_instances_modify ON scene.instances
FOR UPDATE TO chattyness_app
USING (
created_by = public.current_user_id()
OR public.is_realm_moderator(
(SELECT s.realm_id FROM realm.scenes s WHERE s.id = scene_id)
)
);
CREATE POLICY scene_instances_delete ON scene.instances
FOR DELETE TO chattyness_app
USING (
created_by = public.current_user_id()
OR public.is_realm_moderator(
(SELECT s.realm_id FROM realm.scenes s WHERE s.id = scene_id)
)
);
GRANT SELECT, INSERT, UPDATE, DELETE ON scene.instances TO chattyness_app;
-- scene.instance_members
ALTER TABLE scene.instance_members ENABLE ROW LEVEL SECURITY;
CREATE POLICY scene_instance_members_select ON scene.instance_members
FOR SELECT TO chattyness_app
USING (
EXISTS (
SELECT 1 FROM realm.scenes s
WHERE s.id = instance_id
)
);
CREATE POLICY scene_instance_members_own ON scene.instance_members
FOR ALL TO chattyness_app
USING (user_id = public.current_user_id())
WITH CHECK (user_id = public.current_user_id());
GRANT SELECT, INSERT, UPDATE, DELETE ON scene.instance_members TO chattyness_app;
-- scene.instance_invites
ALTER TABLE scene.instance_invites ENABLE ROW LEVEL SECURITY;
CREATE POLICY scene_instance_invites_select ON scene.instance_invites
FOR SELECT TO chattyness_app
USING (
invited_user_id = public.current_user_id()
OR invited_by = public.current_user_id()
OR public.is_server_admin()
);
CREATE POLICY scene_instance_invites_own ON scene.instance_invites
FOR ALL TO chattyness_app
USING (
invited_user_id = public.current_user_id()
OR invited_by = public.current_user_id()
)
WITH CHECK (
invited_by = public.current_user_id()
);
GRANT SELECT, INSERT, UPDATE, DELETE ON scene.instance_invites TO chattyness_app;
-- scene.spots
ALTER TABLE scene.spots ENABLE ROW LEVEL SECURITY;
CREATE POLICY scene_spots_select ON scene.spots
FOR SELECT TO chattyness_app
USING (
EXISTS (
SELECT 1 FROM realm.scenes s
WHERE s.id = scene_id
)
);
CREATE POLICY scene_spots_modify ON scene.spots
FOR ALL TO chattyness_app
USING (
EXISTS (
SELECT 1 FROM realm.scenes s
JOIN realm.memberships m ON m.realm_id = s.realm_id
WHERE s.id = scene_id
AND m.user_id = public.current_user_id()
AND m.role IN ('owner', 'builder')
)
OR public.is_server_admin()
)
WITH CHECK (
EXISTS (
SELECT 1 FROM realm.scenes s
JOIN realm.memberships m ON m.realm_id = s.realm_id
WHERE s.id = scene_id
AND m.user_id = public.current_user_id()
AND m.role IN ('owner', 'builder')
)
OR public.is_server_admin()
);
GRANT SELECT, INSERT, UPDATE, DELETE ON scene.spots TO chattyness_app;
-- scene.spot_states
ALTER TABLE scene.spot_states ENABLE ROW LEVEL SECURITY;
CREATE POLICY scene_spot_states_select ON scene.spot_states
FOR SELECT TO chattyness_app
USING (
EXISTS (
SELECT 1 FROM scene.spots sp
WHERE sp.id = spot_id
)
);
CREATE POLICY scene_spot_states_modify ON scene.spot_states
FOR ALL TO chattyness_app
USING (
EXISTS (
SELECT 1 FROM scene.spots sp
JOIN realm.scenes s ON s.id = sp.scene_id
JOIN realm.memberships m ON m.realm_id = s.realm_id
WHERE sp.id = spot_id
AND m.user_id = public.current_user_id()
AND m.role IN ('owner', 'builder')
)
OR public.is_server_admin()
)
WITH CHECK (
EXISTS (
SELECT 1 FROM scene.spots sp
JOIN realm.scenes s ON s.id = sp.scene_id
JOIN realm.memberships m ON m.realm_id = s.realm_id
WHERE sp.id = spot_id
AND m.user_id = public.current_user_id()
AND m.role IN ('owner', 'builder')
)
OR public.is_server_admin()
);
GRANT SELECT, INSERT, UPDATE, DELETE ON scene.spot_states TO chattyness_app;
-- scene.scripts
ALTER TABLE scene.scripts ENABLE ROW LEVEL SECURITY;
CREATE POLICY scene_scripts_select ON scene.scripts
FOR SELECT TO chattyness_app
USING (
EXISTS (
SELECT 1 FROM realm.scenes s
WHERE s.id = scene_id
)
);
CREATE POLICY scene_scripts_modify ON scene.scripts
FOR ALL TO chattyness_app
USING (
EXISTS (
SELECT 1 FROM realm.scenes s
JOIN realm.memberships m ON m.realm_id = s.realm_id
WHERE s.id = scene_id
AND m.user_id = public.current_user_id()
AND m.role IN ('owner', 'builder')
)
OR public.is_server_admin()
);
GRANT SELECT, INSERT, UPDATE, DELETE ON scene.scripts TO chattyness_app;
-- scene.loose_props
ALTER TABLE scene.loose_props ENABLE ROW LEVEL SECURITY;
CREATE POLICY scene_loose_props_select ON scene.loose_props
FOR SELECT TO chattyness_app
USING (
EXISTS (
SELECT 1 FROM scene.instances i
WHERE i.id = instance_id
)
);
CREATE POLICY scene_loose_props_modify ON scene.loose_props
FOR ALL TO chattyness_app
USING (true)
WITH CHECK (true);
GRANT SELECT, INSERT, UPDATE, DELETE ON scene.loose_props TO chattyness_app;
-- scene.decorations
ALTER TABLE scene.decorations ENABLE ROW LEVEL SECURITY;
CREATE POLICY scene_decorations_select ON scene.decorations
FOR SELECT TO chattyness_app
USING (
EXISTS (
SELECT 1 FROM realm.scenes s
WHERE s.id = scene_id
)
);
CREATE POLICY scene_decorations_modify ON scene.decorations
FOR ALL TO chattyness_app
USING (
EXISTS (
SELECT 1 FROM realm.scenes s
JOIN realm.memberships m ON m.realm_id = s.realm_id
WHERE s.id = scene_id
AND m.user_id = public.current_user_id()
AND m.role IN ('owner', 'builder')
)
OR public.is_server_admin()
);
GRANT SELECT, INSERT, UPDATE, DELETE ON scene.decorations TO chattyness_app;
-- =============================================================================
-- CHAT SCHEMA POLICIES
-- =============================================================================
-- chat.messages
ALTER TABLE chat.messages ENABLE ROW LEVEL SECURITY;
CREATE POLICY chat_messages_select ON chat.messages
FOR SELECT TO chattyness_app
USING (
(NOT is_deleted OR public.is_server_moderator())
AND EXISTS (
SELECT 1 FROM scene.instances i
WHERE i.id = instance_id
)
);
CREATE POLICY chat_messages_insert ON chat.messages
FOR INSERT TO chattyness_app
WITH CHECK (user_id = public.current_user_id());
CREATE POLICY chat_messages_update ON chat.messages
FOR UPDATE TO chattyness_app
USING (
user_id = public.current_user_id()
OR public.is_server_moderator()
);
GRANT SELECT, INSERT, UPDATE ON chat.messages TO chattyness_app;
-- chat.whispers
ALTER TABLE chat.whispers ENABLE ROW LEVEL SECURITY;
CREATE POLICY chat_whispers_own ON chat.whispers
FOR ALL TO chattyness_app
USING (
sender_id = public.current_user_id()
OR recipient_id = public.current_user_id()
)
WITH CHECK (
sender_id = public.current_user_id()
);
GRANT SELECT, INSERT, UPDATE ON chat.whispers TO chattyness_app;
-- chat.reactions
ALTER TABLE chat.reactions ENABLE ROW LEVEL SECURITY;
CREATE POLICY chat_reactions_select ON chat.reactions
FOR SELECT TO chattyness_app
USING (true);
CREATE POLICY chat_reactions_own ON chat.reactions
FOR ALL TO chattyness_app
USING (user_id = public.current_user_id())
WITH CHECK (user_id = public.current_user_id());
GRANT SELECT, INSERT, DELETE ON chat.reactions TO chattyness_app;
-- chat.shouts
ALTER TABLE chat.shouts ENABLE ROW LEVEL SECURITY;
CREATE POLICY chat_shouts_select ON chat.shouts
FOR SELECT TO chattyness_app
USING (true);
CREATE POLICY chat_shouts_insert ON chat.shouts
FOR INSERT TO chattyness_app
WITH CHECK (public.is_server_admin());
GRANT SELECT, INSERT ON chat.shouts TO chattyness_app;
-- =============================================================================
-- AUDIT SCHEMA POLICIES
-- =============================================================================
-- audit.events
ALTER TABLE audit.events ENABLE ROW LEVEL SECURITY;
CREATE POLICY audit_events_own ON audit.events
FOR SELECT TO chattyness_app
USING (user_id = public.current_user_id());
CREATE POLICY audit_events_admin ON audit.events
FOR SELECT TO chattyness_app
USING (public.is_server_admin());
CREATE POLICY audit_events_realm ON audit.events
FOR SELECT TO chattyness_app
USING (
realm_id IS NOT NULL
AND EXISTS (
SELECT 1 FROM realm.realms r
WHERE r.id = realm_id
AND r.owner_id = public.current_user_id()
)
);
CREATE POLICY audit_events_insert ON audit.events
FOR INSERT TO chattyness_app
WITH CHECK (true);
GRANT SELECT, INSERT ON audit.events TO chattyness_app;
-- audit.login_history
ALTER TABLE audit.login_history ENABLE ROW LEVEL SECURITY;
CREATE POLICY audit_login_history_own ON audit.login_history
FOR SELECT TO chattyness_app
USING (user_id = public.current_user_id());
CREATE POLICY audit_login_history_admin ON audit.login_history
FOR SELECT TO chattyness_app
USING (public.is_server_admin());
CREATE POLICY audit_login_history_insert ON audit.login_history
FOR INSERT TO chattyness_app
WITH CHECK (true);
GRANT SELECT, INSERT ON audit.login_history TO chattyness_app;
COMMIT;