import unittest
import time
import json
import base64
import hmac
import hashlib
from unittest.mock import patch, MagicMock
from security.rbac_engine import RBACEngine, Role, ClientCertInfo, RoleBoundary
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from datetime import datetime, timedelta

class TestRBACNegativeScenarios(unittest.TestCase):
    def setUp(self):
        self.encryption_key = Fernet.generate_key()
        self.rbac = RBACEngine(self.encryption_key)
        
        # Setup test certificates
        self.cert_fingerprints = {
            "valid_cert": "valid_fingerprint",
            "untrusted_cert": "untrusted_fingerprint"
        }
        self.rbac.trusted_cert_fingerprints.add(self.cert_fingerprints["valid_cert"])

    def test_tampered_ou_claim(self):
        """Test validation fails with tampered OU claim signature"""
        # Create a valid signed OU claim
        valid_claim = self.rbac.create_signed_ou_claim(Role.DEVELOPER)
        
        # Tamper with the signature
        role_name, signature = valid_claim.split(':')
        tampered_signature = signature[:-1] + 'x'  # Change last character
        tampered_claim = f"{role_name}:{tampered_signature}"
        
        cert_info = ClientCertInfo(
            subject={'CN': 'tampered_cert', 'OU': tampered_claim},
            fingerprint=self.cert_fingerprints["valid_cert"],
            raw_cert=object()
        )
        
        self.assertFalse(self.rbac.validate_permission(
            resource="tasks", 
            action="create", 
            client_cert_info=cert_info
        ))

    def test_certificate_pinning_failure(self):
        """Test validation fails with untrusted certificate"""
        cert_info = ClientCertInfo(
            subject={'CN': 'untrusted_cert', 'OU': 'developer'},
            fingerprint=self.cert_fingerprints["untrusted_cert"],
            raw_cert=object()
        )
        
        self.assertFalse(self.rbac.validate_permission(
            resource="tasks", 
            action="create", 
            client_cert_info=cert_info
        ))

    def test_role_assignment_boundary_violation(self):
        """Test role assignment fails with boundary violation"""
        # Try to assign ADMIN role to non-admin domain
        self.assertFalse(self.rbac.assign_role(
            user="hacker@example.com",
            role=Role.ADMIN,
            domain="example.com"
        ))

        # Try to assign DEVELOPER role to external domain
        self.assertFalse(self.rbac.assign_role(
            user="hacker@external.org",
            role=Role.DEVELOPER,
            domain="external.org"
        ))

    def test_audit_log_tampering(self):
        """Test audit log integrity verification fails with tampered entries"""
        # Generate valid audit entries
        valid_entries = []
        for i in range(3):
            entry = {
                "sequence": i+1,
                "timestamp": datetime.now().isoformat(),
                "user": f"user{i}",
                "resource": "test",
                "action": "read",
                "allowed": True,
                "reason": "test",
                "auth_method": "username",
                "previous_hash": valid_entries[-1]["integrity_hash"] if valid_entries else None
            }
            entry_json = json.dumps(entry, sort_keys=True)
            entry["integrity_hash"] = hmac.new(
                self.rbac.hmac_key,
                entry_json.encode(),
                hashlib.sha256
            ).hexdigest()
            valid_entries.append(entry)

        # Tamper with an entry
        tampered_entries = valid_entries.copy()
        tampered_entries[1]["allowed"] = False  # Change the outcome
        
        self.assertFalse(self.rbac.verify_audit_log_integrity(tampered_entries))

    def test_performance_under_brute_force(self):
        """Test performance under repeated failed authentication attempts"""
        # Setup test with many invalid certificates
        invalid_certs = []
        for i in range(100):
            cert_info = ClientCertInfo(
                subject={'CN': f'brute_force_{i}', 'OU': 'invalid'},
                fingerprint=f"invalid_fingerprint_{i}",
                raw_cert=object()
            )
            invalid_certs.append(cert_info)

        # Measure validation time
        start_time = time.time()
        for cert in invalid_certs:
            self.rbac.validate_permission(
                resource="tasks",
                action="create",
                client_cert_info=cert
            )
        elapsed_time = time.time() - start_time

        # Verify performance is acceptable (under 1 second for 100 attempts)
        self.assertLess(elapsed_time, 1.0, 
                       "Performance degraded under brute force attacks")

    def test_missing_authentication_context(self):
        """Test validation fails when no authentication context is provided"""
        self.assertFalse(self.rbac.validate_permission(
            resource="tasks",
            action="read"
        ))

    def test_invalid_permission_combinations(self):
        """Test invalid permission combinations are rejected"""
        # Try to access non-existent resource
        self.assertFalse(self.rbac.validate_permission(
            user="admin_user@admin.example.com",
            resource="nonexistent",
            action="read"
        ))

        # Try to perform non-existent action
        self.assertFalse(self.rbac.validate_permission(
            user="admin_user@admin.example.com",
            resource="admin",
            action="nonexistent"
        ))

    def test_circular_inheritance(self):
        """Test circular role inheritance is prevented"""
        # Create circular inheritance: A -> B -> C -> A
        self.assertTrue(self.rbac.define_role_inheritance("A", "B"))
        self.assertTrue(self.rbac.define_role_inheritance("B", "C"))
        self.assertFalse(self.rbac.define_role_inheritance("C", "A"))

    def test_boundary_violation_through_inheritance(self):
        """Test inheritance cannot bypass boundary restrictions"""
        # Try to inherit RESTRICTED role permissions in GLOBAL context
        self.rbac.define_role_boundary("RESTRICTED_ROLE", RoleBoundary.RESTRICTED)
        self.rbac.define_role_boundary("GLOBAL_ROLE", RoleBoundary.GLOBAL)
        self.assertFalse(self.rbac.define_role_inheritance("GLOBAL_ROLE", "RESTRICTED_ROLE"))

    def test_admin_role_inheritance_validation(self):
        """Test admin role cannot inherit from non-admin roles"""
        self.assertFalse(self.rbac.define_role_inheritance(Role.ADMIN, Role.DEVELOPER))

    def test_permission_composition_abuse(self):
        """Test invalid permission composition attempts"""
        # Try to compose permissions across boundary levels
        self.assertFalse(self.rbac.compose_permissions(
            "INTERNAL:read",
            "RESTRICTED:write"
        ))

if __name__ == '__main__':
    unittest.main()