diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 5d7ef4b..f1f5191 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -906,6 +906,14 @@ function node_node_access(NodeInterface $node, $op, $account) {
   $type = $node->bundle();
 
   switch ($op) {
+    case 'view':
+      if (!$node->isPublished()) {
+        return AccessResult::allowedIfHasPermissions($account, array('view any unpublished content', 'view unpublished ' . $type . ' content'), 'OR');
+      }
+      else {
+        // No opinion.
+        return AccessResult::neutral();
+      }
     case 'create':
       return AccessResult::allowedIfHasPermission($account, 'create ' . $type . ' content');
 
diff --git a/core/modules/node/node.permissions.yml b/core/modules/node/node.permissions.yml
index f852db7..ff7cae3 100644
--- a/core/modules/node/node.permissions.yml
+++ b/core/modules/node/node.permissions.yml
@@ -17,6 +17,8 @@ access content:
   title: 'View published content'
 view own unpublished content:
   title: 'View own unpublished content'
+view any unpublished content:
+  title: 'View any unpublished content'
 view all revisions:
   title: 'View all revisions'
 revert all revisions:
diff --git a/core/modules/node/node.views_execution.inc b/core/modules/node/node.views_execution.inc
index 701f439..ec8967f 100644
--- a/core/modules/node/node.views_execution.inc
+++ b/core/modules/node/node.views_execution.inc
@@ -17,6 +17,7 @@ function node_views_query_substitutions(ViewExecutable $view) {
   return array(
     '***ADMINISTER_NODES***' => intval($account->hasPermission('administer nodes')),
     '***VIEW_OWN_UNPUBLISHED_NODES***' => intval($account->hasPermission('view own unpublished content')),
+    '***VIEW_ANY_UNPUBLISHED_NODE***' => intval($account->hasPermission('view any unpublished content')),
     '***BYPASS_NODE_ACCESS***' =>  intval($account->hasPermission('bypass node access')),
   );
 }
diff --git a/core/modules/node/src/NodePermissions.php b/core/modules/node/src/NodePermissions.php
index fd28110..94b426c 100644
--- a/core/modules/node/src/NodePermissions.php
+++ b/core/modules/node/src/NodePermissions.php
@@ -65,6 +65,9 @@ protected function buildPermissions(NodeType $type) {
       "delete any $type_id content" => array(
         'title' => $this->t('%type_name: Delete any content', $type_params),
       ),
+      "view unpublished $type_id content" => array(
+        'title' => $this->t('%type_name: View unpublished', $type_params),
+      ),
       "view $type_id revisions" => array(
         'title' => $this->t('%type_name: View revisions', $type_params),
       ),
diff --git a/core/modules/node/src/NodeViewsData.php b/core/modules/node/src/NodeViewsData.php
index 1c648c4..958d289 100644
--- a/core/modules/node/src/NodeViewsData.php
+++ b/core/modules/node/src/NodeViewsData.php
@@ -45,12 +45,12 @@ public function getViewsData() {
     $data['node_field_data']['status']['filter']['use_equal'] = TRUE;
 
     $data['node_field_data']['status_extra'] = array(
-      'title' => $this->t('Published status or admin user'),
+      'title' => $this->t('Published status or unpublished access'),
       'help' => $this->t('Filters out unpublished content if the current user cannot view it.'),
       'filter' => array(
         'field' => 'status',
         'id' => 'node_status',
-        'label' => $this->t('Published status or admin user'),
+        'label' => $this->t('Published status or unpublished access'),
       ),
     );
 
diff --git a/core/modules/node/src/Plugin/views/filter/Status.php b/core/modules/node/src/Plugin/views/filter/Status.php
index 5e119e5..7f8e98b 100644
--- a/core/modules/node/src/Plugin/views/filter/Status.php
+++ b/core/modules/node/src/Plugin/views/filter/Status.php
@@ -27,7 +27,8 @@ public function canExpose() { return FALSE; }
 
   public function query() {
     $table = $this->ensureMyTable();
-    $this->query->addWhereExpression($this->options['group'], "$table.status = 1 OR ($table.uid = ***CURRENT_USER*** AND ***CURRENT_USER*** <> 0 AND ***VIEW_OWN_UNPUBLISHED_NODES*** = 1) OR ***BYPASS_NODE_ACCESS*** = 1");
+    // @todo: Add per-node-type unpublished checks.
+    $this->query->addWhereExpression($this->options['group'], "$table.status = 1 OR ($table.uid = ***CURRENT_USER*** AND ***CURRENT_USER*** <> 0 AND ***VIEW_OWN_UNPUBLISHED_NODES*** = 1) OR ***VIEW_ANY_UNPUBLISHED_NODE*** = 1 OR ***BYPASS_NODE_ACCESS*** = 1");
   }
 
   /**
diff --git a/core/modules/node/src/Tests/NodeAccessTest.php b/core/modules/node/src/Tests/NodeAccessTest.php
index 4decd66..5c83b10 100644
--- a/core/modules/node/src/Tests/NodeAccessTest.php
+++ b/core/modules/node/src/Tests/NodeAccessTest.php
@@ -55,23 +55,37 @@ function testNodeAccess() {
     $this->assertNodeAccess(array('view' => TRUE, 'update' => FALSE), $node4, $web_user4);
     $this->assertNodeAccess(array('view' => FALSE), $node4, $web_user5);
 
+    // Ensures user with 'view any unpublished content' can do so.
+    $web_user6 = $this->drupalCreateUser(array('access content', 'view any unpublished content'));
+    $this->assertNodeAccess(array('view' => TRUE, 'update' => FALSE), $node4, $web_user6);
+
+    // Ensures the more granular view unpublished permissions are respected.
+    $web_user7 = $this->drupalCreateUser(array('access content', 'view unpublished page content'));
+    $web_user8 = $this->drupalCreateUser(array('access content', 'view unpublished article content'));
+    $node5 = $this->drupalCreateNode(array('type' => 'page', 'status' => 0));
+    $node6 = $this->drupalCreateNode(array('type' => 'article', 'status' => 0));
+    $this->assertNodeAccess(array('view' => TRUE, 'update' => FALSE), $node5, $web_user7);
+    $this->assertNodeAccess(array('view' => FALSE, 'update' => FALSE), $node5, $web_user8);
+    $this->assertNodeAccess(array('view' => FALSE, 'update' => FALSE), $node6, $web_user7);
+    $this->assertNodeAccess(array('view' => TRUE, 'update' => FALSE), $node6, $web_user8);
+
     // Tests the default access provided for a published node.
-    $node5 = $this->drupalCreateNode();
-    $this->assertNodeAccess(array('view' => TRUE, 'update' => FALSE, 'delete' => FALSE), $node5, $web_user3);
+    $node7 = $this->drupalCreateNode();
+    $this->assertNodeAccess(array('view' => TRUE, 'update' => FALSE, 'delete' => FALSE), $node7, $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);
+    $web_user9 = $this->drupalCreateUser(array('access content', 'edit any page content', 'delete any page content'));
+    $node8 = $this->drupalCreateNode(array('type' => 'page'));
+    $this->assertNodeAccess(array('view' => TRUE, 'update' => TRUE, 'delete' => TRUE), $node8, $web_user9);
 
     // 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'));
+    $web_user10 = $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);
+    $this->assertNodeAccess(array('view' => TRUE, 'update' => FALSE, 'delete' => FALSE), $node8, $web_user10);
 
     // 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);
+    $node9 = $this->drupalCreateNode(array('type' => 'page', 'uid' => $web_user10->id()));
+    $this->assertNodeAccess(array('view' => TRUE, 'update' => TRUE, 'delete' => TRUE), $node9, $web_user10);
   }
 
 }
