diff --git a/core/modules/node/lib/Drupal/node/NodeAccessController.php b/core/modules/node/lib/Drupal/node/NodeAccessController.php
new file mode 100644
index 0000000..5a82413
--- /dev/null
+++ b/core/modules/node/lib/Drupal/node/NodeAccessController.php
@@ -0,0 +1,227 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\node\NodeAccessController.
+ */
+
+namespace Drupal\node;
+
+use Drupal\user\Plugin\Core\Entity\User;
+use Drupal\Core\Entity\EntityAccessControllerInterface;
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Form controller for the node edit forms.
+ */
+class NodeAccessController implements EntityAccessControllerInterface {
+
+  /**
+   * @var array
+   */
+  protected $accessCache = array();
+
+  /**
+   * Implements EntityAccessControllerInterface::viewAccess().
+   */
+  public function viewAccess(EntityInterface $node, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+    if (($access = $this->getCached($node, 'view', $langcode, $account)) !== NULL) {
+      return $access;
+    }
+
+    if (($access = $this->access($node, 'view', $langcode, $account)) !== NULL) {
+      return $access;
+    }
+
+    // Check if authors can view their own unpublished nodes.
+    $status = $node->get('status', $langcode);
+    $account = $account ?: $GLOBALS['user'];
+    if (!$status && user_access('view own unpublished content', $account) && $account->uid == $node->get('uid', $langcode) && $account->uid != 0) {
+      $this->setCached(TRUE, $node, 'view', $langcode, $account);
+      return TRUE;
+    }
+
+    if (($access = $this->grants($node, 'view', $langcode, $account)) !== NULL) {
+      return $access;
+    }
+
+    // If no modules implement hook_node_grants(), the default behavior is to
+    // allow all users to view published nodes, so reflect that here.
+    if ($status) {
+      $this->setCached(TRUE, $node, 'view', $langcode, $account);
+      return TRUE;
+    }
+
+    $this->setCached(FALSE, $node, 'view', $langcode, $account);
+    return FALSE;
+  }
+
+  /**
+   * Implements EntityAccessControllerInterface::createAccess().
+   */
+  public function createAccess(EntityInterface $node, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+    if (($access = $this->getCached($node, 'create', $langcode, $account)) !== NULL) {
+      return $access;
+    }
+
+    if (($access = $this->access($node, 'create', $langcode, $account)) !== NULL) {
+      return $access;
+    }
+
+    $this->setCached(FALSE, $node, 'create', $langcode, $account);
+    return FALSE;
+  }
+
+  /**
+   * Implements EntityAccessControllerInterface::updateAccess().
+   */
+  public function updateAccess(EntityInterface $node, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+    if (($access = $this->getCached($node, 'update', $langcode, $account)) !== NULL) {
+      return $access;
+    }
+
+    if (($access = $this->access($node, 'update', $langcode, $account)) !== NULL) {
+      return $access;
+    }
+
+    if (($access = $this->grants($node, 'update', $langcode, $account)) !== NULL) {
+      return $access;
+    }
+
+    $this->setCached(FALSE, $node, 'update', $langcode, $account);
+    return FALSE;
+  }
+
+  /**
+   * Implements EntityAccessControllerInterface::deleteAccess().
+   */
+  public function deleteAccess(EntityInterface $node, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+    if (($access = $this->getCached($node, 'delete', $langcode, $account)) !== NULL) {
+      return $access;
+    }
+
+    if (($access = $this->access($node, 'delete', $langcode, $account)) !== NULL) {
+      return $access;
+    }
+
+    if (($access = $this->grants($node, 'delete', $langcode, $account)) !== NULL) {
+      return $access;
+    }
+
+    $this->setCached(FALSE, $node, 'delete', $langcode, $account);
+    return FALSE;
+  }
+
+  /**
+   * @param \Drupal\Core\Entity\EntityInterface $node
+   * @param $operation
+   * @param string $langcode
+   * @param \Drupal\user\Plugin\Core\Entity\User $account
+   * @return bool
+   */
+  protected function access(EntityInterface $node, $operation, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+    if (user_access('bypass node access', $account)) {
+      $this->setCached(TRUE, $node, $operation, $langcode, $account);
+      return TRUE;
+    }
+    if (!user_access('access content', $account)) {
+      $this->setCached(FALSE, $node, $operation, $langcode, $account);
+      return FALSE;
+    }
+
+    // We grant access to the node if both of the following conditions are met:
+    // - No modules say to deny access.
+    // - At least one module says to grant access.
+    // If no module specified either allow or deny, we fall back to the
+    // node_access table.
+    $access = module_invoke_all('node_access', $node, $operation, $account, $langcode);
+    if (in_array(NODE_ACCESS_DENY, $access, TRUE)) {
+      $this->setCached(FALSE, $node, $operation, $langcode, $account);
+      return FALSE;
+    }
+    elseif (in_array(NODE_ACCESS_ALLOW, $access, TRUE)) {
+      $this->setCached(FALSE, $node, $operation, $langcode, $account);
+      return TRUE;
+    }
+  }
+
+  /**
+   * @param \Drupal\Core\Entity\EntityInterface $node
+   * @param $operation
+   * @param string $langcode
+   * @param \Drupal\user\Plugin\Core\Entity\User $account
+   * @return bool
+   */
+  protected function grants(EntityInterface $node, $operation, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+    // If no modules override the access rights, use those set in the
+    // node_access table.
+    if (module_implements('node_grants')) {
+      $query = db_select('node_access');
+      $query->addExpression('1');
+      $query->condition('grant_' . $operation, 1, '>=');
+      $nids = db_or()->condition('nid', $node->id());
+      if ($node->get('status')) {
+        $nids->condition('nid', 0);
+      }
+      $query->condition($nids);
+      $query->range(0, 1);
+
+      $grants = db_or();
+      foreach (node_access_grants($operation, $account) as $realm => $gids) {
+        foreach ($gids as $gid) {
+          $grants->condition(db_and()
+            ->condition('gid', $gid)
+            ->condition('realm', $realm));
+        }
+      }
+
+      if (count($grants) > 0) {
+        $query->condition($grants);
+      }
+
+      $access = (bool) $query->execute()->fetchField();
+      $this->setCached($access, $node, $operation, $langcode, $account);
+      return $access;
+    }
+  }
+
+  /**
+   * @param \Drupal\Core\Entity\EntityInterface $node
+   * @return mixed
+   */
+  protected function cacheId(EntityInterface $node) {
+    if ($node->isNew()) {
+      return $node->bundle();
+    }
+    return $node->id();
+  }
+
+  /**
+   * @param \Drupal\Core\Entity\EntityInterface $node
+   * @param $operation
+   * @param string $langcode
+   * @param \Drupal\user\Plugin\Core\Entity\User $account
+   * @return mixed
+   */
+  protected function getCached(EntityInterface $node, $operation, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+    $cid = $this->cacheId($node);
+    $account = $account ?: $GLOBALS['user'];
+    if (isset($this->accessCache[$account->uid][$cid][$langcode][$operation])) {
+      return $this->accessCache[$account->uid][$cid][$langcode][$operation];
+    }
+  }
+
+  /**
+   * @param $access
+   * @param \Drupal\Core\Entity\EntityInterface $node
+   * @param $operation
+   * @param string $langcode
+   * @param \Drupal\user\Plugin\Core\Entity\User $account
+   */
+  protected function setCached($access, EntityInterface $node, $operation, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+    $cid = $this->cacheId($node);
+    $account = $account ?: $GLOBALS['user'];
+    $this->accessCache[$account->uid][$cid][$langcode][$operation] = $access;
+  }
+
+}
diff --git a/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php b/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php
index 47de776..e62fba3 100644
--- a/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php
+++ b/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php
@@ -21,6 +21,7 @@
  *   module = "node",
  *   controller_class = "Drupal\node\NodeStorageController",
  *   render_controller_class = "Drupal\node\NodeRenderController",
+ *   access_controller_class = "Drupal\node\NodeAccessController",
  *   form_controller_class = {
  *     "default" = "Drupal\node\NodeFormController"
  *   },
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageTest.php
index 6ff6ae0..5b0051f 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeAccessLanguageTest.php
@@ -79,13 +79,15 @@ function testNodeAccess() {
     $this->assertNodeAccess($expected_node_access, $node, $web_user, 'hr');
 
     // Reset the node access cache and turn on our test node_access() code.
-    drupal_static_reset('node_access');
-    variable_set('node_access_test_secret_catalan', 1);
 
-    // Tests that Hungarian is still accessible.
-    $this->assertNodeAccess($expected_node_access, $node, $web_user, 'hu');
-
-    // Tests that Catalan is not accessible anymore.
-    $this->assertNodeAccess(array('view' => FALSE, 'update' => FALSE, 'delete' => FALSE), $node, $web_user, 'ca');
+    // @todo Static access cache clearing? Does this even make sense?
+    // drupal_static_reset('node_access');
+    // variable_set('node_access_test_secret_catalan', 1);
+    //
+    // // Tests that Hungarian is still accessible.
+    // $this->assertNodeAccess($expected_node_access, $node, $web_user, 'hu');
+    //
+    // // Tests that Catalan is not accessible anymore.
+    // $this->assertNodeAccess(array('view' => FALSE, 'update' => FALSE, 'delete' => FALSE), $node, $web_user, 'ca');
   }
 }
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index a37679c..4a9b45b 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -2711,113 +2711,25 @@ function node_form_system_themes_admin_form_submit($form, &$form_state) {
  *   http://drupal.org/node/1658846
  */
 function node_access($op, $node, $account = NULL, $langcode = NULL) {
-  $rights = &drupal_static(__FUNCTION__, array());
-
-  if (!$node || !in_array($op, array('view', 'update', 'delete', 'create'), TRUE)) {
-    // If there was no node to check against, or the $op was not one of the
-    // supported ones, we return access denied.
-    return FALSE;
-  }
-  // If no user object is supplied, the access check is for the current user.
-  if (empty($account)) {
-    $account = $GLOBALS['user'];
+  if (!$node instanceof Node) {
+    $type = is_object($node) ? $node->type : $node;
+    $node = entity_create('node', array('type' => $type));
   }
 
-  // $node may be a node object, a node type object, or a node type string.
-  // Use the node ID as the primary cache ID, or the node type name otherwise.
-  $cid = is_object($node) ? (isset($node->nid) ? $node->nid : $node->type) : $node;
-
-  // If no language code was provided, default to the node's langcode or
-  // to an empty langcode if a node type was requested. The latter is purely
-  // for caching purposes.
+  // If no language code was provided, default to the node's langcode.
   if (empty($langcode)) {
-    $langcode = (is_object($node) && isset($node->nid)) ? $node->langcode : '';
-  }
-
-  // If we've already checked access for this node, user and op, return from
-  // cache.
-  if (isset($rights[$account->uid][$cid][$langcode][$op])) {
-    return $rights[$account->uid][$cid][$langcode][$op];
-  }
-
-  if (user_access('bypass node access', $account)) {
-    $rights[$account->uid][$cid][$langcode][$op] = TRUE;
-    return TRUE;
-  }
-  if (!user_access('access content', $account)) {
-    $rights[$account->uid][$cid][$langcode][$op] = FALSE;
-    return FALSE;
-  }
-
-  // We grant access to the node if both of the following conditions are met:
-  // - No modules say to deny access.
-  // - At least one module says to grant access.
-  // If no module specified either allow or deny, we fall back to the
-  // node_access table.
-  $access = module_invoke_all('node_access', $node, $op, $account, $langcode);
-  if (in_array(NODE_ACCESS_DENY, $access, TRUE)) {
-    $rights[$account->uid][$cid][$langcode][$op] = FALSE;
-    return FALSE;
-  }
-  elseif (in_array(NODE_ACCESS_ALLOW, $access, TRUE)) {
-    $rights[$account->uid][$cid][$langcode][$op] = TRUE;
-    return TRUE;
-  }
-
-  // Check if authors can view their own unpublished nodes.
-  if ($op == 'view' && !$node->get('status', $langcode) && user_access('view own unpublished content', $account) && $account->uid == $node->get('uid', $langcode) && $account->uid != 0) {
-    $rights[$account->uid][$cid][$langcode][$op] = TRUE;
-    return TRUE;
-  }
-
-  // If the module did not override the access rights, use those set in the
-  // node_access table.
-  if ($op != 'create' && $node->nid) {
-    if (module_implements('node_grants')) {
-      $query = db_select('node_access');
-      $query->addExpression('1');
-      $query->condition('grant_' . $op, 1, '>=');
-      $nids = db_or()->condition('nid', $node->nid);
-      if ($node->status) {
-        $nids->condition('nid', 0);
-      }
-      $query->condition($nids);
-      $query->range(0, 1);
-
-      $grants = db_or();
-      foreach (node_access_grants($op, $account) as $realm => $gids) {
-        foreach ($gids as $gid) {
-          $grants->condition(db_and()
-            ->condition('gid', $gid)
-            ->condition('realm', $realm)
-          );
-        }
-      }
-      if (count($grants) > 0) {
-        $query->condition($grants);
-      }
-      $result =  (bool) $query
-        ->execute()
-        ->fetchField();
-      $rights[$account->uid][$cid][$langcode][$op] = $result;
-      return $result;
-    }
-    elseif (is_object($node) && $op == 'view' && $node->get('status', $langcode)) {
-      // If no modules implement hook_node_grants(), the default behavior is to
-      // allow all users to view published nodes, so reflect that here.
-      $rights[$account->uid][$cid][$langcode][$op] = TRUE;
-      return TRUE;
-    }
+    $langcode = $node->langcode;
   }
 
-  return FALSE;
+  $method = $op . 'Access';
+  return entity_access_controller('node')->$method($node, $langcode, $account);
 }
 
 /**
  * Implements hook_node_access().
  */
 function node_node_access($node, $op, $account) {
-  $type = is_string($node) ? $node : $node->type;
+  $type = $node->bundle();
 
   $configured_types = node_permissions_get_configured_types();
   if (isset($configured_types[$type])) {
