# 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
