image_to_media_swapper-2.x-dev/src/Form/SecuritySettingsForm.php

src/Form/SecuritySettingsForm.php
<?php

declare(strict_types=1);

namespace Drupal\image_to_media_swapper\Form;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\TypedConfigManagerInterface;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\image_to_media_swapper\SwapperService;

/**
 * Configure security settings for Image to Media Swapper module.
 */
final class SecuritySettingsForm extends ConfigFormBase {

  /**
   * The swapper service.
   */
  protected SwapperService $swapperService;

  /**
   * SecuritySettingsForm constructor.
   */
  public function __construct(
    ConfigFactoryInterface $configFactory,
    TypedConfigManagerInterface $typedConfigManager,
    SwapperService $swapperService,
  ) {
    parent::__construct($configFactory, $typedConfigManager);
    $this->swapperService = $swapperService;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container): self {
    /** @var \Drupal\Core\Config\ConfigFactoryInterface $configFactory */
    $configFactory = $container->get('config.factory');
    /** @var \Drupal\Core\Config\TypedConfigManagerInterface $typedConfigManager */
    $typedConfigManager = $container->get('config.typed');
    /** @var \Drupal\image_to_media_swapper\SwapperService $swapperService */
    $swapperService = $container->get('image_to_media_swapper.service');
    return new self(
      $configFactory,
      $typedConfigManager,
      $swapperService,
    );
  }

  /**
   * Config settings.
   *
   * @var string
   */
  const SETTINGS = 'image_to_media_swapper.security_settings';

  /**
   * {@inheritdoc}
   */
  public function getFormId(): string {
    return 'image_to_media_swapper_security_settings';
  }

  /**
   * {@inheritdoc}
   */
  protected function getEditableConfigNames(): array {
    return [
      SecuritySettingsForm::SETTINGS,
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state): array {
    $config = $this->config(self::SETTINGS);

    $form['remote_downloads'] = [
      '#type' => 'details',
      '#title' => $this->t('Remote File Downloads'),
      '#open' => TRUE,
    ];

    $form['remote_downloads']['enable_remote_downloads'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Enable remote file downloads'),
      '#description' => $this->t('Allow downloading files from remote URLs. Disable for maximum security.'),
      '#default_value' => $config->get('enable_remote_downloads') ?? TRUE,
    ];

    $form['remote_downloads']['max_file_size'] = [
      '#type' => 'number',
      '#title' => $this->t('Maximum file size (MB)'),
      '#description' => $this->t('Maximum file size allowed for remote downloads in megabytes.'),
      '#default_value' => $config->get('max_file_size') ?? 10,
      '#min' => 1,
      '#max' => 100,
      '#step' => 1,
      '#states' => [
        'visible' => [
          ':input[name="enable_remote_downloads"]' => ['checked' => TRUE],
        ],
      ],
    ];

    $form['remote_downloads']['download_timeout'] = [
      '#type' => 'number',
      '#title' => $this->t('Download timeout (seconds)'),
      '#description' => $this->t('Maximum time to wait for a remote file download.'),
      '#default_value' => $config->get('download_timeout') ?? 30,
      '#min' => 5,
      '#max' => 300,
      '#step' => 5,
      '#states' => [
        'visible' => [
          ':input[name="enable_remote_downloads"]' => ['checked' => TRUE],
        ],
      ],
    ];

    $form['allowed_domains'] = [
      '#type' => 'details',
      '#title' => $this->t('Allowed Domains'),
      '#open' => TRUE,
    ];

    $form['allowed_domains']['restrict_domains'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Restrict allowed domains'),
      '#description' => $this->t('Only allow downloads from specified domains. Leave unchecked to allow all domains (not recommended).'),
      '#default_value' => $config->get('restrict_domains') ?? FALSE,
    ];

    $form['allowed_domains']['allowed_domains_list'] = [
      '#type' => 'textarea',
      '#title' => $this->t('Allowed domains'),
      '#description' => $this->t('Enter one domain per line. Use wildcards for subdomains (e.g., *.example.com). Examples:<br/>example.com<br/>*.example.org<br/>cdn.mysite.com'),
      '#default_value' => $config->get('allowed_domains_list') ?? '',
      '#rows' => 8,
      '#states' => [
        'visible' => [
          ':input[name="restrict_domains"]' => ['checked' => TRUE],
        ],
        'required' => [
          ':input[name="restrict_domains"]' => ['checked' => TRUE],
        ],
      ],
    ];

    $form['file_types'] = [
      '#type' => 'details',
      '#title' => $this->t('File Type Restrictions'),
      '#open' => TRUE,
    ];

    $form['file_types']['allowed_extensions'] = [
      '#type' => 'textarea',
      '#title' => $this->t('Allowed file extensions'),
      '#description' => $this->t('Enter allowed file extensions, separated by spaces. Examples: jpg png gif pdf.'),
      '#default_value' => $this->getAllowedExtensions(),
      '#rows' => 4,
      '#required' => TRUE,
    ];
    $form['file_types']['allowed_extensions_description'] = [
      '#markup' => $this->t('The available file extensions for all media types are: <code>@extensions</code>', [
        '@extensions' => $this->arrayToString($this->swapperService->getAvailableExtensions()),
      ]),
    ];

    $form['security'] = [
      '#type' => 'details',
      '#title' => $this->t('Security Options'),
      '#open' => TRUE,
    ];

    $form['security']['disable_batch_processing'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Disable batch processing'),
      '#description' => $this->t('Completely disable the batch file-to-media swapper functionality. This will prevent all users from accessing the batch processing feature regardless of permissions.'),
      '#default_value' => $config->get('disable_batch_processing') ?? TRUE,
    ];

    $form['security']['block_private_ips'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Block private IP addresses'),
      '#description' => $this->t('Prevent downloads from private/internal IP addresses (recommended for security).'),
      '#default_value' => $config->get('block_private_ips') ?? TRUE,
    ];

    $form['security']['require_https'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Require HTTPS'),
      '#description' => $this->t('Only allow downloads from HTTPS URLs (recommended for security).'),
      '#default_value' => $config->get('require_https') ?? FALSE,
    ];

    $form['security']['max_redirects'] = [
      '#type' => 'number',
      '#title' => $this->t('Maximum redirects'),
      '#description' => $this->t('Maximum number of redirects to follow when downloading files.'),
      '#default_value' => $config->get('max_redirects') ?? 3,
      '#min' => 0,
      '#max' => 10,
      '#step' => 1,
    ];

    return parent::buildForm($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state): void {
    parent::validateForm($form, $form_state);

    // Validate allowed domains format.
    if ($form_state->getValue('restrict_domains')) {
      $domains = $form_state->getValue('allowed_domains_list');
      if (empty(trim($domains))) {
        $form_state->setErrorByName('allowed_domains_list', $this->t('You must specify at least one allowed domain when domain restriction is enabled.'));
      }
      else {
        $this->validateDomainsList($domains, $form_state);
      }
    }

    // Validate file extensions.
    $extensions = $form_state->getValue('allowed_extensions');
    if (!empty($extensions)) {
      $this->validateExtensionsList($extensions, $form_state);
    }

  }

  /**
   * Validates the domains list format.
   */
  private function validateDomainsList(string $domains, FormStateInterface $form_state): void {
    $domainLines = array_filter(array_map('trim', preg_split('/[\r\n]+/', $domains)));

    foreach ($domainLines as $line => $domain) {
      // Basic domain validation (allowing wildcards).
      if (!preg_match('/^(\*\.)?[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$/', $domain)) {
        $form_state->setErrorByName('allowed_domains_list', $this->t('Invalid domain format on line @line: @domain', [
          '@line' => $line + 1,
          '@domain' => $domain,
        ]));
      }
    }
  }

  /**
   * Validates the extensions list format.
   */
  private function validateExtensionsList(string $extensions, FormStateInterface $form_state): void {
    // Parse both comma-separated and line-separated formats.
    $extensionList = $this->parseListInput($extensions);

    foreach ($extensionList as $ext) {
      if (!preg_match('/^[a-zA-Z0-9]+$/', $ext)) {
        $form_state->setErrorByName('allowed_extensions', $this->t('Invalid file extension: @ext. Extensions should only contain letters and numbers.', [
          '@ext' => $ext,
        ]));
      }
    }
  }

  /**
   * Parses comma-separated or line-separated input.
   */
  private function parseListInput(string $input): array {
    return explode(' ', $input,);
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state): void {
    $config = $this->config(self::SETTINGS);

    // Remote download settings.
    $config->set('enable_remote_downloads', $form_state->getValue('enable_remote_downloads'));
    $config->set('max_file_size', $form_state->getValue('max_file_size'));
    $config->set('download_timeout', $form_state->getValue('download_timeout'));

    // Domain restrictions.
    $config->set('restrict_domains', $form_state->getValue('restrict_domains'));
    $config->set('allowed_domains_list', $form_state->getValue('allowed_domains_list'));

    // Parse and store domains as array.
    if ($form_state->getValue('restrict_domains')) {
      $domains = array_filter(array_map('trim', preg_split('/[\r\n]+/', $form_state->getValue('allowed_domains_list'))));
      $config->set('allowed_domains', $domains);
    }
    else {
      $config->set('allowed_domains', []);
    }

    // File type restrictions.
    $config->set('allowed_extensions', $this->parseListInput($form_state->getValue('allowed_extensions')));

    // Security options.
    $config->set('disable_batch_processing', $form_state->getValue('disable_batch_processing'));
    $config->set('block_private_ips', $form_state->getValue('block_private_ips'));
    $config->set('require_https', $form_state->getValue('require_https'));
    $config->set('max_redirects', $form_state->getValue('max_redirects'));

    $config->save();

    parent::submitForm($form, $form_state);
  }

  /**
   * Converts an associative array to textarea format.
   */
  private function arrayToString(array $array): string {
    return implode(' ', $array);
  }

  /**
   * Gets the allowed file extensions from configuration.
   *
   * @return string
   *   * Returns a string of allowed file extensions, from config or site logic.
   */
  private function getAllowedExtensions(): string {
    if (empty($extensions)) {
      $extensions = $this->swapperService->getAvailableExtensions();
    }
    return $this->arrayToString($extensions);
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc