From a68b88d3a59864586267066680dbab78bf19d7e3 Mon Sep 17 00:00:00 2001 From: dhruvi-16-me Date: Sun, 4 Jan 2026 18:44:19 +0530 Subject: [PATCH 1/3] Changed RLS Policies so admin can change roles --- sqls/02_user_auth_policies.sql | 72 ++++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 8 deletions(-) diff --git a/sqls/02_user_auth_policies.sql b/sqls/02_user_auth_policies.sql index 5fe68cc..b6267e9 100644 --- a/sqls/02_user_auth_policies.sql +++ b/sqls/02_user_auth_policies.sql @@ -1,10 +1,22 @@ --- First, drop all existing policies +-- Drop helper functions +DROP FUNCTION IF EXISTS is_admin_in_same_team(UUID); +DROP FUNCTION IF EXISTS get_current_user_role(); + +-- Drop policies on teams table (organized by operation: SELECT, INSERT, UPDATE) DROP POLICY IF EXISTS "Team members can view their team" ON teams; -DROP POLICY IF EXISTS "Only admins can update their team" ON teams; -DROP POLICY IF EXISTS "Users can view members of their team" ON users; -DROP POLICY IF EXISTS "Users can update their own profile" ON users; -DROP POLICY IF EXISTS "Allow authenticated users to insert users" ON users; +DROP POLICY IF EXISTS "Allow team creation" ON teams; DROP POLICY IF EXISTS "Allow authenticated users to insert teams" ON teams; +DROP POLICY IF EXISTS "Only admins can update their team" ON teams; + +-- Drop policies on users table (organized by operation: SELECT, INSERT, UPDATE) +DROP POLICY IF EXISTS "Users can view themselves" ON users; +DROP POLICY IF EXISTS "Users can view team members" ON users; +DROP POLICY IF EXISTS "Users can view members of their team" ON users; -- Legacy policy +DROP POLICY IF EXISTS "Allow user creation" ON users; +DROP POLICY IF EXISTS "Allow authenticated users to insert users" ON users; -- Legacy policy +DROP POLICY IF EXISTS "Users can update their own profile" ON users; -- Legacy policy +DROP POLICY IF EXISTS "Users can update their own profile (except role)" ON users; +DROP POLICY IF EXISTS "Admins can manage roles within their team" ON users; -- Temporarily disable RLS to make sure we can fix everything ALTER TABLE teams DISABLE ROW LEVEL SECURITY; @@ -14,6 +26,29 @@ ALTER TABLE users DISABLE ROW LEVEL SECURITY; CREATE OR REPLACE VIEW user_teams AS SELECT id, team_id FROM users; +-- Helper function to check if current user is admin in same team (bypasses RLS) +CREATE OR REPLACE FUNCTION is_admin_in_same_team(target_team_id UUID) +RETURNS BOOLEAN AS $$ +BEGIN + RETURN EXISTS ( + SELECT 1 + FROM users + WHERE id = auth.uid() + AND role = 'admin' + AND team_id = target_team_id + AND team_id IS NOT NULL + ); +END; +$$ LANGUAGE plpgsql SECURITY DEFINER STABLE; + +-- Helper function to get current user's role (bypasses RLS) +CREATE OR REPLACE FUNCTION get_current_user_role() +RETURNS TEXT AS $$ +BEGIN + RETURN (SELECT role FROM users WHERE id = auth.uid()); +END; +$$ LANGUAGE plpgsql SECURITY DEFINER STABLE; + -- Re-enable RLS ALTER TABLE teams ENABLE ROW LEVEL SECURITY; ALTER TABLE users ENABLE ROW LEVEL SECURITY; @@ -47,9 +82,30 @@ CREATE POLICY "Users can view team members" ) ); -CREATE POLICY "Users can update their own profile" - ON users FOR UPDATE - USING (id = auth.uid()); +CREATE POLICY "Users can update their own profile (except role)" + ON users + FOR UPDATE + USING (id = auth.uid()) + WITH CHECK ( + id = auth.uid() + -- Prevent users from changing their own role + AND role = get_current_user_role() + ); + +-- Policy for admins to update roles of team members +-- This policy allows admins to update the role field of users in their team +CREATE POLICY "Admins can manage roles within their team" +ON users +FOR UPDATE +USING ( + -- Check: current user is admin in same team (using helper function to avoid recursion) + users.team_id IS NOT NULL + AND is_admin_in_same_team(users.team_id) +) +WITH CHECK ( + -- Validate the new role value + role IN ('admin', 'member') +); CREATE POLICY "Allow user creation" ON users FOR INSERT From 0bb8352809472dde2972dca79e52ad82270bed13 Mon Sep 17 00:00:00 2001 From: dhruvi-16-me Date: Sun, 4 Jan 2026 18:50:00 +0530 Subject: [PATCH 2/3] Added team member role change with validation and error handling --- lib/services/supabase_service.dart | 85 ++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/lib/services/supabase_service.dart b/lib/services/supabase_service.dart index 1f22eb5..36954fc 100644 --- a/lib/services/supabase_service.dart +++ b/lib/services/supabase_service.dart @@ -484,6 +484,91 @@ class SupabaseService { } } + Future> changeTeamMemberRole({ + required String userId, + required String newRole, // 'admin' or 'member' + }) async { + try { + if (!_isInitialized) { + return { + 'success': false, + 'error': 'Supabase not initialized', + }; + } + + final currentUser = _client.auth.currentUser; + if (currentUser == null) { + return { + 'success': false, + 'error': 'User not authenticated', + }; + } + + // Verify current user is an admin and both users are in the same team + final currentUserProfile = await _client + .from('users') + .select('role, team_id') + .eq('id', currentUser.id) + .single(); + + if (currentUserProfile['role'] != 'admin') { + return { + 'success': false, + 'error': 'Only admins can change member roles', + }; + } + + final targetUserProfile = await _client + .from('users') + .select('team_id, role') + .eq('id', userId) + .single(); + + if (currentUserProfile['team_id'] != targetUserProfile['team_id']) { + return { + 'success': false, + 'error': 'Users must be in the same team', + }; + } + + // Validate new role + if (newRole != 'admin' && newRole != 'member') { + return { + 'success': false, + 'error': 'Invalid role: $newRole', + }; + } + + // Update the role + final response = await _client + .from('users') + .update({'role': newRole}) + .eq('id', userId) + .select(); + + if (response.isEmpty) { + return { + 'success': false, + 'error': + 'Update failed - RLS policy may be blocking. Please check database permissions.', + }; + } + + debugPrint('Successfully updated role to $newRole for user $userId'); + return { + 'success': true, + 'message': 'Role updated successfully', + }; + } catch (e, stackTrace) { + debugPrint('Error changing user role: $e'); + debugPrint('Stack trace: $stackTrace'); + return { + 'success': false, + 'error': e.toString(), + }; + } + } + Future teamExists(String teamId) async { try { if (!_isInitialized) return false; From 9e1ce000a987bfc1db6f4a7895663b5f05dd91fa Mon Sep 17 00:00:00 2001 From: dhruvi-16-me Date: Sun, 1 Feb 2026 23:59:33 +0530 Subject: [PATCH 3/3] Admin self-role modification prevention --- sqls/02_user_auth_policies.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sqls/02_user_auth_policies.sql b/sqls/02_user_auth_policies.sql index b6267e9..fa3d733 100644 --- a/sqls/02_user_auth_policies.sql +++ b/sqls/02_user_auth_policies.sql @@ -101,6 +101,8 @@ USING ( -- Check: current user is admin in same team (using helper function to avoid recursion) users.team_id IS NOT NULL AND is_admin_in_same_team(users.team_id) + -- Prevent admins from changing their own role + AND users.id <> auth.uid() ) WITH CHECK ( -- Validate the new role value