project initialization
Some checks failed
System Monitoring / Health Checks (push) Has been cancelled
System Monitoring / Performance Monitoring (push) Has been cancelled
System Monitoring / Database Monitoring (push) Has been cancelled
System Monitoring / Cache Monitoring (push) Has been cancelled
System Monitoring / Log Monitoring (push) Has been cancelled
System Monitoring / Resource Monitoring (push) Has been cancelled
System Monitoring / Uptime Monitoring (push) Has been cancelled
System Monitoring / Backup Monitoring (push) Has been cancelled
System Monitoring / Security Monitoring (push) Has been cancelled
System Monitoring / Monitoring Dashboard (push) Has been cancelled
System Monitoring / Alerting (push) Has been cancelled
Security Scanning / Dependency Scanning (push) Has been cancelled
Security Scanning / Code Security Scanning (push) Has been cancelled
Security Scanning / Secrets Scanning (push) Has been cancelled
Security Scanning / Container Security Scanning (push) Has been cancelled
Security Scanning / Compliance Checking (push) Has been cancelled
Security Scanning / Security Dashboard (push) Has been cancelled
Security Scanning / Security Remediation (push) Has been cancelled

This commit is contained in:
2025-10-05 02:37:33 +08:00
parent 2cbb6d5fa1
commit b3fff546e9
226 changed files with 97805 additions and 35 deletions

View File

@@ -0,0 +1,404 @@
"""
Integration test for multi-tenant data isolation.
This test MUST fail before implementation.
"""
import pytest
from django.test import TestCase
from django.urls import reverse
from rest_framework.test import APIClient
from rest_framework import status
import json
class TenantIsolationIntegrationTest(TestCase):
def setUp(self):
self.client = APIClient()
# Super admin authentication header
self.admin_auth = {'HTTP_AUTHORIZATION': 'Bearer super_admin_token'}
# Tenant 1 authentication header
self.tenant1_auth = {'HTTP_AUTHORIZATION': 'Bearer tenant1_admin_token'}
# Tenant 2 authentication header
self.tenant2_auth = {'HTTP_AUTHORIZATION': 'Bearer tenant2_admin_token'}
# Test data for different tenants
self.tenant1_user_data = {
'email': 'user1@tenant1.com',
'name': 'User One',
'role': 'MANAGER',
'department': 'Sales'
}
self.tenant2_user_data = {
'email': 'user1@tenant2.com',
'name': 'User One Duplicate',
'role': 'MANAGER',
'department': 'Marketing'
}
self.tenant1_product_data = {
'sku': 'PROD-001',
'name': 'Product A',
'category': 'ELECTRONICS',
'price': 999.99
}
self.tenant2_product_data = {
'sku': 'PROD-001', # Same SKU as tenant1
'name': 'Product A Different',
'category': 'ELECTRONICS',
'price': 899.99
}
def test_user_data_isolation(self):
"""Test that user data is properly isolated between tenants."""
# Step 1: Create users in different tenants with same email structure
tenant1_user_response = self.client.post(
'/api/v1/users/',
data=json.dumps(self.tenant1_user_data),
content_type='application/json',
**self.tenant1_auth
)
assert tenant1_user_response.status_code == status.HTTP_201_CREATED
tenant1_user = tenant1_user_response.json()
tenant2_user_response = self.client.post(
'/api/v1/users/',
data=json.dumps(self.tenant2_user_data),
content_type='application/json',
**self.tenant2_auth
)
assert tenant2_user_response.status_code == status.HTTP_201_CREATED
tenant2_user = tenant2_user_response.json()
# Step 2: Verify tenant isolation - each tenant should only see their own users
tenant1_users_response = self.client.get(
'/api/v1/users/',
**self.tenant1_auth
)
assert tenant1_users_response.status_code == status.HTTP_200_OK
tenant1_users = tenant1_users_response.json()['users']
# Should only see users from tenant1
assert len(tenant1_users) == 1
assert tenant1_users[0]['email'] == self.tenant1_user_data['email']
assert tenant1_users[0]['tenant_id'] == tenant1_user['tenant_id']
tenant2_users_response = self.client.get(
'/api/v1/users/',
**self.tenant2_auth
)
assert tenant2_users_response.status_code == status.HTTP_200_OK
tenant2_users = tenant2_users_response.json()['users']
# Should only see users from tenant2
assert len(tenant2_users) == 1
assert tenant2_users[0]['email'] == self.tenant2_user_data['email']
assert tenant2_users[0]['tenant_id'] == tenant2_user['tenant_id']
# Step 3: Super admin should see all users
admin_users_response = self.client.get(
'/api/v1/users/',
**self.admin_auth
)
assert admin_users_response.status_code == status.HTTP_200_OK
admin_users = admin_users_response.json()['users']
# Should see users from both tenants
assert len(admin_users) >= 2
user_emails = [user['email'] for user in admin_users]
assert self.tenant1_user_data['email'] in user_emails
assert self.tenant2_user_data['email'] in user_emails
def test_product_data_isolation(self):
"""Test that product data is properly isolated between tenants."""
# Step 1: Create products with same SKU in different tenants
tenant1_product_response = self.client.post(
'/api/v1/retail/products/',
data=json.dumps(self.tenant1_product_data),
content_type='application/json',
**self.tenant1_auth
)
assert tenant1_product_response.status_code == status.HTTP_201_CREATED
tenant1_product = tenant1_product_response.json()
tenant2_product_response = self.client.post(
'/api/v1/retail/products/',
data=json.dumps(self.tenant2_product_data),
content_type='application/json',
**self.tenant2_auth
)
assert tenant2_product_response.status_code == status.HTTP_201_CREATED
tenant2_product = tenant2_product_response.json()
# Step 2: Verify SKU isolation - same SKU allowed in different tenants
assert tenant1_product['sku'] == tenant2_product['sku']
assert tenant1_product['id'] != tenant2_product['id']
assert tenant1_product['tenant_id'] != tenant2_product['tenant_id']
# Step 3: Test product retrieval isolation
tenant1_products_response = self.client.get(
'/api/v1/retail/products/',
**self.tenant1_auth
)
assert tenant1_products_response.status_code == status.HTTP_200_OK
tenant1_products = tenant1_products_response.json()['products']
# Should only see products from tenant1
assert len(tenant1_products) == 1
assert tenant1_products[0]['name'] == self.tenant1_product_data['name']
assert tenant1_products[0]['tenant_id'] == tenant1_product['tenant_id']
tenant2_products_response = self.client.get(
'/api/v1/retail/products/',
**self.tenant2_auth
)
assert tenant2_products_response.status_code == status.HTTP_200_OK
tenant2_products = tenant2_products_response.json()['products']
# Should only see products from tenant2
assert len(tenant2_products) == 1
assert tenant2_products[0]['name'] == self.tenant2_product_data['name']
assert tenant2_products[0]['tenant_id'] == tenant2_product['tenant_id']
def test_healthcare_data_isolation(self):
"""Test that healthcare patient data is properly isolated."""
# Patient data for different tenants
tenant1_patient_data = {
'ic_number': '900101-10-1234',
'name': 'Ahmad bin Hassan',
'gender': 'MALE',
'date_of_birth': '1990-01-01'
}
tenant2_patient_data = {
'ic_number': '900101-10-1234', # Same IC number
'name': 'Ahmad bin Ali', # Different name
'gender': 'MALE',
'date_of_birth': '1990-01-01'
}
# Create patients in different tenants
tenant1_patient_response = self.client.post(
'/api/v1/healthcare/patients/',
data=json.dumps(tenant1_patient_data),
content_type='application/json',
**self.tenant1_auth
)
assert tenant1_patient_response.status_code == status.HTTP_201_CREATED
tenant1_patient = tenant1_patient_response.json()
tenant2_patient_response = self.client.post(
'/api/v1/healthcare/patients/',
data=json.dumps(tenant2_patient_data),
content_type='application/json',
**self.tenant2_auth
)
assert tenant2_patient_response.status_code == status.HTTP_201_CREATED
tenant2_patient = tenant2_patient_response.json()
# Verify same IC number allowed in different tenants (healthcare compliance)
assert tenant1_patient['ic_number'] == tenant2_patient['ic_number']
assert tenant1_patient['id'] != tenant2_patient['id']
# Test patient data isolation
tenant1_patients_response = self.client.get(
'/api/v1/healthcare/patients/',
**self.tenant1_auth
)
assert tenant1_patients_response.status_code == status.HTTP_200_OK
tenant1_patients = tenant1_patients_response.json()['patients']
# Should only see patients from tenant1
assert len(tenant1_patients) == 1
assert tenant1_patients[0]['name'] == tenant1_patient_data['name']
def test_cross_tenant_access_prevention(self):
"""Test that cross-tenant access is properly prevented."""
# Step 1: Create a user in tenant1
tenant1_user_response = self.client.post(
'/api/v1/users/',
data=json.dumps(self.tenant1_user_data),
content_type='application/json',
**self.tenant1_auth
)
assert tenant1_user_response.status_code == status.HTTP_201_CREATED
created_user = tenant1_user_response.json()
user_id = created_user['id']
# Step 2: Try to access tenant1 user from tenant2 (should fail)
tenant2_access_response = self.client.get(
f'/api/v1/users/{user_id}/',
**self.tenant2_auth
)
assert tenant2_access_response.status_code == status.HTTP_404_NOT_FOUND
# Step 3: Try to modify tenant1 user from tenant2 (should fail)
modify_data = {'name': 'Hacked Name'}
tenant2_modify_response = self.client.put(
f'/api/v1/users/{user_id}/',
data=json.dumps(modify_data),
content_type='application/json',
**self.tenant2_auth
)
assert tenant2_modify_response.status_code == status.HTTP_404_NOT_FOUND
# Step 4: Verify user data is unchanged
verify_response = self.client.get(
f'/api/v1/users/{user_id}/',
**self.tenant1_auth
)
assert verify_response.status_code == status.HTTP_200_OK
verified_user = verify_response.json()
assert verified_user['name'] == self.tenant1_user_data['name']
def test_database_row_level_security(self):
"""Test that database row-level security is working."""
# This test verifies that data isolation is enforced at the database level
# Create test data in both tenants
self.client.post(
'/api/v1/users/',
data=json.dumps(self.tenant1_user_data),
content_type='application/json',
**self.tenant1_auth
)
self.client.post(
'/api/v1/users/',
data=json.dumps(self.tenant2_user_data),
content_type='application/json',
**self.tenant2_auth
)
# Test direct database queries would be isolated
# This is more of an integration test that would require actual database setup
pass
def test_file_storage_isolation(self):
"""Test that file storage is properly isolated between tenants."""
# Upload files for different tenants
# This would test file storage isolation mechanisms
pass
def test_cache_isolation(self):
"""Test that cache keys are properly isolated between tenants."""
# Test that cache keys include tenant information
# This ensures cache data doesn't leak between tenants
pass
def test_tenant_context_propagation(self):
"""Test that tenant context is properly propagated through the system."""
# Create a user and verify tenant context is maintained across operations
user_response = self.client.post(
'/api/v1/users/',
data=json.dumps(self.tenant1_user_data),
content_type='application/json',
**self.tenant1_auth
)
assert user_response.status_code == status.HTTP_201_CREATED
created_user = user_response.json()
# Verify tenant ID is consistently set
assert 'tenant_id' in created_user
tenant_id = created_user['tenant_id']
# Create a product and verify same tenant context
product_response = self.client.post(
'/api/v1/retail/products/',
data=json.dumps(self.tenant1_product_data),
content_type='application/json',
**self.tenant1_auth
)
assert product_response.status_code == status.HTTP_201_CREATED
created_product = product_response.json()
assert created_product['tenant_id'] == tenant_id
def test_tenant_configuration_isolation(self):
"""Test that tenant configurations are properly isolated."""
# Set tenant-specific configurations
tenant1_config = {
'timezone': 'Asia/Kuala_Lumpur',
'currency': 'MYR',
'date_format': 'DD/MM/YYYY'
}
tenant2_config = {
'timezone': 'Asia/Singapore',
'currency': 'SGD',
'date_format': 'MM/DD/YYYY'
}
# Apply configurations (would need actual config endpoints)
# Verify configurations don't interfere
pass
def test_tenant_performance_isolation(self):
"""Test that one tenant's performance doesn't affect others."""
# This would test resource limits and performance isolation
pass
def test_audit_log_tenant_isolation(self):
"""Test that audit logs are properly isolated by tenant."""
# Perform actions in different tenants
self.client.post(
'/api/v1/users/',
data=json.dumps(self.tenant1_user_data),
content_type='application/json',
**self.tenant1_auth
)
self.client.post(
'/api/v1/users/',
data=json.dumps(self.tenant2_user_data),
content_type='application/json',
**self.tenant2_auth
)
# Check that each tenant only sees their own audit logs
tenant1_audit_response = self.client.get(
'/api/v1/audit/logs/',
**self.tenant1_auth
)
assert tenant1_audit_response.status_code == status.HTTP_200_OK
tenant1_logs = tenant1_audit_response.json()['logs']
# Should only see logs from tenant1 operations
for log in tenant1_logs:
assert log['tenant_id'] is not None
# Super admin should see all logs
admin_audit_response = self.client.get(
'/api/v1/audit/logs/',
**self.admin_auth
)
assert admin_audit_response.status_code == status.HTTP_200_OK
admin_logs = admin_audit_response.json()['logs']
# Should see logs from both tenants
assert len(admin_logs) >= len(tenant1_logs)