config_preview_deploy-1.0.0-alpha3/tests/src/Kernel/IncomingRequestTest.php
tests/src/Kernel/IncomingRequestTest.php
<?php
declare(strict_types=1);
namespace Drupal\Tests\config_preview_deploy\Kernel;
use Symfony\Component\HttpFoundation\Response;
use Drupal\consumers\Entity\Consumer;
use Drupal\Core\Url;
use Drupal\KernelTests\KernelTestBase;
use Drupal\user\Entity\User;
use Symfony\Component\HttpFoundation\Request;
/**
* Tests incoming requests to production endpoints.
*
* @group config_preview_deploy
*/
class IncomingRequestTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'config_preview_deploy',
'simple_oauth',
'simple_oauth_static_scope',
'consumers',
'key',
'system',
'user',
'serialization',
'options',
'file',
'image',
];
/**
* Test user.
*/
protected User $testUser;
/**
* OAuth consumer.
*/
protected Consumer $consumer;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Set HTTP_HOST for environment detection.
$_SERVER['HTTP_HOST'] = 'test.example.com';
$this->installEntitySchema('user');
$this->installEntitySchema('consumer');
$this->installEntitySchema('oauth2_token');
$this->installEntitySchema('oauth2_scope');
$this->installEntitySchema('oauth2_token_type');
$this->installEntitySchema('key');
$this->installConfig([
'system',
'user',
'simple_oauth',
'consumers',
'config_preview_deploy',
]);
// Create temporary directory for OAuth keys.
$keysDir = sys_get_temp_dir() . '/oauth_keys_' . uniqid();
mkdir($keysDir, 0777, TRUE);
// Generate test keys.
$this->generateTestKeys($keysDir);
// Configure simple_oauth to use our test keys.
$this->config('simple_oauth.settings')
->set('public_key', $keysDir . '/public.key')
->set('private_key', $keysDir . '/private.key')
->save();
// Create test user with deployment permissions.
$this->testUser = User::create([
'name' => 'deploy_user',
'mail' => 'deploy@example.com',
'status' => 1,
]);
$this->testUser->save();
// Grant the user the required permission.
user_role_grant_permissions('authenticated', ['accept config deployments']);
// Run module install to create OAuth consumer.
\Drupal::moduleHandler()->loadInclude('config_preview_deploy', 'install');
config_preview_deploy_install();
// Load the created consumer.
$consumers = \Drupal::entityTypeManager()
->getStorage('consumer')
->loadByProperties(['client_id' => 'config_preview_deploy']);
$this->consumer = reset($consumers);
}
/**
* Generate test RSA key pair for OAuth.
*/
protected function generateTestKeys(string $keysDir): void {
// Generate private key.
$private_key = openssl_pkey_new([
'digest_alg' => 'sha256',
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
]);
// Export private key.
openssl_pkey_export_to_file($private_key, $keysDir . '/private.key');
// Export public key.
$public_key_details = openssl_pkey_get_details($private_key);
file_put_contents($keysDir . '/public.key', $public_key_details['key']);
}
/**
* Helper to issue a request and return the response.
*/
protected function request(string $path, string $method = 'GET', array $query = [], array $headers = [], string $content = ''): Response {
$request = Request::create($path, $method, $query, [], [], [], $content);
// Add headers.
foreach ($headers as $name => $value) {
$request->headers->set($name, $value);
}
return $this->container->get('http_kernel')->handle($request);
}
/**
* Tests status endpoint with valid token.
*/
public function testStatusEndpointWithValidToken(): void {
// Create valid token parameters using HashVerification service.
$timestamp = time();
$hashVerification = $this->container->get('config_preview_deploy.hash_verification');
// Get the host from Drupal's base URL to match verification logic.
$currentUrl = Url::fromRoute('system.admin', [], ['absolute' => TRUE])->toString();
$productionHost = parse_url($currentUrl, PHP_URL_HOST);
$token = $hashVerification->generateVerificationHash($productionHost, $timestamp);
$response = $this->request('/api/config-preview-deploy/status', 'GET', [
'timestamp' => $timestamp,
'token' => $token,
]);
$this->assertEquals(200, $response->getStatusCode());
$data = json_decode($response->getContent(), TRUE);
$this->assertArrayHasKey('last_change', $data);
$this->assertArrayHasKey('timestamp', $data);
}
/**
* Tests status endpoint with invalid token.
*/
public function testStatusEndpointWithInvalidToken(): void {
$response = $this->request('/api/config-preview-deploy/status', 'GET', [
'timestamp' => time(),
'token' => 'invalid-token',
]);
$this->assertEquals(403, $response->getStatusCode());
$data = json_decode($response->getContent(), TRUE);
$this->assertEquals('Invalid verification hash', $data['error']);
}
/**
* Tests status endpoint with expired timestamp.
*/
public function testStatusEndpointWithExpiredTimestamp(): void {
// More than 5 minutes old.
$timestamp = time() - 400;
$hashVerification = $this->container->get('config_preview_deploy.hash_verification');
$productionHost = 'test.example.com';
$token = $hashVerification->generateVerificationHash($productionHost, $timestamp);
$response = $this->request('/api/config-preview-deploy/status', 'GET', [
'timestamp' => $timestamp,
'token' => $token,
]);
$this->assertEquals(403, $response->getStatusCode());
$data = json_decode($response->getContent(), TRUE);
$this->assertEquals('Invalid verification hash', $data['error']);
}
/**
* Tests deploy endpoint requires OAuth authentication.
*/
public function testDeployEndpointRequiresOauth(): void {
// Create deployment request without OAuth token.
$data = json_encode([
'diff' => 'test diff',
'environment' => 'test',
'auth_hash' => 'test_hash',
'timestamp' => time(),
]);
$response = $this->request('/admin/config/development/config-preview-deploy/deploy-endpoint', 'POST', [], [
'Content-Type' => 'application/json',
], $data);
// Should return 403 due to missing OAuth authentication.
$this->assertEquals(403, $response->getStatusCode());
}
/**
* Tests deploy endpoint with invalid OAuth token.
*/
public function testDeployEndpointWithInvalidOauth(): void {
$data = json_encode([
'diff' => 'test diff',
'environment' => 'test',
'auth_hash' => 'test_hash',
'timestamp' => time(),
]);
$response = $this->request('/admin/config/development/config-preview-deploy/deploy-endpoint', 'POST', [], [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer invalid-oauth-token-12345',
], $data);
// Should return 401 due to invalid OAuth token.
$this->assertEquals(401, $response->getStatusCode());
}
/**
* Tests deploy endpoint OAuth protection with malformed request.
*/
public function testDeployEndpointOauthProtectionWithMalformedRequest(): void {
$response = $this->request('/admin/config/development/config-preview-deploy/deploy-endpoint', 'POST', [], [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer invalid-token',
], 'invalid json data');
// OAuth protection should trigger before request parsing.
$this->assertEquals(401, $response->getStatusCode());
}
/**
* Tests that OAuth protection is enforced on deploy endpoint.
*/
public function testOauthProtectionOnDeployEndpoint(): void {
// Test deploy endpoint (POST) - the main OAuth-protected endpoint.
$response = $this->request('/admin/config/development/config-preview-deploy/deploy-endpoint', 'POST', [], [
'Content-Type' => 'application/json',
], '{}');
$this->assertEquals(403, $response->getStatusCode(), "Deploy endpoint should require OAuth authentication");
}
/**
* Tests deploy endpoint handles different request scenarios.
*/
public function testDeployEndpointScenarios(): void {
// Test with valid JSON structure but no OAuth.
$data = json_encode([
'diff' => 'test diff',
'environment' => 'test',
'auth_hash' => 'test_hash',
'timestamp' => time(),
]);
$response = $this->request('/admin/config/development/config-preview-deploy/deploy-endpoint', 'POST', [], [
'Content-Type' => 'application/json',
], $data);
// Should return 403 due to missing OAuth authentication.
$this->assertEquals(403, $response->getStatusCode());
}
/**
* Tests environment name extraction from services.
*/
public function testEnvironmentNameExtraction(): void {
$config_verifier = \Drupal::service('config_preview_deploy.config_verifier');
$environment = $config_verifier->getEnvironmentName();
$this->assertIsString($environment);
$this->assertNotEmpty($environment);
// In test environment, should be one of these values.
$valid_environments = ['local', 'test', gethostname()];
$this->assertTrue(in_array($environment, $valid_environments));
}
}
