MLM CMMS uses PostgreSQL Row-Level Security (RLS) to enforce permission-based access control at the database level. This page documents the RLS policies defined in sql/modules/core_cmms/09_policies.sql.
-- Enable RLSALTER TABLE public.table_name ENABLE ROW LEVEL SECURITY;-- SELECT policyCREATE POLICY "Users with read permission can view" ON public.table_name FOR SELECT USING (public.me_has_permission('resource:read'));-- INSERT policyCREATE POLICY "Users with create permission can insert" ON public.table_name FOR INSERT WITH CHECK (public.me_has_permission('resource:create'));-- UPDATE policyCREATE POLICY "Users with update permission can modify" ON public.table_name FOR UPDATE USING (public.me_has_permission('resource:update'));-- DELETE policyCREATE POLICY "Users with delete permission can remove" ON public.table_name FOR DELETE USING (public.me_has_permission('resource:delete'));
Work requests and work orders with hierarchical permissions.
ALTER TABLE public.tickets ENABLE ROW LEVEL SECURITY;-- Read: full access or read permissionCREATE POLICY "tickets_select_policy" ON public.tickets FOR SELECT USING ( public.me_has_permission('work_orders:read') OR public.me_has_permission('work_orders:full_access') OR (public.me_has_permission('work_orders:read_own') AND created_by = auth.uid()) );-- Insert: create permissionCREATE POLICY "tickets_insert_policy" ON public.tickets FOR INSERT WITH CHECK ( public.me_has_permission('work_orders:create') OR public.me_has_permission('work_orders:full_access') );-- Update: full access permissionCREATE POLICY "tickets_update_policy" ON public.tickets FOR UPDATE USING (public.me_has_permission('work_orders:full_access'));-- Delete: delete or full access permissionCREATE POLICY "tickets_delete_policy" ON public.tickets FOR DELETE USING ( public.me_has_permission('work_orders:delete') OR public.me_has_permission('work_orders:full_access') );
Key Features:
read_own permission allows users to view only tickets they created
full_access grants all operations
Separate delete permission for destructive operations
User profile management with self-update capability.
ALTER TABLE public.users ENABLE ROW LEVEL SECURITY;-- Read: users:read permissionCREATE POLICY "users_select_policy" ON public.users FOR SELECT USING (public.me_has_permission('users:read'));-- Insert: users:create or full_accessCREATE POLICY "users_insert_policy" ON public.users FOR INSERT WITH CHECK ( public.me_has_permission('users:create') OR public.me_has_permission('users:full_access') );-- Update: full_access OR self-update (own profile)CREATE POLICY "users_update_policy" ON public.users FOR UPDATE USING ( public.me_has_permission('users:full_access') OR (id = auth.uid()) -- Users can update their own profile );-- Delete: users:delete permissionCREATE POLICY "users_delete_policy" ON public.users FOR DELETE USING (public.me_has_permission('users:delete'));
Key Features:
Any authenticated user can update their own profile
Admin permissions required for creating/deleting users
ALTER TABLE public.assignees ENABLE ROW LEVEL SECURITY;CREATE POLICY "assignees_select_policy" ON public.assignees FOR SELECT USING (public.me_has_permission('assignees:read'));CREATE POLICY "assignees_insert_policy" ON public.assignees FOR INSERT WITH CHECK (public.me_has_permission('assignees:full_access'));CREATE POLICY "assignees_update_policy" ON public.assignees FOR UPDATE USING (public.me_has_permission('assignees:full_access'));CREATE POLICY "assignees_delete_policy" ON public.assignees FOR DELETE USING (public.me_has_permission('assignees:delete'));
ALTER TABLE public.roles ENABLE ROW LEVEL SECURITY;ALTER TABLE public.permissions ENABLE ROW LEVEL SECURITY;ALTER TABLE public.role_permissions ENABLE ROW LEVEL SECURITY;-- Only users with manage_permissions can view/modifyCREATE POLICY "roles_all_policy" ON public.roles FOR ALL USING (public.me_has_permission('rbac:manage_permissions'));CREATE POLICY "permissions_all_policy" ON public.permissions FOR ALL USING (public.me_has_permission('rbac:manage_permissions'));CREATE POLICY "role_permissions_all_policy" ON public.role_permissions FOR ALL USING (public.me_has_permission('rbac:manage_permissions'));
Key Features:
Single ALL policy covers SELECT, INSERT, UPDATE, DELETE
Only users with RBAC management permission can access
ALTER TABLE public.user_roles ENABLE ROW LEVEL SECURITY;-- Users can view their own rolesCREATE POLICY "user_roles_select_policy" ON public.user_roles FOR SELECT USING ( user_id = auth.uid() OR public.me_has_permission('rbac:manage_roles') );-- Only RBAC managers can modify role assignmentsCREATE POLICY "user_roles_insert_policy" ON public.user_roles FOR INSERT WITH CHECK (public.me_has_permission('rbac:manage_roles'));CREATE POLICY "user_roles_delete_policy" ON public.user_roles FOR DELETE USING (public.me_has_permission('rbac:manage_roles'));
Location management with read access for all authenticated users.
ALTER TABLE public.locations ENABLE ROW LEVEL SECURITY;-- All authenticated users can read locationsCREATE POLICY "locations_select_policy" ON public.locations FOR SELECT USING (auth.uid() IS NOT NULL);-- Admins with locations:full_access can modifyCREATE POLICY "locations_insert_policy" ON public.locations FOR INSERT WITH CHECK (public.me_has_permission('locations:full_access'));CREATE POLICY "locations_update_policy" ON public.locations FOR UPDATE USING (public.me_has_permission('locations:full_access'));CREATE POLICY "locations_delete_policy" ON public.locations FOR DELETE USING (public.me_has_permission('locations:delete'));
Key Features:
Locations are readable by all authenticated users (needed for dropdown selectors)
Notification delivery tables with user-specific access.
ALTER TABLE public.notification_deliveries ENABLE ROW LEVEL SECURITY;-- Users can only see their own notificationsCREATE POLICY "notification_deliveries_select_policy" ON public.notification_deliveries FOR SELECT USING (recipient_user_id = auth.uid());-- Users can mark their own notifications as readCREATE POLICY "notification_deliveries_update_policy" ON public.notification_deliveries FOR UPDATE USING (recipient_user_id = auth.uid());
Key Features:
Strict user isolation - users can only access their own notifications
No INSERT/DELETE - managed by triggers and functions
-- Check if RLS is enabledSELECT tablename, rowsecurityFROM pg_tablesWHERE schemaname = 'public' AND tablename IN ('tickets', 'users', 'assignees', 'roles');-- List all policies on a tableSELECT policyname, cmd, qual, with_checkFROM pg_policiesWHERE tablename = 'tickets';-- Test as specific userSET LOCAL ROLE authenticated;SET LOCAL "request.jwt.claim.sub" = 'user-uuid-here';SELECT * FROM public.tickets; -- Should enforce policies