-- 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;