<?php

namespace Drupal\system\Tests\System;

use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Site\Settings;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\simpletest\WebTestBase;

/**
 * Tests Drupal permissions hardening of /sites subdirectories.
 *
 * @group system
 */
class SitesDirectoryHardeningTest extends WebTestBase {
  use StringTranslationTrait;

  /**
   * Tests the default behavior to restrict directory permissions is enforced.
   *
   * Checks both the the current sites directory and settings.php.
   */
  public function testSitesDirectoryHardening() {
    $site_path = $this->kernel->getSitePath();
    $settings_file = $this->settingsFile($site_path);

    // First, we check based on what the initial install has set.
    $this->assertTrue(drupal_verify_install_file($site_path, FILE_NOT_WRITABLE, 'dir'), new FormattableMarkup('Verified permissions for @file.', array('@file' => $site_path)));

    // We intentionally don't check for settings.local.php as that file is
    // not created by Drupal.
    $this->assertTrue(drupal_verify_install_file($settings_file, FILE_EXIST | FILE_READABLE | FILE_NOT_WRITABLE), new FormattableMarkup('Verified permissions for @file.', array('@file' => $settings_file)));

    $this->makeWritable($site_path);
    $this->checkSystemRequirements();

    $this->assertTrue(drupal_verify_install_file($site_path, FILE_NOT_WRITABLE, 'dir'), new FormattableMarkup('Verified permissions for @file after manual permissions change.', array('@file' => $site_path)));
    $this->assertTrue(drupal_verify_install_file($settings_file, FILE_EXIST | FILE_READABLE | FILE_NOT_WRITABLE), new FormattableMarkup('Verified permissions for @file after manual permissions change.', array('@file' => $settings_file)));
  }

  /**
   * Tests writable files remain writable when directory hardening is disabled.
   */
  public function testSitesDirectoryHardeningConfig() {
    $site_path = $this->kernel->getSitePath();
    $settings_file = $this->settingsFile($site_path);

    // Disable permissions enforcement.
    $settings = Settings::getAll();
    $settings['skip_permissions_hardening'] = TRUE;
    new Settings($settings);
    $this->assertTrue(Settings::get('skip_permissions_hardening'), 'Able to set hardening to true');
    $this->makeWritable($site_path);

    // Manually trigger the requirements check.
    $requirements = $this->checkSystemRequirements();
    $this->assertEqual(REQUIREMENT_WARNING, $requirements['configuration_files']['severity'], 'Warning severity is properly set.');
    $this->assertEqual($this->t('Protection disabled'), (string) $requirements['configuration_files']['description']['#context']['configuration_error_list']['#items'][0], 'Description is properly set.');

    $this->assertTrue(is_writable($site_path), 'Site directory remains writable when automatically fixing permissions is disabled.');
    $this->assertTrue(is_writable($settings_file), 'settings.php remains writable when automatically fixing permissions is disabled.');

    // Re-enable permissions enforcement.
    $settings = Settings::getAll();
    $settings['skip_permissions_hardening'] = FALSE;
    new Settings($settings);

    // Manually trigger the requirements check.
    $this->checkSystemRequirements();

    $this->assertFalse(is_writable($site_path), 'Site directory is protected when automatically fixing permissions is enabled.');
    $this->assertFalse(is_writable($settings_file), 'settings.php is protected when automatically fixing permissions is enabled.');
  }

  /**
   * Checks system runtime requirements.
   *
   * @return array
   *   An array of system requirements.
   */
  protected function checkSystemRequirements() {
    module_load_install('system');
    return system_requirements('runtime');
  }

  /**
   * Makes the given path and settings file writable.
   *
   * @param string $site_path
   *   The sites directory path, such as 'sites/default'.
   */
  protected function makeWritable($site_path) {
    chmod($site_path, 0755);
    chmod($this->settingsFile($site_path), 0644);
  }

  /**
   * Returns the path to settings.php
   *
   * @param string $site_path
   *   The sites subdirectory path.
   *
   * @return string
   *   The path to settings.php.
   */
  protected function settingsFile($site_path) {
    $settings_file = $site_path . '/settings.php';
    return $settings_file;
  }

}
