diff --git a/src/Controller/ExperienceBuilderController.php b/src/Controller/ExperienceBuilderController.php
index 94dfed3125141ee3c2256b1c01377ca7e5901be8..a9935755bd3fae29a0ef5fa07777e316db9b1f04 100644
--- a/src/Controller/ExperienceBuilderController.php
+++ b/src/Controller/ExperienceBuilderController.php
@@ -13,10 +13,14 @@ use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Field\WidgetPluginManager;
 use Drupal\Core\Render\HtmlResponse;
 use Drupal\Core\Render\RendererInterface;
+use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Template\Attribute;
 use Drupal\Core\Theme\ThemeInitializationInterface;
 use Drupal\Core\Theme\ThemeManagerInterface;
 use Drupal\experience_builder\AssetRenderer;
+use Drupal\experience_builder\Entity\JavaScriptComponent;
+use Drupal\experience_builder\Entity\PageRegion;
+use Drupal\experience_builder\Entity\Pattern;
 use Symfony\Component\DependencyInjection\Attribute\Autowire;
 
 final class ExperienceBuilderController {
@@ -31,6 +35,7 @@ final class ExperienceBuilderController {
     private readonly LibraryDiscoveryInterface $libraryDiscovery,
     private readonly RendererInterface $renderer,
     private readonly ThemeInitializationInterface $themeInitialization,
+    private readonly AccountInterface $currentUser,
   ) {}
 
   private const HTML = <<<HTML
@@ -110,6 +115,11 @@ HTML;
             'jsFooter' => $this->assetRenderer->renderJsFooterAssets($preview_assets),
           ],
           'xbModulePath' => $xb_module_path,
+          'permissions' => [
+            'globalRegions' => $this->currentUser->hasPermission(PageRegion::ADMIN_PERMISSION),
+            'sections' => $this->currentUser->hasPermission(Pattern::ADMIN_PERMISSION),
+            'codeComponents' => $this->currentUser->hasPermission(JavaScriptComponent::ADMIN_PERMISSION),
+          ],
         ],
       ],
       // Note: the tokens here are under our control, and this accepts no user
diff --git a/src/Entity/JavaScriptComponent.php b/src/Entity/JavaScriptComponent.php
index 74c8399d8516022badb2ab816be6db6ecd55cc54..be41cd3e00ba9fcda0cc3ab2bc64a6ab98d964ed 100644
--- a/src/Entity/JavaScriptComponent.php
+++ b/src/Entity/JavaScriptComponent.php
@@ -18,7 +18,7 @@ use Drupal\experience_builder\ClientSideRepresentation;
  *   label_singular = @Translation("code component"),
  *   label_plural = @Translation("code components"),
  *   label_collection = @Translation("Code components"),
- *   admin_permission = "administer code components",
+ *   admin_permission = \Drupal\experience_builder\Entity\JavaScriptComponent::ADMIN_PERMISSION,
  *   handlers = {
  *     "storage" = \Drupal\experience_builder\EntityHandlers\JavascriptComponentStorage::class,
  *     "access" = \Drupal\experience_builder\EntityHandlers\XbConfigEntityAccessControlHandler::class,
@@ -49,6 +49,7 @@ final class JavaScriptComponent extends ConfigEntityBase implements XbAssetInter
   use XbAssetLibraryTrait;
 
   public const string ENTITY_TYPE_ID = 'js_component';
+  public const ADMIN_PERMISSION = 'administer code components';
   private const string ASSETS_DIRECTORY = 'assets://astro-island/';
 
   /**
diff --git a/src/Entity/PageRegion.php b/src/Entity/PageRegion.php
index c5eeef1b91e34eb54d8d96c1e393723caf1deeb2..11665ece6933abe702a263993bb488b7bdaaf31b 100644
--- a/src/Entity/PageRegion.php
+++ b/src/Entity/PageRegion.php
@@ -23,7 +23,7 @@ use Drupal\experience_builder\Plugin\Field\FieldType\ComponentTreeItemInstantiat
  *    label_singular = @Translation("page region"),
  *    label_plural = @Translation("page region"),
  *    label_collection = @Translation("Page region"),
- *    admin_permission = "administer page template",
+ *    admin_permission = \Drupal\experience_builder\Entity\PageRegion::ADMIN_PERMISSION,
  *    handlers = {
  *      "access" = \Drupal\experience_builder\EntityHandlers\XbConfigEntityAccessControlHandler::class
  *    },
@@ -45,6 +45,7 @@ use Drupal\experience_builder\Plugin\Field\FieldType\ComponentTreeItemInstantiat
 final class PageRegion extends ConfigEntityBase implements ComponentTreeEntityInterface {
 
   public const PLUGIN_ID = 'page_region';
+  public const ADMIN_PERMISSION = 'administer page template';
   use ComponentTreeItemInstantiatorTrait;
   use ClientServerConversionTrait;
 
diff --git a/src/Entity/Pattern.php b/src/Entity/Pattern.php
index b2ce7b99610c0679b195b862463c4847b16676e7..eeebe2d957b8c8f5cf90f174e96093926915dc48 100644
--- a/src/Entity/Pattern.php
+++ b/src/Entity/Pattern.php
@@ -17,12 +17,12 @@ use Drupal\experience_builder\Plugin\Field\FieldType\ComponentTreeItemInstantiat
 
 /**
  * @ConfigEntityType(
- *    id = "pattern",
+ *    id = \Drupal\experience_builder\Entity\Pattern::PLUGIN_ID,
  *    label = @Translation("Pattern"),
  *    label_singular = @Translation("pattern"),
  *    label_plural = @Translation("patterns"),
  *    label_collection = @Translation("Patterns"),
- *    admin_permission = "administer patterns",
+ *    admin_permission = \Drupal\experience_builder\Entity\Pattern::ADMIN_PERMISSION,
  *    handlers = {
  *      "access" = \Drupal\experience_builder\EntityHandlers\ContentCreatorVisibleXbConfigEntityAccessControlHandler::class
  *    },
@@ -39,6 +39,9 @@ use Drupal\experience_builder\Plugin\Field\FieldType\ComponentTreeItemInstantiat
  */
 final class Pattern extends ConfigEntityBase implements XbHttpApiEligibleConfigEntityInterface, ComponentTreeEntityInterface {
 
+  public const PLUGIN_ID = 'pattern';
+  public const ADMIN_PERMISSION = 'administer patterns';
+
   use ComponentTreeItemInstantiatorTrait;
   use ClientServerConversionTrait;
 
diff --git a/tests/src/Kernel/Controller/ExperienceBuilderControllerTest.php b/tests/src/Kernel/Controller/ExperienceBuilderControllerTest.php
index 33211b2ca208459eb4af4bd6b17c01351012f438..7ac0d508812e6fdc884433b66d8d716c296b6b13 100644
--- a/tests/src/Kernel/Controller/ExperienceBuilderControllerTest.php
+++ b/tests/src/Kernel/Controller/ExperienceBuilderControllerTest.php
@@ -4,7 +4,11 @@ declare(strict_types=1);
 
 namespace Drupal\Tests\experience_builder\Kernel\Controller;
 
+use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\Url;
+use Drupal\experience_builder\Entity\JavaScriptComponent;
+use Drupal\experience_builder\Entity\PageRegion;
+use Drupal\experience_builder\Entity\Pattern;
 use Drupal\KernelTests\KernelTestBase;
 use Drupal\Tests\experience_builder\Kernel\Traits\PageTrait;
 use Drupal\Tests\experience_builder\Kernel\Traits\RequestTrait;
@@ -41,6 +45,12 @@ final class ExperienceBuilderControllerTest extends KernelTestBase {
     $this->installEntitySchema('path_alias');
   }
 
+  public function register(ContainerBuilder $container): void {
+    // Enable debug_cacheability_headers, so we can test cacheability.
+    $container->setParameter('http.response.debug_cacheability_headers', TRUE);
+    parent::register($container);
+  }
+
   /**
    * Tests controller output when adding or editing an entity.
    *
@@ -75,7 +85,18 @@ final class ExperienceBuilderControllerTest extends KernelTestBase {
       'entity' => $sut->id(),
     ])->toString();
     self::assertEquals("/xb/$entity_type/{$sut->id()}", $edit_url);
-    $this->request(Request::create($edit_url));
+
+    /** @var \Drupal\Core\Render\HtmlResponse $response */
+    $response = $this->request(Request::create($edit_url));
+
+    self::assertSame([
+      'user.permissions',
+      'languages:language_interface',
+      'theme',
+    ], $response->getCacheableMetadata()->getCacheContexts());
+    self::assertSame([
+      'http_response'
+    ], $response->getCacheableMetadata()->getCacheTags());
 
     $this->assertExperienceBuilderMount($entity_type, $sut);
   }
@@ -101,4 +122,80 @@ final class ExperienceBuilderControllerTest extends KernelTestBase {
     ];
   }
 
+  /**
+   * Tests controller exposed permissions.
+   *
+   * @param array $permissions
+   *   The permissions.
+   * @param array $expectedPermissionFlags
+   *   The expected flags.
+   *
+   * @dataProvider permissionsData
+   */
+  public function testControllerExposedPermissions(array $permissions, array $expectedPermissionFlags): void {
+    $this->installEntitySchema('xb_page');
+
+    $this->setUpCurrentUser([], $permissions);
+
+    $add_url = Url::fromRoute('experience_builder.experience_builder', [
+      'entity_type' => 'xb_page',
+      'entity' => '',
+    ])->toString();
+    self::assertEquals("/xb/xb_page", $add_url);
+    $this->request(Request::create($add_url));
+
+    $this->assertEqualsCanonicalizing($expectedPermissionFlags, $this->drupalSettings['xb']['permissions']);
+  }
+
+  public static function permissionsData(): array {
+    return [
+      [
+        [
+          'administer xb_page',
+        ],
+        [
+          'globalRegion' => FALSE,
+          'sections' => FALSE,
+          'codeComponents' => FALSE,
+        ],
+      ],
+      [
+        [
+          'administer xb_page',
+          JavaScriptComponent::ADMIN_PERMISSION,
+        ],
+        [
+          'globalRegion' => FALSE,
+          'sections' => FALSE,
+          'codeComponents' => TRUE,
+        ],
+      ],
+      [
+        [
+          'administer xb_page',
+          Pattern::ADMIN_PERMISSION,
+          PageRegion::ADMIN_PERMISSION,
+        ],
+        [
+          'globalRegion' => TRUE,
+          'sections' => TRUE,
+          'codeComponents' => FALSE,
+        ],
+      ],
+      [
+        [
+          'administer xb_page',
+          Pattern::ADMIN_PERMISSION,
+          PageRegion::ADMIN_PERMISSION,
+          JavaScriptComponent::ADMIN_PERMISSION,
+        ],
+        [
+          'globalRegion' => TRUE,
+          'sections' => TRUE,
+          'codeComponents' => TRUE,
+        ],
+      ],
+    ];
+  }
+
 }