- Simplified HasTenantAccess permission logic to ensure every authenticated user has an account. - Added fallback to system account for OpenAI settings in AI configuration. - Allowed any authenticated user to check task progress in IntegrationSettingsViewSet. - Created a script to identify and fix orphaned users without accounts. - Updated error response handling in business endpoints for clarity.
193 lines
5.8 KiB
Markdown
193 lines
5.8 KiB
Markdown
# CRITICAL MULTI-TENANCY FIXES - December 10, 2025
|
|
|
|
## PROBLEM SUMMARY
|
|
|
|
Users are unable to use business features (auto_cluster, automation, etc.) despite being authenticated. The error is **"Account is required"** or permission denied.
|
|
|
|
## ROOT CAUSE
|
|
|
|
The system has **structural** issues in the multi-tenancy implementation:
|
|
|
|
### 1. **User Model Allows NULL Accounts**
|
|
**File**: `backend/igny8_core/auth/models.py` (line 644)
|
|
```python
|
|
account = models.ForeignKey('igny8_core_auth.Account',
|
|
on_delete=models.CASCADE,
|
|
null=True, # ❌ WRONG
|
|
blank=True, # ❌ WRONG
|
|
...)
|
|
```
|
|
|
|
**Problem**: Users can exist without accounts (orphaned users). When middleware sets `request.account` from `request.user.account`, it becomes `None` for orphaned users.
|
|
|
|
**Impact**: ALL business endpoints that check `request.account` fail.
|
|
|
|
### 2. **HasTenantAccess Permission Too Complex**
|
|
**File**: `backend/igny8_core/api/permissions.py` (lines 24-67)
|
|
|
|
**Problem**: The permission class had unnecessary fallback logic and unclear flow:
|
|
- Checked `request.account`
|
|
- Fell back to `request.user.account`
|
|
- Compared if they match
|
|
- Returned `False` on ANY exception
|
|
|
|
**Impact**: Added complexity made debugging hard. If user has no account, access is denied silently.
|
|
|
|
### 3. **Business Endpoints Explicitly Check `request.account`**
|
|
**File**: `backend/igny8_core/modules/planner/views.py` (line 633)
|
|
```python
|
|
account = getattr(request, 'account', None)
|
|
if not account:
|
|
return error_response(error='Account is required', ...)
|
|
```
|
|
|
|
**Problem**: Direct dependency on `request.account`. If middleware fails to set it, feature breaks.
|
|
|
|
**Impact**: Auto-cluster and other AI functions fail with "Account is required".
|
|
|
|
## FIXES APPLIED
|
|
|
|
### ✅ Fix 1: Simplified HasTenantAccess Permission
|
|
**File**: `backend/igny8_core/api/permissions.py`
|
|
|
|
**Change**: Removed fallback logic. Made it clear: **authenticated users MUST have accounts**.
|
|
|
|
```python
|
|
# SIMPLIFIED LOGIC: Every authenticated user MUST have an account
|
|
# Middleware already set request.account from request.user.account
|
|
# Just verify it exists
|
|
if not hasattr(request.user, 'account'):
|
|
return False
|
|
|
|
try:
|
|
user_account = request.user.account
|
|
if not user_account:
|
|
return False
|
|
return True
|
|
except (AttributeError, Exception):
|
|
return False
|
|
```
|
|
|
|
### ✅ Fix 2: Task Progress Permission
|
|
**File**: `backend/igny8_core/modules/system/integration_views.py` (line 900)
|
|
|
|
**Change**: Allowed any authenticated user to check task progress (not just system accounts).
|
|
|
|
```python
|
|
@action(..., permission_classes=[IsAuthenticatedAndActive])
|
|
def task_progress(self, request, task_id=None):
|
|
```
|
|
|
|
### ✅ Fix 3: AI Settings Fallback
|
|
**File**: `backend/igny8_core/ai/settings.py`
|
|
|
|
**Change**: Added fallback to system account (aws-admin) for OpenAI settings when user account doesn't have them configured.
|
|
|
|
### ✅ Fix 4: Error Response Parameter
|
|
**File**: `backend/igny8_core/modules/planner/views.py`
|
|
|
|
**Change**: Fixed `error_response()` call - changed invalid `extra_data` parameter to `debug_info`.
|
|
|
|
## REQUIRED ACTIONS
|
|
|
|
### 🔴 CRITICAL: Fix Orphaned Users
|
|
|
|
1. **Run the fix script**:
|
|
```bash
|
|
cd /data/app/igny8/backend
|
|
python3 fix_orphaned_users.py
|
|
```
|
|
|
|
2. **The script will**:
|
|
- Find users with `account = NULL`
|
|
- Create accounts for them OR delete them
|
|
- Report results
|
|
|
|
### 🔴 CRITICAL: Make Account Field Required
|
|
|
|
After fixing orphaned users, update the User model:
|
|
|
|
**File**: `backend/igny8_core/auth/models.py` (line 644)
|
|
|
|
**Change**:
|
|
```python
|
|
# BEFORE
|
|
account = models.ForeignKey(..., null=True, blank=True, ...)
|
|
|
|
# AFTER
|
|
account = models.ForeignKey(..., null=False, blank=False, ...)
|
|
```
|
|
|
|
**Then create and run migration**:
|
|
```bash
|
|
cd /data/app/igny8/backend
|
|
python3 manage.py makemigrations
|
|
python3 manage.py migrate
|
|
```
|
|
|
|
## VERIFICATION
|
|
|
|
After fixes, verify:
|
|
|
|
1. **Check no orphaned users**:
|
|
```bash
|
|
cd /data/app/igny8/backend
|
|
python3 manage.py shell -c "
|
|
from igny8_core.auth.models import User
|
|
orphaned = User.objects.filter(account__isnull=True).count()
|
|
print(f'Orphaned users: {orphaned}')
|
|
"
|
|
```
|
|
Expected: `Orphaned users: 0`
|
|
|
|
2. **Test auto-cluster**:
|
|
- Login as normal user
|
|
- Select 5+ keywords
|
|
- Click "Auto Cluster"
|
|
- Should work without "Account is required" error
|
|
|
|
3. **Test task progress**:
|
|
- Start any AI function
|
|
- Progress modal should show real-time updates
|
|
- No "403 Forbidden" errors
|
|
|
|
## ARCHITECTURE PRINCIPLES ESTABLISHED
|
|
|
|
1. **NO NULL ACCOUNTS**: Every user MUST have an account. Period.
|
|
|
|
2. **NO FALLBACKS**: If `request.user.account` is None, it's a data integrity issue, not a code issue.
|
|
|
|
3. **CLEAR FLOW**:
|
|
- User registers → Account created → User.account set
|
|
- User logs in → Middleware sets request.account from user.account
|
|
- Permission checks → Verify request.user.account exists
|
|
- Business logic → Use request.account directly
|
|
|
|
4. **FAIL FAST**: Don't hide errors with fallbacks. If account is missing, raise error.
|
|
|
|
## FILES MODIFIED
|
|
|
|
1. `backend/igny8_core/api/permissions.py` - Simplified HasTenantAccess
|
|
2. `backend/igny8_core/modules/system/integration_views.py` - Fixed task_progress permission
|
|
3. `backend/igny8_core/ai/settings.py` - Added system account fallback for AI settings
|
|
4. `backend/igny8_core/modules/planner/views.py` - Fixed error_response call
|
|
|
|
## FILES CREATED
|
|
|
|
1. `backend/fix_orphaned_users.py` - Script to fix orphaned users
|
|
2. `MULTI-TENANCY-FIXES-DEC-2025.md` - This document
|
|
|
|
## NEXT STEPS
|
|
|
|
1. ✅ Run orphaned users fix script
|
|
2. ✅ Make User.account field required (migration)
|
|
3. ✅ Test all business features
|
|
4. ✅ Update documentation to reflect "no fallbacks" principle
|
|
5. ✅ Add database constraints to prevent orphaned users
|
|
|
|
---
|
|
|
|
**Date**: December 10, 2025
|
|
**Status**: FIXES APPLIED - VERIFICATION PENDING
|
|
**Priority**: CRITICAL
|