Files
michaelschiemer/tests/debug/test-csrf-integration.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

247 lines
7.3 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use App\Framework\DateTime\SystemClock;
use App\Framework\Http\Session\Session;
use App\Framework\Http\Session\SessionId;
use App\Framework\LiveComponents\ComponentEventDispatcher;
use App\Framework\LiveComponents\Contracts\LiveComponentContract;
use App\Framework\LiveComponents\LiveComponentHandler;
use App\Framework\LiveComponents\ValueObjects\ActionParameters;
use App\Framework\LiveComponents\ValueObjects\ComponentData;
use App\Framework\LiveComponents\ValueObjects\ComponentId;
use App\Framework\Random\SecureRandomGenerator;
use App\Framework\Security\CsrfToken;
use App\Framework\Security\CsrfTokenGenerator;
use App\Framework\View\LiveComponentRenderer;
use App\Framework\View\TemplateRenderer;
echo "=== CSRF Integration Test ===\n\n";
// Create real Session instance for testing
$sessionId = SessionId::fromString(bin2hex(random_bytes(16)));
$clock = new SystemClock();
$randomGenerator = new SecureRandomGenerator();
$csrfGenerator = new CsrfTokenGenerator($randomGenerator);
$session = Session::fromArray($sessionId, $clock, $csrfGenerator, []);
// Test 1: Token Generation in Renderer
echo "Test 1: Token Generation in LiveComponentRenderer\n";
echo "------------------------------------------------\n";
$mockTemplateRenderer = new class () implements TemplateRenderer {
public function render(\App\Framework\View\RenderContext $context): string
{
return '';
}
public function renderPartial(\App\Framework\View\RenderContext $context): string
{
return '';
}
};
$renderer = new LiveComponentRenderer($mockTemplateRenderer, $session);
$componentId = 'counter:test123';
$html = $renderer->renderWithWrapper(
$componentId,
'<div>Counter: 0</div>',
['count' => 0]
);
echo "\nGenerated HTML:\n";
echo substr($html, 0, 200) . "...\n";
// Extract token from HTML
if (preg_match('/data-csrf-token="([^"]+)"/', $html, $matches)) {
$extractedToken = $matches[1];
echo "\n✓ CSRF token found in HTML: " . substr($extractedToken, 0, 16) . "...\n";
} else {
echo "\n✗ CSRF token NOT found in HTML!\n";
exit(1);
}
// Test 2: Token Validation in Handler
echo "\n\nTest 2: Token Validation in LiveComponentHandler\n";
echo "------------------------------------------------\n";
$eventDispatcher = new ComponentEventDispatcher();
$handler = new LiveComponentHandler($eventDispatcher, $session);
// Create test component
$testComponent = new class (ComponentId::fromString($componentId)) implements LiveComponentContract {
public function __construct(private ComponentId $id, private int $count = 0)
{
}
public function getId(): ComponentId
{
return $this->id;
}
public function getData(): ComponentData
{
return ComponentData::fromArray(['count' => $this->count]);
}
public function increment(): ComponentData
{
echo " [Component] Executing increment action\n";
$this->count++;
return $this->getData();
}
};
// Test 2a: Valid Token
echo "\nTest 2a: Valid CSRF Token\n";
try {
$csrfToken = CsrfToken::fromString($extractedToken);
$params = ActionParameters::fromArray([], $csrfToken);
$result = $handler->handle($testComponent, 'increment', $params);
echo "✓ Action executed successfully!\n";
echo " New state: count = " . $result->state->data['count'] . "\n";
} catch (\Exception $e) {
echo "✗ Failed: " . $e->getMessage() . "\n";
exit(1);
}
// Test 2b: Missing Token
echo "\nTest 2b: Missing CSRF Token\n";
try {
$params = ActionParameters::fromArray([]);
$handler->handle($testComponent, 'increment', $params);
echo "✗ Should have thrown exception for missing token!\n";
exit(1);
} catch (\InvalidArgumentException $e) {
echo "✓ Correctly rejected: " . $e->getMessage() . "\n";
}
// Test 2c: Invalid Token
echo "\nTest 2c: Invalid CSRF Token\n";
try {
$invalidToken = CsrfToken::fromString(bin2hex(random_bytes(16)));
$params = ActionParameters::fromArray([], $invalidToken);
$handler->handle($testComponent, 'increment', $params);
echo "✗ Should have thrown exception for invalid token!\n";
exit(1);
} catch (\RuntimeException $e) {
echo "✓ Correctly rejected: " . $e->getMessage() . "\n";
}
// Test 3: Token Isolation Between Components
echo "\n\nTest 3: Token Isolation Between Components\n";
echo "------------------------------------------------\n";
$componentA = 'counter:instanceA';
$componentB = 'counter:instanceB';
// Render component A
$htmlA = $renderer->renderWithWrapper($componentA, '<div>A</div>', []);
preg_match('/data-csrf-token="([^"]+)"/', $htmlA, $matchesA);
$tokenA = CsrfToken::fromString($matchesA[1]);
echo "Component A token: " . substr($matchesA[1], 0, 16) . "...\n";
// Try to use token A with component B
$testComponentB = new class (ComponentId::fromString($componentB)) implements LiveComponentContract {
public function __construct(private ComponentId $id)
{
}
public function getId(): ComponentId
{
return $this->id;
}
public function getData(): ComponentData
{
return ComponentData::fromArray([]);
}
public function action(): ComponentData
{
return $this->getData();
}
};
try {
$params = ActionParameters::fromArray([], $tokenA);
$handler->handle($testComponentB, 'action', $params);
echo "✗ Should have rejected token from different component!\n";
exit(1);
} catch (\RuntimeException $e) {
echo "✓ Correctly isolated: Token from component A rejected by component B\n";
}
// Test 4: End-to-End Flow
echo "\n\nTest 4: Complete End-to-End CSRF Flow\n";
echo "------------------------------------------------\n";
$e2eComponentId = 'counter:e2e';
echo "1. Rendering component...\n";
$htmlE2E = $renderer->renderWithWrapper($e2eComponentId, '<div>E2E Test</div>', ['count' => 0]);
echo "2. Extracting CSRF token from HTML...\n";
preg_match('/data-csrf-token="([^"]+)"/', $htmlE2E, $matchesE2E);
$tokenE2E = CsrfToken::fromString($matchesE2E[1]);
echo "3. Simulating client action with extracted token...\n";
$e2eComponent = new class (ComponentId::fromString($e2eComponentId)) implements LiveComponentContract {
public function __construct(private ComponentId $id, private int $count = 0)
{
}
public function getId(): ComponentId
{
return $this->id;
}
public function getData(): ComponentData
{
return ComponentData::fromArray(['count' => $this->count]);
}
public function increment(): ComponentData
{
$this->count++;
return $this->getData();
}
};
$paramsE2E = ActionParameters::fromArray([], $tokenE2E);
$resultE2E = $handler->handle($e2eComponent, 'increment', $paramsE2E);
echo "4. Verifying result...\n";
if ($resultE2E->state->data['count'] === 1) {
echo "✓ E2E Flow completed successfully!\n";
} else {
echo "✗ E2E Flow failed: unexpected state\n";
exit(1);
}
echo "\n=== All Tests Passed! ===\n";
echo "\nSummary:\n";
echo " ✓ Token generation in renderer\n";
echo " ✓ Token validation in handler\n";
echo " ✓ Missing token rejection\n";
echo " ✓ Invalid token rejection\n";
echo " ✓ Token isolation between components\n";
echo " ✓ Complete end-to-end flow\n";
echo "\nCSRF Integration is working correctly!\n";