From 81f055eb1cabb12c36f99c96675b1b9991d2d168 Mon Sep 17 00:00:00 2001
From: Alex Pott <alex.a.pott@googlemail.com>
Date: Tue, 8 Oct 2024 14:50:21 +0100
Subject: [PATCH] Issue #3477329 by phenaproxima, thejimbirch, b_sharpe: Recipe
 validation should always treat required modules as installed

---
 core/lib/Drupal/Core/Recipe/Recipe.php         | 11 ++++++++++-
 .../KernelTests/Core/Recipe/RecipeTest.php     | 18 ++++++++++++++++++
 2 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/core/lib/Drupal/Core/Recipe/Recipe.php b/core/lib/Drupal/Core/Recipe/Recipe.php
index 89e7b0ed742d..c9f5f055932c 100644
--- a/core/lib/Drupal/Core/Recipe/Recipe.php
+++ b/core/lib/Drupal/Core/Recipe/Recipe.php
@@ -383,14 +383,23 @@ private static function validateConfigActions(mixed $value, ExecutionContextInte
 
     $configurator = new RecipeConfigurator($recipe_being_validated['recipes'] ?? [], $include_path);
 
+    /** @var \Drupal\Core\Extension\ModuleExtensionList $module_list */
+    $module_list = \Drupal::service('extension.list.module');
     // The config provider must either be an already-installed module or theme,
     // or an extension being installed by this recipe or a recipe it depends on.
     $all_extensions = [
-      ...array_keys(\Drupal::service('extension.list.module')->getAllInstalledInfo()),
+      ...array_keys($module_list->getAllInstalledInfo()),
       ...array_keys(\Drupal::service('extension.list.theme')->getAllInstalledInfo()),
       ...$recipe_being_validated['install'] ?? [],
       ...$configurator->listAllExtensions(),
     ];
+    // Explicitly treat required modules as installed, even if Drupal isn't
+    // installed yet, because we know they WILL be installed.
+    foreach ($module_list->getAllAvailableInfo() as $name => $info) {
+      if (!empty($info['required'])) {
+        $all_extensions[] = $name;
+      }
+    }
 
     if (!in_array($config_provider, $all_extensions, TRUE)) {
       $context->addViolation('Config actions cannot be applied to %config_name because the %config_provider extension is not installed, and is not installed by this recipe or any of the recipes it depends on.', [
diff --git a/core/tests/Drupal/KernelTests/Core/Recipe/RecipeTest.php b/core/tests/Drupal/KernelTests/Core/Recipe/RecipeTest.php
index 9db4abe5709c..b63861276ff3 100644
--- a/core/tests/Drupal/KernelTests/Core/Recipe/RecipeTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Recipe/RecipeTest.php
@@ -8,6 +8,7 @@
 use Drupal\Core\Recipe\RecipeFileException;
 use Drupal\Core\Recipe\RecipePreExistingConfigException;
 use Drupal\Core\Recipe\RecipeRunner;
+use Drupal\FunctionalTests\Core\Recipe\RecipeTestTrait;
 use Drupal\KernelTests\KernelTestBase;
 
 /**
@@ -16,6 +17,8 @@
  */
 class RecipeTest extends KernelTestBase {
 
+  use RecipeTestTrait;
+
   /**
    * {@inheritdoc}
    */
@@ -80,4 +83,19 @@ public function testExampleRecipe(): void {
     $this->assertSame($this->config('text.settings')->get('default_summary_length'), 700);
   }
 
+  public function testImplicitlyRequiredModule(): void {
+    $this->disableModules(['user']);
+    $recipe = $this->createRecipe([
+      'name' => 'Actions on config from required module',
+      'config' => [
+        'actions' => [
+          'user.role.authenticated' => [
+            'grantPermission' => 'access administration pages',
+          ],
+        ],
+      ],
+    ]);
+    $this->assertIsObject($recipe);
+  }
+
 }
-- 
GitLab