diff --git a/2943304-2.patch b/2943304-2.patch
new file mode 100644
index 0000000..0c4bcb1
--- /dev/null
+++ b/2943304-2.patch
@@ -0,0 +1,198 @@
+diff --git a/deploy.module b/deploy.module
+index b743e7b..f4c4f28 100644
+--- a/deploy.module
++++ b/deploy.module
+@@ -11,36 +11,32 @@ use Drupal\Core\Url;
+  * Implements hook_toolbar().
+  */
+ function deploy_toolbar() {
+-  $items = [];
+-
+   $user = \Drupal::currentUser();
+-  $deploy_access = $user->hasPermission('administer deployments');
+-  if ($deploy_access) {
+-    $items['deploy'] = [
+-      '#type' => 'toolbar_item',
+-      'tab' => [
+-        '#type' => 'link',
+-        '#title' => t('Deploy'),
+-        '#url' => Url::fromRoute('entity.replication.add_form'),
+-        '#attributes' => [
+-          'title' => t('Deploy'),
+-          'class' => ['toolbar-icon', 'toolbar-icon-deploy', 'use-ajax'],
+-          'data-dialog-type' => 'modal',
+-          'data-dialog-options' => json_encode([
+-            'width' => '50%',
+-          ]),
+-        ],
+-      ],
+-      '#wrapper_attributes' => [
+-        'class' => ['deploy-toolbar-tab'],
++  $items['deploy'] = [
++    '#type' => 'toolbar_item',
++    '#access' => \Drupal::entityTypeManager()->getAccessControlHandler('replication')->createAccess('', $user),
++    'tab' => [
++      '#type' => 'link',
++      '#title' => t('Deploy'),
++      '#url' => Url::fromRoute('entity.replication.add_form'),
++      '#attributes' => [
++        'title' => t('Deploy'),
++        'class' => ['toolbar-icon', 'toolbar-icon-deploy', 'use-ajax'],
++        'data-dialog-type' => 'modal',
++        'data-dialog-options' => json_encode([
++          'width' => '50%',
++        ]),
+       ],
+-      '#attached' => [
+-        'library' => [
+-          'deploy/drupal.deploy.toolbar',
+-        ],
++    ],
++    '#wrapper_attributes' => [
++      'class' => ['deploy-toolbar-tab'],
++    ],
++    '#attached' => [
++      'library' => [
++        'deploy/drupal.deploy.toolbar',
+       ],
+-    ];
+-  }
++    ],
++  ];
+ 
+   return $items;
+ }
+diff --git a/deploy.permissions.yml b/deploy.permissions.yml
+index 19eac0c..fd4d44b 100644
+--- a/deploy.permissions.yml
++++ b/deploy.permissions.yml
+@@ -1,2 +1,5 @@
+ administer deployments:
+   title: 'Administer deployments'
++
++permission_callbacks:
++  - \Drupal\deploy\DeployPermissions::workspacePointerPermissions
+diff --git a/deploy.routing.yml b/deploy.routing.yml
+index 4f82720..777e974 100644
+--- a/deploy.routing.yml
++++ b/deploy.routing.yml
+@@ -15,6 +15,6 @@ entity.replication.add_form:
+     _entity_form: replication.add
+     _title_callback: '\Drupal\deploy\Entity\Form\ReplicationForm::addTitle'
+   requirements:
+-    _permission: 'administer deployments'
++    _entity_create_access: 'replication'
+   options:
+     _admin_route: TRUE
+diff --git a/src/DeployPermissions.php b/src/DeployPermissions.php
+new file mode 100644
+index 0000000..f2dce69
+--- /dev/null
++++ b/src/DeployPermissions.php
+@@ -0,0 +1,31 @@
++<?php
++
++namespace Drupal\deploy;
++
++use Drupal\Core\StringTranslation\StringTranslationTrait;
++use Drupal\workspace\Entity\WorkspacePointer;
++
++/**
++ * Defines permissions for Deploy module.
++ */
++class DeployPermissions {
++  use StringTranslationTrait;
++
++  /**
++   * Generates the permissions for workspace pointer entities.
++   *
++   * @return array
++   *   The permissions for workspace pointer entities.
++   */
++  public function workspacePointerPermissions() {
++    $permissions = [];
++    $workspace_pointers = WorkspacePointer::loadMultiple();
++    foreach ($workspace_pointers as $workspace_pointer) {
++      $permissions['Deploy to ' . $workspace_pointer->label()] = [
++        'title' => $this->t('Deploy to @workspace_pointer', ['@workspace_pointer' => $workspace_pointer->label()]),
++      ];
++    }
++    return $permissions;
++  }
++
++}
+diff --git a/src/ReplicationAccessControlHandler.php b/src/ReplicationAccessControlHandler.php
+index 33209b8..273ae50 100644
+--- a/src/ReplicationAccessControlHandler.php
++++ b/src/ReplicationAccessControlHandler.php
+@@ -4,20 +4,53 @@ namespace Drupal\deploy;
+ 
+ use Drupal\Core\Access\AccessResult;
+ use Drupal\Core\Entity\EntityAccessControlHandler;
++use Drupal\Core\Entity\EntityHandlerInterface;
++use Drupal\Core\Entity\EntityTypeInterface;
+ use Drupal\Core\Field\FieldDefinitionInterface;
+ use Drupal\Core\Field\FieldItemListInterface;
+ use Drupal\Core\Session\AccountInterface;
++use Drupal\multiversion\Workspace\WorkspaceManagerInterface;
++use Symfony\Component\DependencyInjection\ContainerInterface;
+ 
+ /**
+  * ReplicationAccessControlHandler class.
+  */
+-class ReplicationAccessControlHandler extends EntityAccessControlHandler {
++class ReplicationAccessControlHandler extends EntityAccessControlHandler implements EntityHandlerInterface {
++
++  /**
++   * The workspace manager service.
++   *
++   * @var \Drupal\multiversion\Workspace\WorkspaceManagerInterface
++   */
++  protected $workspaceManager;
++
++  /**
++   * Constructs a NodeAccessControlHandler object.
++   *
++   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
++   *   The entity type definition.
++   * @param \Drupal\multiversion\Workspace\WorkspaceManagerInterface $workspace_manager
++   *   The workspace manager service.
++   */
++  public function __construct(EntityTypeInterface $entity_type, WorkspaceManagerInterface $workspace_manager) {
++    parent::__construct($entity_type);
++    $this->workspaceManager = $workspace_manager;
++  }
+ 
+   /**
+    * {@inheritdoc}
+    */
+-  protected function checkFieldAccess($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
++  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
++    return new static(
++      $entity_type,
++      $container->get('workspace.manager')
++    );
++  }
+ 
++  /**
++   * {@inheritdoc}
++   */
++  protected function checkFieldAccess($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
+     $restricted_fields = ['source', 'target'];
+     if (in_array($field_definition->getName(), $restricted_fields)) {
+       return AccessResult::forbidden();
+@@ -26,4 +59,14 @@ class ReplicationAccessControlHandler extends EntityAccessControlHandler {
+     return parent::checkFieldAccess($operation, $field_definition, $account, $items);
+   }
+ 
++  protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
++    $access = parent::checkCreateAccess($account, $context, $entity_bundle);
++    $active_workspace = $this->workspaceManager->getActiveWorkspace();
++    $upstream_workspace_pointer = $active_workspace->upstream->entity;
++    if (!$account->hasPermission('Deploy to ' . $upstream_workspace_pointer->label()) ) {
++      $access->isForbidden();
++    }
++    return $access;
++  }
++
+ }
diff --git a/deploy.module b/deploy.module
index b743e7b..93ddaa2 100644
--- a/deploy.module
+++ b/deploy.module
@@ -11,36 +11,32 @@ use Drupal\Core\Url;
  * Implements hook_toolbar().
  */
 function deploy_toolbar() {
-  $items = [];
-
   $user = \Drupal::currentUser();
-  $deploy_access = $user->hasPermission('administer deployments');
-  if ($deploy_access) {
-    $items['deploy'] = [
-      '#type' => 'toolbar_item',
-      'tab' => [
-        '#type' => 'link',
-        '#title' => t('Deploy'),
-        '#url' => Url::fromRoute('entity.replication.add_form'),
-        '#attributes' => [
-          'title' => t('Deploy'),
-          'class' => ['toolbar-icon', 'toolbar-icon-deploy', 'use-ajax'],
-          'data-dialog-type' => 'modal',
-          'data-dialog-options' => json_encode([
-            'width' => '50%',
-          ]),
-        ],
-      ],
-      '#wrapper_attributes' => [
-        'class' => ['deploy-toolbar-tab'],
+  $items['deploy'] = [
+    '#type' => 'toolbar_item',
+    'tab' => [
+      '#type' => 'link',
+      '#title' => t('Deploy'),
+      '#url' => Url::fromRoute('entity.replication.add_form'),
+      '#access' => \Drupal::entityTypeManager()->getAccessControlHandler('replication')->createAccess('', $user),
+      '#attributes' => [
+        'title' => t('Deploy'),
+        'class' => ['toolbar-icon', 'toolbar-icon-deploy', 'use-ajax'],
+        'data-dialog-type' => 'modal',
+        'data-dialog-options' => json_encode([
+          'width' => '50%',
+        ]),
       ],
-      '#attached' => [
-        'library' => [
-          'deploy/drupal.deploy.toolbar',
-        ],
+    ],
+    '#wrapper_attributes' => [
+      'class' => ['deploy-toolbar-tab'],
+    ],
+    '#attached' => [
+      'library' => [
+        'deploy/drupal.deploy.toolbar',
       ],
-    ];
-  }
+    ],
+  ];
 
   return $items;
 }
diff --git a/deploy.permissions.yml b/deploy.permissions.yml
index 19eac0c..fd4d44b 100644
--- a/deploy.permissions.yml
+++ b/deploy.permissions.yml
@@ -1,2 +1,5 @@
 administer deployments:
   title: 'Administer deployments'
+
+permission_callbacks:
+  - \Drupal\deploy\DeployPermissions::workspacePointerPermissions
diff --git a/deploy.routing.yml b/deploy.routing.yml
index 4f82720..777e974 100644
--- a/deploy.routing.yml
+++ b/deploy.routing.yml
@@ -15,6 +15,6 @@ entity.replication.add_form:
     _entity_form: replication.add
     _title_callback: '\Drupal\deploy\Entity\Form\ReplicationForm::addTitle'
   requirements:
-    _permission: 'administer deployments'
+    _entity_create_access: 'replication'
   options:
     _admin_route: TRUE
diff --git a/src/DeployPermissions.php b/src/DeployPermissions.php
new file mode 100644
index 0000000..f2dce69
--- /dev/null
+++ b/src/DeployPermissions.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace Drupal\deploy;
+
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\workspace\Entity\WorkspacePointer;
+
+/**
+ * Defines permissions for Deploy module.
+ */
+class DeployPermissions {
+  use StringTranslationTrait;
+
+  /**
+   * Generates the permissions for workspace pointer entities.
+   *
+   * @return array
+   *   The permissions for workspace pointer entities.
+   */
+  public function workspacePointerPermissions() {
+    $permissions = [];
+    $workspace_pointers = WorkspacePointer::loadMultiple();
+    foreach ($workspace_pointers as $workspace_pointer) {
+      $permissions['Deploy to ' . $workspace_pointer->label()] = [
+        'title' => $this->t('Deploy to @workspace_pointer', ['@workspace_pointer' => $workspace_pointer->label()]),
+      ];
+    }
+    return $permissions;
+  }
+
+}
diff --git a/src/ReplicationAccessControlHandler.php b/src/ReplicationAccessControlHandler.php
index 33209b8..6dcb3bc 100644
--- a/src/ReplicationAccessControlHandler.php
+++ b/src/ReplicationAccessControlHandler.php
@@ -4,20 +4,53 @@ namespace Drupal\deploy;
 
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Entity\EntityAccessControlHandler;
+use Drupal\Core\Entity\EntityHandlerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Session\AccountInterface;
+use Drupal\multiversion\Workspace\WorkspaceManagerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * ReplicationAccessControlHandler class.
  */
-class ReplicationAccessControlHandler extends EntityAccessControlHandler {
+class ReplicationAccessControlHandler extends EntityAccessControlHandler implements EntityHandlerInterface {
+
+  /**
+   * The workspace manager service.
+   *
+   * @var \Drupal\multiversion\Workspace\WorkspaceManagerInterface
+   */
+  protected $workspaceManager;
+
+  /**
+   * Constructs a NodeAccessControlHandler object.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type definition.
+   * @param \Drupal\multiversion\Workspace\WorkspaceManagerInterface $workspace_manager
+   *   The workspace manager service.
+   */
+  public function __construct(EntityTypeInterface $entity_type, WorkspaceManagerInterface $workspace_manager) {
+    parent::__construct($entity_type);
+    $this->workspaceManager = $workspace_manager;
+  }
 
   /**
    * {@inheritdoc}
    */
-  protected function checkFieldAccess($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
+  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
+    return new static(
+      $entity_type,
+      $container->get('workspace.manager')
+    );
+  }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function checkFieldAccess($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
     $restricted_fields = ['source', 'target'];
     if (in_array($field_definition->getName(), $restricted_fields)) {
       return AccessResult::forbidden();
@@ -26,4 +59,20 @@ class ReplicationAccessControlHandler extends EntityAccessControlHandler {
     return parent::checkFieldAccess($operation, $field_definition, $account, $items);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
+    $access = parent::checkCreateAccess($account, $context, $entity_bundle);
+    $active_workspace = $this->workspaceManager->getActiveWorkspace();
+    $upstream_workspace_pointer = $active_workspace->upstream->entity;
+
+    // When no upstream workspace pointer is set or when the user doesn't have
+    // the permissions to deploy to the upstream, the access is forbidden.
+    if (!$upstream_workspace_pointer || !$account->hasPermission('Deploy to ' . $upstream_workspace_pointer->label()) ) {
+      $access = AccessResult::forbidden();
+    }
+    return $access;
+  }
+
 }
diff --git a/tests/src/Functional/DeploymentAccessTest.php b/tests/src/Functional/DeploymentAccessTest.php
new file mode 100644
index 0000000..5ca9193
--- /dev/null
+++ b/tests/src/Functional/DeploymentAccessTest.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Drupal\Tests\deploy\Functional;
+
+use Drupal\Tests\BrowserTestBase;
+use Drupal\user\Entity\Role;
+
+/**
+ * Tests access for creating deployments.
+ *
+ * @group workspace
+ */
+class DeploymentAccessTest extends BrowserTestBase {
+
+  public static $modules = [
+    'user',
+    'deploy',
+    'toolbar',
+  ];
+
+  /**
+   * Test deployment access.
+   */
+  public function testDeploymentAccess() {
+    $web_assert = $this->assertSession();
+
+    // Create and login a user with limited permissions.
+    $permissions = [
+      'access administration pages',
+      'administer workspaces',
+      'access toolbar',
+    ];
+    $test_user = $this->drupalCreateUser($permissions);
+    $this->drupalLogin($test_user);
+
+    // Check the user can't access the deploy link or the deploy page for Live.
+    $this->drupalGet('<front>');
+    $web_assert->linkExists('Live');
+    $web_assert->linkNotExists('Deploy');
+    $this->drupalGet('/admin/structure/deployment/add');
+    $web_assert->statusCodeEquals('403');
+
+    // Switch to the stage workspace.
+    $this->drupalPostForm('/admin/structure/workspace/2/activate', [], 'Activate');
+
+    // Check ther use can't access the deploy link or the deploy page for Stage.
+    $this->drupalGet('<front>');
+    $web_assert->linkExists('Stage');
+    $web_assert->linkNotExists('Deploy');
+    $this->drupalGet('/admin/structure/deployment/add');
+    $web_assert->statusCodeEquals('403');
+
+    // Give the user access to deploy to live.
+    $test_user_roles = $test_user->getRoles();
+    $this->grantPermissions(Role::load(reset($test_user_roles)), ['Deploy to Live']);
+
+    // Check the use can access the deploy link or the deploy page for Stage.
+    $this->drupalGet('<front>');
+    $web_assert->linkExists('Stage');
+    $web_assert->linkExists('Deploy');
+    $this->drupalGet('/admin/structure/deployment/add');
+    $web_assert->statusCodeEquals('200');
+  }
+
+}
