# AI Assistant Permission Security

**Status**: ✅ IMPLEMENTED  
**Date**: 2025-01-16  
**Critical Security Feature**: Prevents unauthorized data access through AI queries

## Overview

The AI Assistant now enforces ERP permissions at multiple levels to prevent users from accessing data they shouldn't see. This addresses the critical security gap where users could ask financial questions even without financial permissions.

## Security Layers

### 1. Permission-Aware System Prompts
**File**: `controllers/AiAssistantController.php::buildPermissionContext()`

Every AI chat message now includes a system prompt that explicitly states:
- User's role and accessible modules
- **Restricted modules** (with ❌ CRITICAL markers)
- Financial data restrictions
- HR/Payroll restrictions
- Data scope (location-based restrictions)
- SQL generation rules

**Example Context**:
```
IMPORTANT SECURITY CONTEXT:

You are an AI assistant helping a user in their ERP system. You MUST respect their permissions.

Current Page: Customers > edit
User Role: Sales Representative

Accessible Modules: customers, products, sales, inventory

RESTRICTED Modules (DO NOT provide data from): accounting, invoices, hr, employees, payroll, financial

❌ CRITICAL: User does NOT have access to financial data (invoices, accounting, revenue, costs, salaries).
You MUST refuse to answer questions about financial information.
If asked, respond: 'I cannot access financial data. You don't have the required permissions.'

🔒 SQL GENERATION RULES:
- If generating SQL queries, you MUST only query tables the user can access
- NEVER generate queries for restricted modules
- For financial tables (invoices, journal_entries, payments): Check financial access first
```

### 2. Table-Level SQL Permissions
**Files**: 
- `core/AI.php::generateSQL()` - Enhanced with `$allowedTables` and `$restrictedTables` parameters
- `core/AI.php::executeSafeQuery()` - Validates queries against permitted tables
- `core/AI.php::validateTablePermissions()` - Checks SQL for unauthorized table access
- `core/AI.php::extractTableNames()` - Parses SQL to identify table usage

**Permission Mapping** (in `AiAssistantController::getTablePermissions()`):
```php
'accounting.view' => ['journal_entries', 'chart_of_accounts', 'fiscal_periods', 'tax_filings']
'invoices.view' => ['invoices', 'invoice_items', 'customer_payments']
'hr.view' => ['employees', 'employee_contracts', 'training_programs', 'performance_reviews']
'payroll.view' => ['payroll', 'payroll_items', 'attendance', 'time_off_requests']
'customers.view' => ['customers', 'customer_contacts']
'suppliers.view' => ['suppliers', 'supplier_contacts']
'products.view' => ['products', 'product_categories', 'product_variants']
'inventory.view' => ['stock_movements', 'stock_transfers', 'lot_serial_tracking']
'sales.view' => ['sales_orders', 'quotes', 'delivery_notes']
'purchases.view' => ['purchase_orders', 'purchase_receipts', 'purchase_returns']
```

### 3. Data Scope Enforcement
**File**: `controllers/AiAssistantController.php::getUserDataScope()`

Users are restricted to their assigned locations:
- **Admin/Manager**: Can access all locations
- **Regular users**: Only see data from assigned locations via `user_locations` table
- AI context explicitly states accessible locations

### 4. SQL Query Validation
**Process Flow**:
1. User asks question → AI generates SQL with permission context
2. AI checks if restricted tables are needed → Returns "ACCESS DENIED" if yes
3. Generated SQL is validated against user's table permissions
4. If validation passes → Query executes
5. If validation fails → Error: "Access denied: You don't have permission to query these tables"

**Validation Logic** (`core/AI.php`):
```php
// Extract table names from SQL (FROM, JOIN clauses)
$tablesUsed = $this->extractTableNames($sql);

// Check restricted tables
$violatedTables = array_intersect($tablesUsed, $restrictedTables);
if (!empty($violatedTables)) {
    return ['error' => 'Access denied: ...'];
}

// Check allowed tables (if specified)
$unauthorizedTables = array_diff($tablesUsed, $allowedTables);
if (!empty($unauthorizedTables)) {
    return ['error' => 'Access denied: You can only query these tables: ...'];
}
```

## Test Scenarios

### Test Case 1: User WITHOUT Financial Permissions
**User**: Sales Representative (no `accounting.view` or `invoices.view`)

**Question**: "Show me total revenue this month"

**Expected Behavior**:
- AI system prompt includes: "❌ CRITICAL: User does NOT have access to financial data"
- AI response: "I cannot access financial data. You don't have the required permissions."
- If AI tries to generate SQL with `invoices` table → Blocked by `validateTablePermissions()`
- Error: "Access denied: You don't have permission to query these tables: invoices"

### Test Case 2: User WITH HR but WITHOUT Payroll Permissions
**User**: HR Manager (has `hr.view` but not `payroll.view`)

**Question**: "What's the average salary in the engineering department?"

**Expected Behavior**:
- AI context: "❌ User can see employee info but NOT payroll/salary data"
- AI response: "I can see employee information, but I don't have access to salary data. You need payroll permissions."
- If SQL includes `payroll` table → Blocked

### Test Case 3: User WITH Customer Permissions
**User**: Sales Rep (has `customers.view`)

**Question**: "List all active customers"

**Expected Behavior**:
- ✅ Allowed: `customers` table is in allowed list
- SQL generated: `SELECT * FROM customers WHERE status = 'active' LIMIT 10;`
- Query executes successfully
- Results returned with data scope filter (only their assigned locations)

### Test Case 4: Data Scope Restriction
**User**: Location A Manager (assigned to Location A only)

**Question**: "Show all products"

**Expected Behavior**:
- SQL executes successfully
- Results filtered to Location A only via `applyDataScopeFilter()` (existing ERP function)
- Cannot see Location B or C products

## Integration Points

### Chat Interface (`sendMessage()`)
```php
// Get permission context
$permissionContext = $this->buildPermissionContext($userId, $userContext);

// Append to system prompt
$systemPrompt = $conversation['system_prompt'] . "\n\n" . $permissionContext;

// Send to AI
$response = $this->ai->chat($message, $systemPrompt);
```

### Data Studio (`dataAnalysis()`)
```php
// Get table permissions
$tablePerms = $this->getTablePermissions($userId);
$allowedTables = $tablePerms['allowed'];
$restrictedTables = $tablePerms['restricted'];

// Generate SQL with permissions
$sql = $this->ai->generateSQL($question, $allowedTables, $restrictedTables);

// Execute with validation
$result = $this->ai->executeSafeQuery($sql, $allowedTables, $restrictedTables);
```

### Global AI Button (Context-Aware Modal)
**File**: `views/layouts/app.php`

The floating AI modal automatically detects current page context and includes it in the permission check:
```javascript
context: {
    module: 'customers',
    page: 'edit',
    id: '123',
    url: window.location.href
}
```

**Security Note**: Even if a user navigates to a restricted page via direct URL (before being blocked by PHP permissions), the AI won't answer questions about that data.

## Configuration

### Adding New Permission → Table Mappings

**Location**: `controllers/AiAssistantController.php`, method `getTablePermissions()`

To add a new module:
```php
$tablePermissions = [
    // ... existing mappings ...
    'new_module.view' => ['table1', 'table2', 'table3']
];
```

### Module Permission List

**Location**: `controllers/AiAssistantController.php`, method `buildPermissionContext()`

To add a new module to the accessibility check:
```php
$modulePermissions = [
    // ... existing mappings ...
    'new_module' => 'new_module.view'
];
```

## Error Messages

### User-Facing Errors
- **Chat context violation**: "I cannot access [module] data. You don't have the required permissions."
- **SQL generation blocked**: "You do not have permission to query the requested data"
- **SQL execution blocked**: "Access denied: You don't have permission to query these tables: [table_list]"

### Logged Errors
All permission denials are logged via:
```php
$this->model->logUsage($userId, 'data_analysis', $model, 0, $responseTime, false, 'Access denied or failed to generate SQL');
```

## Future Enhancements

### Potential Improvements (Not Yet Implemented)
1. **Column-level permissions**: Restrict specific columns within allowed tables (e.g., see employee names but not SSN)
2. **Dynamic schema filtering**: Only show allowed tables in `getDatabaseSchema()`
3. **Audit trail**: Track what questions users ask about restricted data
4. **Permission escalation requests**: "Ask manager for access" workflow
5. **Time-based restrictions**: Certain data only accessible during business hours
6. **Row-level security**: Beyond location scope, restrict by department, team, etc.

## Testing Checklist

- [ ] Create test user with no financial permissions
- [ ] Ask financial question → Verify denial
- [ ] Attempt SQL generation for restricted table → Verify blocked
- [ ] Create test user with HR but not payroll
- [ ] Ask salary question → Verify denial
- [ ] Verify allowed queries work (customers, products)
- [ ] Test Data Studio with restricted user
- [ ] Test global AI modal with restricted user on various pages
- [ ] Verify location-based data scope works
- [ ] Check usage logs for permission denials

## Security Best Practices

### Do's ✅
- Always use `buildPermissionContext()` when calling AI chat
- Always use `getTablePermissions()` for SQL generation
- Log all permission denials for auditing
- Keep table permission mappings up to date
- Test with multiple user roles regularly

### Don'ts ❌
- Don't trust AI to self-enforce permissions (always validate)
- Don't skip permission checks for "simple" queries
- Don't assume column names are secure (they can leak info)
- Don't let users edit SQL directly without validation
- Don't bypass `applyDataScopeFilter()` for location restrictions

## Maintenance

### When Adding New Tables
1. Add table to appropriate permission in `getTablePermissions()`
2. Update documentation
3. Test with restricted user

### When Adding New Modules
1. Add permission to `modulePermissions` array
2. Map tables in `getTablePermissions()`
3. Add to `buildPermissionContext()` checks if critical (financial/HR)
4. Update test cases

## Related Documentation
- `docs/AI_ASSISTANT_COMPLETE.md` - Full AI Assistant feature documentation
- `docs/AI_GLOBAL_ASSISTANT.md` - Global context-aware button documentation
- `docs/DATA_ACCESS_CONTROL_IMPLEMENTATION.md` - Data scope and location restrictions
- `WARP.md` - General security requirements and patterns

## Support

For issues or questions about AI permission security:
1. Check user's role and permissions in database: `role_permissions` table
2. Check AI usage logs: `ai_usage_logs` table
3. Enable debug logging in `core/AI.php` and `AiAssistantController.php`
4. Review permission context in AI system prompt
5. Verify table mappings are correct for the module
