diff --git a/core/modules/node/src/NodeAccessControlHandler.php b/core/modules/node/src/NodeAccessControlHandler.php
index 3ec9631..6475f9b 100644
--- a/core/modules/node/src/NodeAccessControlHandler.php
+++ b/core/modules/node/src/NodeAccessControlHandler.php
@@ -109,21 +109,8 @@ protected function checkAccess(EntityInterface $node, $operation, $langcode, Acc
       return AccessResult::allowed()->cachePerPermissions()->cachePerUser()->cacheUntilEntityChanges($node);
     }
 
-    // If no module specified either ALLOW or KILL, we fall back to the
-    // node_access table.
-    $grants = $this->grantStorage->access($node, $operation, $langcode, $account);
-    if ($grants->isAllowed() || $grants->isForbidden()) {
-      return $grants;
-    }
-
-    // If no modules implement hook_node_grants(), the default behavior is to
-    // allow all users to view published nodes, so reflect that here.
-    if ($operation === 'view') {
-      return AccessResult::allowedIf($status)->cacheUntilEntityChanges($node);
-    }
-
-    // No opinion.
-    return AccessResult::neutral();
+    // Evaluate node_access table records created by hook_node_grants().
+    return $this->grantStorage->access($node, $operation, $langcode, $account);
   }
 
   /**
diff --git a/core/modules/node/src/NodeGrantDatabaseStorage.php b/core/modules/node/src/NodeGrantDatabaseStorage.php
index 2185245..a44f855 100644
--- a/core/modules/node/src/NodeGrantDatabaseStorage.php
+++ b/core/modules/node/src/NodeGrantDatabaseStorage.php
@@ -69,8 +69,14 @@ public function access(NodeInterface $node, $operation, $langcode, AccountInterf
     // If no module implements the hook or the node does not have an id there is
     // no point in querying the database for access grants.
     if (!$this->moduleHandler->getImplementations('node_grants') || !$node->id()) {
-      // No opinion.
-      return AccessResult::neutral();
+      // Return the equivalent of the fallback grant: allow to view if node is
+      // published.
+      if ($operation === 'view') {
+        return AccessResult::allowedIf($node->getTranslation($langcode)->isPublished())->cacheUntilEntityChanges($node);
+      }
+      else {
+        return AccessResult::neutral();
+      }
     }
 
     // Check the database for potential access grants.
@@ -117,7 +123,7 @@ public function access(NodeInterface $node, $operation, $langcode, AccountInterf
       return $set_cacheability(AccessResult::allowed());
     }
     else {
-      return $set_cacheability(AccessResult::forbidden());
+      return $set_cacheability(AccessResult::neutral());
     }
   }
 
diff --git a/core/modules/node/src/NodeGrantDatabaseStorageInterface.php b/core/modules/node/src/NodeGrantDatabaseStorageInterface.php
index e865387..e2f132d 100644
--- a/core/modules/node/src/NodeGrantDatabaseStorageInterface.php
+++ b/core/modules/node/src/NodeGrantDatabaseStorageInterface.php
@@ -106,7 +106,7 @@ public function writeDefault();
    *   The user for which to check access.
    *
    * @return \Drupal\Core\Access\AccessResultInterface
-   *   The access result.
+   *   The access result, either allowed or neutral.
    */
   public function access(NodeInterface $node, $operation, $langcode, AccountInterface $account);
 
diff --git a/core/modules/node/src/Tests/NodeAccessGrantsTest.php b/core/modules/node/src/Tests/NodeAccessGrantsTest.php
new file mode 100644
index 0000000..4c924c8
--- /dev/null
+++ b/core/modules/node/src/Tests/NodeAccessGrantsTest.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node\Tests\NodeAccessGrantsTest.
+ */
+
+namespace Drupal\node\Tests;
+
+use Drupal\node\Entity\NodeType;
+
+/**
+ * Tests basic node_access functionality with hook_node_grants().
+ *
+ * This test just wraps the existing default permissions test while a module
+ * that implements hook_node_grants() is enabled.
+ *
+ * @group node
+ */
+class NodeAccessGrantsTest extends NodeAccessTest {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('node', 'datetime', 'node_access_test');
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // Make sure the grants in node_access_test_node_access_records() are not
+    // actually considered, because they break assertions in NodeAccessTest. The
+    // fact that the hook is implemented is enough for this test.
+    \Drupal::state()->set('node_access_test.private', TRUE);
+    node_access_test_add_field(NodeType::load('page'));
+  }
+
+}
diff --git a/core/modules/node/src/Tests/NodeAccessTest.php b/core/modules/node/src/Tests/NodeAccessTest.php
index 5812380..a38f280 100644
--- a/core/modules/node/src/Tests/NodeAccessTest.php
+++ b/core/modules/node/src/Tests/NodeAccessTest.php
@@ -18,6 +18,7 @@
  * @todo Cover hook_node_access in a separate test class.
  */
 class NodeAccessTest extends NodeTestBase {
+
   protected function setUp() {
     parent::setUp();
     // Clear permissions for authenticated users.
@@ -58,6 +59,20 @@ function testNodeAccess() {
     // Tests the default access provided for a published node.
     $node5 = $this->drupalCreateNode();
     $this->assertNodeAccess(array('view' => TRUE, 'update' => FALSE, 'delete' => FALSE), $node5, $web_user3);
+
+    // Tests the "edit any BUNDLE" and "delete any BUNDLE" permissions.
+    $web_user6 = $this->drupalCreateUser(array('access content', 'edit any page content', 'delete any page content'));
+    $node6 = $this->drupalCreateNode(array('type' => 'page'));
+    $this->assertNodeAccess(array('view' => TRUE, 'update' => TRUE, 'delete' => TRUE), $node6, $web_user6);
+
+    // Tests the "edit own BUNDLE" and "delete own BUNDLE" permission.
+    $web_user7 = $this->drupalCreateUser(array('access content', 'edit own page content', 'delete own page content'));
+    // User should not be able to edit or delete nodes they do not own.
+    $this->assertNodeAccess(array('view' => TRUE, 'update' => FALSE, 'delete' => FALSE), $node6, $web_user7);
+
+    // User should be able to edit or delete nodes they own.
+    $node7 = $this->drupalCreateNode(array('type' => 'page', 'uid' => $web_user7->id()));
+    $this->assertNodeAccess(array('view' => TRUE, 'update' => TRUE, 'delete' => TRUE), $node7, $web_user7);
   }
 
 }
diff --git a/core/modules/node/src/Tests/Views/BulkFormAccessTest.php b/core/modules/node/src/Tests/Views/BulkFormAccessTest.php
index 027a66f..480f931 100644
--- a/core/modules/node/src/Tests/Views/BulkFormAccessTest.php
+++ b/core/modules/node/src/Tests/Views/BulkFormAccessTest.php
@@ -105,7 +105,7 @@ public function testNodeEditAccess() {
 
     // Create an account that may view the private node, but can update the
     // status.
-    $account = $this->drupalCreateUser(array('administer nodes', 'edit any article content', 'node test view'));
+    $account = $this->drupalCreateUser(array('administer nodes', 'node test view'));
     $this->drupalLogin($account);
 
     // Ensure the node is published.
