Skip to content

Commit 1b53021

Browse files
committed
docs: add role/status analysis and query audit documentation
Added comprehensive documentation for role_id vs internal_status confusion: 1. ROLES-STATUS-PROPOSAL.md - Analyzes the confusion between role_id and internal_status - Documents 14 mismatches found in database - Proposes 3 solutions (recommends using role_id for roles, internal_status for progression) - Lists all files that need updates - Provides migration SQL queries 2. CODEV-QUERY-AUDIT.md - Audits all codev table queries for RLS issues - Identifies safe vs vulnerable queries - Provides fix for getCodevs() function - Testing checklist for RLS issues Key Findings: - 14 users have mismatched role_id vs internal_status - 232 users have undefined role_id = 4 - 20 users have undefined role_id = 6 - lib/server/codev.service.ts needs RLS protection Recommendation: Use role_id for permission levels (Admin, Mentor, etc.) and internal_status for training progression (TRAINING → GRADUATED → DEPLOYED)
1 parent e63b261 commit 1b53021

2 files changed

Lines changed: 515 additions & 0 deletions

File tree

‎docs/CODEV-QUERY-AUDIT.md‎

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
# Codev Query Audit - RLS Issues
2+
3+
**Date**: April 27, 2026
4+
**Issue**: Queries without explicit `role_id` filtering get corrupted role_id values due to RLS policies
5+
6+
---
7+
8+
## ✅ Safe Queries (No RLS Issues)
9+
10+
### 1. `lib/server/codev-queries.ts`
11+
**Status**: ✅ SAFE - Purpose-built to avoid RLS issues
12+
13+
```typescript
14+
// Queries each role separately, then combines results
15+
export async function fetchAllCodevsWithCorrectRoles()
16+
export async function fetchMentors()
17+
export async function fetchAdmins()
18+
```
19+
20+
**Recommendation**: Use these utilities for all future queries
21+
22+
---
23+
24+
### 2. `app/home/projects/actions.ts` - `getProjectCodevs()`
25+
**Status**: ✅ FIXED - Commit c65c4396
26+
27+
```typescript
28+
// Fetches users by role in separate queries to avoid RLS corruption
29+
const roleIds = [1, 5, 7, 10];
30+
const queries = roleIds.map(roleId =>
31+
supabase.from("codev").select(selectFields).eq("role_id", roleId)
32+
);
33+
```
34+
35+
**Pattern**: ✅ Queries by role_id separately, then combines
36+
37+
---
38+
39+
### 3. `app/(marketing)/_components/landing/LandingAdmins.tsx`
40+
**Status**: ✅ SAFE - Already uses correct pattern
41+
42+
```typescript
43+
const [
44+
{ data: admins, error: adminError },
45+
{ data: mentors, error: mentorError },
46+
] = await Promise.all([
47+
supabase.from("codev").select("*").eq("role_id", 1),
48+
supabase.from("codev").select("*").eq("role_id", 5),
49+
]);
50+
```
51+
52+
**Pattern**: ✅ Separate queries with explicit role_id filters
53+
54+
---
55+
56+
### 4. `app/home/my-team/AddMembersModal.tsx` - `fetchCodevsByFilter()`
57+
**Status**: ✅ FIXED - Uses role_id filtering
58+
59+
```typescript
60+
case 'mentor':
61+
query = query.eq('role_id', 5);
62+
break;
63+
case 'admin':
64+
query = query.eq('role_id', 1);
65+
break;
66+
```
67+
68+
**Pattern**: ✅ Explicitly filters by role_id
69+
70+
---
71+
72+
## ⚠️ Potentially Unsafe Queries
73+
74+
### 5. `lib/server/codev.service.ts` - `getCodevs()`
75+
**Status**: ⚠️ VULNERABLE - Can be called without role_id filter
76+
77+
```typescript
78+
export const getCodevs = async ({
79+
filters = {},
80+
}: {
81+
filters?: {
82+
id?: string;
83+
role_id?: number | string;
84+
application_status?: string;
85+
};
86+
} = {}): Promise<{ error: any; data: Codev[] | null }> => {
87+
const supabase = await createClientServerComponent();
88+
let query = supabase.from("codev").select(`...`);
89+
90+
// ⚠️ Problem: If filters doesn't include role_id, gets corrupted data
91+
Object.entries(filters).forEach(([key, value]) => {
92+
if (value !== undefined) {
93+
query = query.eq(key, value);
94+
}
95+
});
96+
```
97+
98+
**Issue**: When called without `role_id` filter:
99+
```typescript
100+
// ❌ WRONG - Gets corrupted role_id values
101+
const { data } = await getCodevs({ filters: { application_status: 'passed' }});
102+
103+
// ✅ CORRECT - Gets accurate role_id values
104+
const { data } = await getCodevs({ filters: { role_id: 5 }});
105+
```
106+
107+
**Fix Required**:
108+
```typescript
109+
export const getCodevs = async ({ filters = {} } = {}): Promise<...> => {
110+
// If no role_id filter, use safe utility
111+
if (!filters.role_id) {
112+
const allUsers = await fetchAllCodevsWithCorrectRoles();
113+
114+
// Apply other filters client-side
115+
let filtered = allUsers;
116+
if (filters.id) filtered = filtered.filter(u => u.id === filters.id);
117+
if (filters.application_status) {
118+
filtered = filtered.filter(u => u.application_status === filters.application_status);
119+
}
120+
121+
return { error: null, data: filtered };
122+
}
123+
124+
// Safe to use direct query when role_id is provided
125+
const supabase = await createClientServerComponent();
126+
let query = supabase.from("codev").select(`...`).eq('role_id', filters.role_id);
127+
128+
// Apply other filters
129+
if (filters.id) query = query.eq('id', filters.id);
130+
if (filters.application_status) {
131+
query = query.eq('application_status', filters.application_status);
132+
}
133+
134+
const { data, error } = await query;
135+
return { error, data };
136+
};
137+
```
138+
139+
---
140+
141+
## 📊 Usage Analysis
142+
143+
### Where is `getCodevs()` used?
144+
145+
```bash
146+
grep -r "getCodevs" apps/codebility --include="*.ts" --include="*.tsx"
147+
```
148+
149+
**Need to check**:
150+
1. Is it called without role_id filter?
151+
2. Does the calling code rely on accurate role_id values?
152+
3. Should we fix it now or deprecate in favor of utility functions?
153+
154+
---
155+
156+
## 🎯 Recommended Actions
157+
158+
### Immediate (High Priority)
159+
160+
1. ✅ Fix `lib/server/codev.service.ts` - `getCodevs()` function
161+
2. ✅ Audit all usages of `getCodevs()` to ensure they work after fix
162+
3. ✅ Add JSDoc warning to `getCodevs()` about RLS issue
163+
164+
### Short Term (Next Sprint)
165+
166+
4. Create database migration to fix unknown role_ids (4, 6)
167+
5. Update CLAUDE.md with clear guidelines on role_id vs internal_status
168+
6. Add constants/roles.ts file for consistency
169+
170+
### Long Term (Technical Debt)
171+
172+
7. Consider deprecating `getCodevs()` in favor of specific query functions
173+
8. Add database triggers to prevent role_id/internal_status mismatches
174+
9. Add automated tests to catch RLS issues
175+
176+
---
177+
178+
## 📝 Testing Checklist
179+
180+
Before marking `getCodevs()` as fixed, verify:
181+
182+
- [ ] Called without filters returns all users with correct role_id
183+
- [ ] Called with role_id filter returns correct subset
184+
- [ ] Called with application_status filter returns correct subset with correct role_id
185+
- [ ] Mentors count matches database (11 mentors)
186+
- [ ] Admins count matches database (20 admins)
187+
- [ ] No console errors about RLS or permissions
188+
189+
---
190+
191+
## 🔍 How to Test for RLS Issues
192+
193+
Add this debug code temporarily:
194+
195+
```typescript
196+
const { data: allUsers } = await getCodevs();
197+
198+
console.log('Total users:', allUsers?.length);
199+
console.log('Mentors (role_id=5):', allUsers?.filter(u => u.role_id === 5).length);
200+
console.log('Admins (role_id=1):', allUsers?.filter(u => u.role_id === 1).length);
201+
202+
// Compare with direct role-filtered query
203+
const { data: mentors } = await getCodevs({ filters: { role_id: 5 }});
204+
console.log('Direct mentor query:', mentors?.length);
205+
206+
// If numbers don't match, RLS issue exists!
207+
```
208+
209+
---
210+
211+
**Last Updated**: April 27, 2026
212+
**Next Review**: After implementing getCodevs() fix

0 commit comments

Comments
 (0)