Skip to content
Snippets Groups Projects
Commit eecdf907 authored by Adam G-H's avatar Adam G-H
Browse files

Issue #3377237 by phenaproxima, tedbow: For now, disallow unattended updates on Windows machines

parent 93e8392e
No related branches found
No related tags found
2 merge requests!989Issue #3356804 by phenaproxima: Flag a warning during status check if the...,!938Issue #3377237: For now, disallow unattended updates on Windows machines
......@@ -60,6 +60,9 @@ services:
tags:
- { name: event_subscriber }
Drupal\automatic_updates\Validator\VersionPolicyValidator: '@automatic_updates.validator.version_policy'
Drupal\automatic_updates\Validator\WindowsValidator:
tags:
- { name: event_subscriber }
logger.channel.automatic_updates:
parent: logger.channel_base
arguments: ['automatic_updates']
......
......@@ -92,8 +92,7 @@ class CronUpdateStage implements CronInterface, LoggerAwareInterface {
protected function runTerminalUpdateCommand(): void {
$command_path = $this->getCommandPath();
$php_binary_finder = new PhpExecutableFinder();
// @todo Check if on Windows to not allow cron updates in
// https://drupal.org/i/3377237.
// Use the `&` on the command line to detach this process after it is
// started. This will allow the command to outlive the web request.
$process = Process::fromShellCommandline($php_binary_finder->find() . " $command_path auto-update --is-from-web &")
......
<?php
declare(strict_types = 1);
namespace Drupal\automatic_updates\Validator;
use Drupal\automatic_updates\CronUpdateStage;
use Drupal\automatic_updates\DrushUpdateStage;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\package_manager\Event\PreCreateEvent;
use Drupal\package_manager\Event\PreOperationStageEvent;
use Drupal\package_manager\Event\StatusCheckEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Disallows unattended background updates on Windows systems.
*/
final class WindowsValidator implements EventSubscriberInterface {
use StringTranslationTrait;
/**
* The value of the PHP_OS constant.
*
* @var string
*/
private static $os = PHP_OS;
/**
* Constructs a WindowsValidator object.
*
* @param \Drupal\automatic_updates\CronUpdateStage $cronRunner
* The cron update runner service.
* @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
* The config factory service.
*/
public function __construct(
private readonly CronUpdateStage $cronRunner,
private readonly ConfigFactoryInterface $configFactory,
) {}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array {
return [
StatusCheckEvent::class => 'validate',
PreCreateEvent::class => 'validate',
];
}
/**
* Disallows unattended updates if running on Windows.
*
* @param \Drupal\package_manager\Event\PreOperationStageEvent $event
* The event being handled.
*/
public function validate(PreOperationStageEvent $event): void {
// If we're not on Windows, there's nothing for us to validate.
if (!str_starts_with(strtoupper(static::$os), 'WIN')) {
return;
}
$method = $this->configFactory->get('automatic_updates.settings')
->get('unattended.method');
$stage = $event->stage;
if ($stage instanceof DrushUpdateStage && $this->cronRunner->getMode() !== CronUpdateStage::DISABLED && $method === 'web') {
$message = $this->t('Unattended updates are not supported on Windows.');
$form_url = Url::fromRoute('update.report_update');
if ($form_url->access()) {
$message = $this->t('@message Use <a href=":form-url">the update form</a> to update Drupal core.', [
'@message' => $message,
':form-url' => $form_url->toString(),
]);
}
$event->addError([$message]);
}
}
}
<?php
declare(strict_types = 1);
namespace Drupal\Tests\automatic_updates\Kernel\StatusCheck;
use ColinODell\PsrTestLogger\TestLogger;
use Drupal\automatic_updates\CronUpdateStage;
use Drupal\automatic_updates\Validator\WindowsValidator;
use Drupal\Core\Logger\RfcLogLevel;
use Drupal\package_manager\ValidationResult;
use Drupal\Tests\automatic_updates\Kernel\AutomaticUpdatesKernelTestBase;
use Drupal\Tests\user\Traits\UserCreationTrait;
/**
* @covers \Drupal\automatic_updates\Validator\WindowsValidator
* @group automatic_updates
* @internal
*/
class WindowsValidatorTest extends AutomaticUpdatesKernelTestBase {
use UserCreationTrait;
/**
* {@inheritdoc}
*/
protected static $modules = ['automatic_updates', 'user'];
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
$this->installConfig('automatic_updates');
}
/**
* Data provider for ::testBackgroundUpdatesDisallowedOnWindows().
*
* @return array[]
* The test cases.
*/
public function providerBackgroundUpdatesDisallowedOnWindows(): array {
return [
'updates enabled via web, user has access to update form' => [
['administer software updates'],
[
'method' => 'web',
'level' => CronUpdateStage::ALL,
],
[
ValidationResult::createError([
t('Unattended updates are not supported on Windows. Use <a href="/admin/reports/updates/update">the update form</a> to update Drupal core.'),
]),
],
],
'updates enabled via web, user cannot access update form' => [
[],
[
'method' => 'web',
'level' => CronUpdateStage::ALL,
],
[
ValidationResult::createError([
t('Unattended updates are not supported on Windows.'),
]),
],
],
'updates enabled via console, user has access to update form' => [
['administer software updates'],
[
'method' => 'console',
'level' => CronUpdateStage::ALL,
],
[],
],
'updates enabled via console, user cannot access update form' => [
[],
[
'method' => 'console',
'level' => CronUpdateStage::ALL,
],
[],
],
'updates disabled, user has access to update form' => [
['administer software updates'],
[
'method' => 'web',
'level' => CronUpdateStage::DISABLED,
],
[],
],
'updates disabled, user cannot access update form' => [
[],
[
'method' => 'web',
'level' => CronUpdateStage::DISABLED,
],
[],
],
];
}
/**
* Tests that background updates are not allowed on Windows.
*
* @param array $user_permissions
* The permissions the current user should have, if any.
* @param array $unattended_update_settings
* The `automatic_updates.settings:unattended` config values.
* @param \Drupal\package_manager\ValidationResult[] $expected_results
* The expected validation results on Windows.
*
* @dataProvider providerBackgroundUpdatesDisallowedOnWindows
*/
public function testBackgroundUpdatesDisallowedOnWindows(array $user_permissions, array $unattended_update_settings, array $expected_results): void {
if ($user_permissions) {
$this->setUpCurrentUser([], $user_permissions, FALSE);
}
$this->config('automatic_updates.settings')
->set('unattended', $unattended_update_settings)
->save();
$property = new \ReflectionProperty(WindowsValidator::class, 'os');
$property->setValue(NULL, 'Windows');
$this->assertCheckerResultsFromManager($expected_results, TRUE);
$logger = new TestLogger();
$this->container->get('logger.factory')
->get('automatic_updates')
->addLogger($logger);
// If any validation errors are expected, they should be logged if we try to
// run an unattended update.
$this->runConsoleUpdateStage();
foreach ($expected_results as $result) {
foreach ($result->messages as $message) {
$this->assertTrue($logger->hasRecordThatContains((string) $message, RfcLogLevel::ERROR));
}
}
// If we're not on Windows, we should never get an error.
$property->setValue(NULL, 'Linux');
$this->assertCheckerResultsFromManager([], TRUE);
// If unattended updates are enabled, ensure that they will succeed.
if ($unattended_update_settings['level'] !== CronUpdateStage::DISABLED) {
$logger->reset();
$this->getStageFixtureManipulator()->setCorePackageVersion('9.8.1');
$this->runConsoleUpdateStage();
$this->assertFalse($logger->hasRecords(RfcLogLevel::ERROR));
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment