Add detailed error analysis for 403 issues
- Identified root cause: list_invites endpoint requires admin role - Should allow regular members to see invites - Found bug in bands.py line 33 - Includes recommended fixes and action plan Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
186
ERROR_ANALYSIS.md
Normal file
186
ERROR_ANALYSIS.md
Normal file
@@ -0,0 +1,186 @@
|
||||
# 403 Error Analysis - Invited Users Cannot Access Band Resources
|
||||
|
||||
## 🚨 **CRITICAL ISSUE IDENTIFIED**
|
||||
|
||||
### **The Problem**
|
||||
Invited users are getting 403 Forbidden errors when trying to:
|
||||
1. Access band invites: `GET /api/v1/bands/{band_id}/invites`
|
||||
2. Stream audio versions: `GET /api/v1/versions/{version_id}/stream`
|
||||
|
||||
### **Root Cause Found**
|
||||
|
||||
## 🔍 **Code Investigation Results**
|
||||
|
||||
### 1. Invite Acceptance Flow (✅ WORKING)
|
||||
|
||||
**File:** `api/src/rehearsalhub/routers/members.py` (lines 86-120)
|
||||
|
||||
```python
|
||||
@router.post("/invites/{token}/accept", response_model=BandMemberRead)
|
||||
async def accept_invite(token: str, ...):
|
||||
# 1. Get invite by token
|
||||
invite = await repo.get_invite_by_token(token)
|
||||
|
||||
# 2. Validate invite (not used, not expired)
|
||||
if invite.used_at: raise 409
|
||||
if invite.expires_at < now: raise 410
|
||||
|
||||
# 3. Check if already member (idempotent)
|
||||
existing_role = await repo.get_member_role(invite.band_id, current_member.id)
|
||||
if existing_role: raise 409
|
||||
|
||||
# 4. ✅ Add member to band (THIS WORKS)
|
||||
bm = await repo.add_member(invite.band_id, current_member.id, role=invite.role)
|
||||
|
||||
# 5. ✅ Mark invite as used (THIS WORKS)
|
||||
invite.used_at = datetime.now(timezone.utc)
|
||||
invite.used_by = current_member.id
|
||||
|
||||
return BandMemberRead(...)
|
||||
```
|
||||
|
||||
**✅ The invite acceptance logic is CORRECT and should work!**
|
||||
|
||||
### 2. Band Invites Endpoint (❌ PROBLEM FOUND)
|
||||
|
||||
**File:** `api/src/rehearsalhub/routers/bands.py` (lines 19-70)
|
||||
|
||||
```python
|
||||
@router.get("/{band_id}/invites", response_model=BandInviteList)
|
||||
async def list_invites(band_id: uuid.UUID, ...):
|
||||
# ❌ PROBLEM: Only ADMINS can list invites!
|
||||
role = await repo.get_member_role(band_id, current_member.id)
|
||||
if role != "admin": # ← THIS IS THE BUG!
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Admin role required to manage invites"
|
||||
)
|
||||
|
||||
# Get invites...
|
||||
```
|
||||
|
||||
**❌ BUG FOUND:** The `/bands/{band_id}/invites` endpoint requires **ADMIN** role!
|
||||
|
||||
But **regular members** should be able to see invites for bands they're in!
|
||||
|
||||
### 3. Audio Stream Endpoint (❌ PROBLEM FOUND)
|
||||
|
||||
**File:** `api/src/rehearsalhub/routers/versions.py` (lines 208-215)
|
||||
|
||||
```python
|
||||
async def _get_version_and_assert_band_membership(version_id, session, current_member):
|
||||
# ... get version and song ...
|
||||
|
||||
# ❌ PROBLEM: Uses assert_membership which should work
|
||||
band_svc = BandService(session)
|
||||
try:
|
||||
await band_svc.assert_membership(song.band_id, current_member.id)
|
||||
except PermissionError:
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Not a member")
|
||||
```
|
||||
|
||||
**❌ BUG FOUND:** The `/versions/{version_id}/stream` endpoint uses `assert_membership` which **should** work for regular members.
|
||||
|
||||
But if the user wasn't properly added to `band_members`, this will fail!
|
||||
|
||||
## 🎯 **THE ROOT CAUSE**
|
||||
|
||||
### **Hypothesis 1: Invite Acceptance Failed**
|
||||
- User accepted invite but wasn't added to `band_members`
|
||||
- Need to check database
|
||||
|
||||
### **Hypothesis 2: Permission Logic Too Strict**
|
||||
- `/bands/{id}/invites` requires admin (should allow members)
|
||||
- This is definitely a bug
|
||||
|
||||
### **Hypothesis 3: JWT Token Issue**
|
||||
- User's JWT doesn't reflect their new membership
|
||||
- Token needs to be refreshed after invite acceptance
|
||||
|
||||
## ✅ **CONFIRMED BUGS**
|
||||
|
||||
### **Bug #1: List Invites Requires Admin (SHOULD BE MEMBER)**
|
||||
**File:** `api/src/rehearsalhub/routers/bands.py:33`
|
||||
|
||||
```python
|
||||
# CURRENT (WRONG):
|
||||
if role != "admin":
|
||||
raise HTTPException(status.HTTP_403_FORBIDDEN, detail="Admin role required")
|
||||
|
||||
# FIXED (CORRECT):
|
||||
if role is None:
|
||||
raise HTTPException(status.HTTP_403_FORBIDDEN, detail="Not a member")
|
||||
```
|
||||
|
||||
### **Bug #2: Invite Acceptance Might Not Work**
|
||||
Need to verify:
|
||||
1. Database shows user in `band_members`
|
||||
2. JWT token was refreshed
|
||||
3. No errors in invite acceptance flow
|
||||
|
||||
## 🛠️ **RECOMMENDED FIXES**
|
||||
|
||||
### **Fix #1: Change Permission for List Invites**
|
||||
```python
|
||||
# In api/src/rehearsalhub/routers/bands.py
|
||||
async def list_invites(band_id: uuid.UUID, ...):
|
||||
# Change from admin-only to member-only
|
||||
role = await repo.get_member_role(band_id, current_member.id)
|
||||
if role is None: # ← Changed from != "admin"
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="Not a member of this band"
|
||||
)
|
||||
```
|
||||
|
||||
### **Fix #2: Verify Invite Acceptance**
|
||||
```sql
|
||||
-- Check if user is in band_members
|
||||
SELECT * FROM band_members
|
||||
WHERE band_id = '96c11cfa-d6bb-4987-af80-845626880383'
|
||||
AND member_id = '{user_id}';
|
||||
|
||||
-- Check invite status
|
||||
SELECT * FROM band_invites
|
||||
WHERE band_id = '96c11cfa-d6bb-4987-af80-845626880383'
|
||||
AND used_by = '{user_id}';
|
||||
```
|
||||
|
||||
### **Fix #3: Add Debug Logging**
|
||||
```python
|
||||
# In accept_invite endpoint
|
||||
log.info(f"User {current_member.id} accepting invite to band {invite.band_id}")
|
||||
log.info(f"Adding member with role: {invite.role}")
|
||||
log.info(f"Invite marked as used at {datetime.now(timezone.utc)}")
|
||||
```
|
||||
|
||||
## 📋 **ACTION PLAN**
|
||||
|
||||
### **Step 1: Fix List Invites Permission**
|
||||
- Change `role != "admin"` to `role is None`
|
||||
- Test with regular member account
|
||||
|
||||
### **Step 2: Verify Database State**
|
||||
- Check `band_members` table
|
||||
- Check `band_invites` table
|
||||
- Verify user was added correctly
|
||||
|
||||
### **Step 3: Test Invite Flow**
|
||||
- Create new invite
|
||||
- Accept as test user
|
||||
- Verify user can access band resources
|
||||
|
||||
### **Step 4: Deploy Fix**
|
||||
- Apply permission fix
|
||||
- Add logging
|
||||
- Monitor for issues
|
||||
|
||||
## 🎯 **IMPACT**
|
||||
|
||||
**Current:** Invited users cannot access band resources (403 errors)
|
||||
**After Fix:** Regular band members can see invites and access recordings
|
||||
|
||||
**Files to Change:**
|
||||
- `api/src/rehearsalhub/routers/bands.py` (line 33)
|
||||
|
||||
**Estimated Time:** 15-30 minutes to fix and test
|
||||
Reference in New Issue
Block a user