PhpTufValidator.php
Namespace
Drupal\package_manager\ValidatorFile
-
core/
modules/ package_manager/ src/ Validator/ PhpTufValidator.php
View source
<?php
declare (strict_types=1);
namespace Drupal\package_manager\Validator;
use Drupal\Component\Assertion\Inspector;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Site\Settings;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\package_manager\ComposerInspector;
use Drupal\package_manager\Event\PreApplyEvent;
use Drupal\package_manager\Event\PreCreateEvent;
use Drupal\package_manager\Event\PreOperationStageEvent;
use Drupal\package_manager\Event\PreRequireEvent;
use Drupal\package_manager\Event\StatusCheckEvent;
use Drupal\package_manager\PathLocator;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Validates that PHP-TUF is installed and correctly configured.
*
* In both the active and stage directories, this checks for the following
* conditions:
* - The PHP-TUF plugin is installed.
* - The plugin is not explicitly blocked by Composer's `allow-plugins`
* configuration.
* - Composer is aware of at least one repository that has TUF support
* explicitly enabled.
*
* Until it's more real world-tested, TUF protection is bypassed by default.
* Ultimately, though, Package Manager will not treat TUF as optional.
*
* @internal
* This is an internal part of Package Manager and may be changed or removed
* at any time without warning. External code should not interact with this
* class.
*/
final class PhpTufValidator implements EventSubscriberInterface {
use StringTranslationTrait;
/**
* The name of the PHP-TUF Composer integration plugin.
*
* @var string
*/
public const PLUGIN_NAME = 'php-tuf/composer-integration';
public function __construct(PathLocator $pathLocator, ComposerInspector $composerInspector, ModuleHandlerInterface $moduleHandler, Settings $settings, array $repositories) {
assert(Inspector::assertAllStrings($repositories));
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() : array {
return [
StatusCheckEvent::class => 'validate',
PreCreateEvent::class => 'validate',
PreRequireEvent::class => 'validate',
PreApplyEvent::class => 'validate',
];
}
/**
* Reacts to a stage event by validating PHP-TUF configuration as needed.
*
* @param \Drupal\package_manager\Event\PreOperationStageEvent $event
* The event object.
*/
public function validate(PreOperationStageEvent $event) : void {
$messages = $this->validateTuf($this->pathLocator
->getProjectRoot());
if ($messages) {
$event->addError($messages, $this->t('The active directory is not protected by PHP-TUF, which is required to use Package Manager securely.'));
}
$stage = $event->stage;
if ($stage->stageDirectoryExists()) {
$messages = $this->validateTuf($stage->getStageDirectory());
if ($messages) {
$event->addError($messages, $this->t('The stage directory is not protected by PHP-TUF, which is required to use Package Manager securely.'));
}
}
}
/**
* Flags messages if PHP-TUF is not installed and configured properly.
*
* @param string $dir
* The directory to examine.
*
* @return \Drupal\Core\StringTranslation\TranslatableMarkup[]
* The error messages, if any.
*/
private function validateTuf(string $dir) : array {
$messages = [];
// This setting will be removed without warning when no longer need.
if ($this->settings
->get('package_manager_bypass_tuf', TRUE)) {
return $messages;
}
if ($this->moduleHandler
->moduleExists('help')) {
$help_url = Url::fromRoute('help.page', [
'name' => 'package_manager',
])->setOption('fragment', 'package-manager-tuf-info')
->toString();
}
// The Composer plugin must be installed.
$installed_packages = $this->composerInspector
->getInstalledPackagesList($dir);
if (!isset($installed_packages[static::PLUGIN_NAME])) {
$message = $this->t('The <code>@plugin</code> plugin is not installed.', [
'@plugin' => static::PLUGIN_NAME,
]);
if (isset($help_url)) {
$message = $this->t('@message See <a href=":url">the help page</a> for more information on how to install the plugin.', [
'@message' => $message,
':url' => $help_url,
]);
}
$messages[] = $message;
}
// And it has to be explicitly enabled.
$allowed_plugins = $this->composerInspector
->getAllowPluginsConfig($dir);
if ($allowed_plugins !== TRUE && empty($allowed_plugins[static::PLUGIN_NAME])) {
$message = $this->t('The <code>@plugin</code> plugin is not listed as an allowed plugin.', [
'@plugin' => static::PLUGIN_NAME,
]);
if (isset($help_url)) {
$message = $this->t('@message See <a href=":url">the help page</a> for more information on how to configure the plugin.', [
'@message' => $message,
':url' => $help_url,
]);
}
$messages[] = $message;
}
// Confirm that all repositories we're configured to look at have opted into
// TUF protection.
foreach ($this->getRepositoryStatus($dir) as $url => $is_protected) {
if ($is_protected) {
continue;
}
$message = $this->t('TUF is not enabled for the <code>@url</code> repository.', [
'@url' => $url,
]);
if (isset($help_url)) {
$message = $this->t('@message See <a href=":url">the help page</a> for more information on how to set up this repository.', [
'@message' => $message,
':url' => $help_url,
]);
}
$messages[] = $message;
}
return $messages;
}
/**
* Gets the TUF protection status of Composer repositories.
*
* @param string $dir
* The directory in which to run Composer.
*
* @return bool[]
* An array of booleans, keyed by repository URL, indicating whether TUF
* protection is enabled for that repository.
*/
private function getRepositoryStatus(string $dir) : array {
$status = [];
$repositories = $this->composerInspector
->getConfig('repositories', $dir);
$repositories = Json::decode($repositories);
foreach ($repositories as $repository) {
// Only Composer repositories can have TUF protection.
if ($repository['type'] === 'composer') {
$url = $repository['url'];
$status[$url] = !empty($repository['tuf']);
}
}
return array_intersect_key($status, array_flip($this->repositories));
}
}
Classes
Title | Deprecated | Summary |
---|---|---|
PhpTufValidator | Validates that PHP-TUF is installed and correctly configured. |
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.