diff --git a/core/lib/Drupal/Core/Session/AccountInterface.php b/core/lib/Drupal/Core/Session/AccountInterface.php
index 1dbc13c..2de00de 100644
--- a/core/lib/Drupal/Core/Session/AccountInterface.php
+++ b/core/lib/Drupal/Core/Session/AccountInterface.php
@@ -32,6 +32,17 @@ public function id();
   public function getRoles();
 
   /**
+   * Whether a user has a certain permission.
+   *
+   * @param string $permission
+   *   The permission string to check.
+   *
+   * @return bool
+   *   TRUE if the user has the permission, FALSE otherwise.
+   */
+  public function hasPermission($permission);
+
+  /**
    * Returns the session ID.
    *
    * @return string|NULL
diff --git a/core/lib/Drupal/Core/Session/UserSession.php b/core/lib/Drupal/Core/Session/UserSession.php
index 300cd8f..72a7edf 100644
--- a/core/lib/Drupal/Core/Session/UserSession.php
+++ b/core/lib/Drupal/Core/Session/UserSession.php
@@ -94,6 +94,25 @@ public function getRoles() {
   /**
    * {@inheritdoc}
    */
+  public function hasPermission($permission) {
+    // User #1 has all privileges.
+    if ((int) $this->id() === 1) {
+      return TRUE;
+    }
+
+    $roles = \Drupal::entityManager()->getStorageController('user_role')->load($this->getRoles());
+    foreach ($roles as $role) {
+      if ($role->hasPermission($permission)) {
+        return TRUE;
+      }
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getSecureSessionId() {
     return $this->ssid;
   }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php
index b613121..cf677ce 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Database\Database;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\DrupalKernel;
+use Drupal\Core\Session\UserSession;
 use Drupal\simpletest\WebTestBase;
 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
 use Symfony\Component\HttpFoundation\Request;
@@ -144,15 +145,12 @@ protected function setUp() {
     // Ensure that the session is not written to the new environment and replace
     // the global $user session with uid 1 from the new test site.
     drupal_save_session(FALSE);
-    // Login as uid 1.
-    $user = db_query('SELECT * FROM {users} WHERE uid = :uid', array(':uid' => 1))->fetchObject();
-    // Load roles for the user object.
-    $roles = array(DRUPAL_AUTHENTICATED_RID => DRUPAL_AUTHENTICATED_RID);
-    $result = db_query('SELECT rid, uid FROM {users_roles} WHERE uid = :uid', array(':uid' => 1));
-    foreach ($result as $record) {
-      $roles[$record->rid] = $record->rid;
-    }
-    $user->roles = $roles;
+    // Load values for uid 1.
+    $values = db_query('SELECT * FROM {users} WHERE uid = :uid', array(':uid' => 1))->fetchAssoc();
+    // Load rolest.
+    $values['roles'] = array_merge(array(DRUPAL_AUTHENTICATED_RID), db_query('SELECT rid FROM {users_roles} WHERE uid = :uid', array(':uid' => 1))->fetchCol());
+    // Create a new user session object.
+    $user = new UserSession($values);
 
     // Generate and set a D8-compatible session cookie.
     $this->prepareD8Session();
diff --git a/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/User.php b/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/User.php
index c9a671b..0ed83ad 100644
--- a/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/User.php
+++ b/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/User.php
@@ -368,4 +368,23 @@ public function removeRole($rid) {
     $this->set('roles', array_diff($this->getRoles(), array($rid)));
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function hasPermission($permission) {
+    // User #1 has all privileges.
+    if ((int) $this->id() === 1) {
+      return TRUE;
+    }
+
+    $roles = \Drupal::entityManager()->getStorageController('user_role')->load($this->getRoles());
+    foreach ($roles as $role) {
+      if ($role->hasPermission($permission)) {
+        return TRUE;
+      }
+    }
+
+    return FALSE;
+  }
+
 }
diff --git a/core/modules/user/lib/Drupal/user/RoleStorageController.php b/core/modules/user/lib/Drupal/user/RoleStorageController.php
index fff106e..ff012f3 100644
--- a/core/modules/user/lib/Drupal/user/RoleStorageController.php
+++ b/core/modules/user/lib/Drupal/user/RoleStorageController.php
@@ -17,15 +17,6 @@ class RoleStorageController extends ConfigStorageController implements RoleStora
   /**
    * {@inheritdoc}
    */
-  public function resetCache(array $ids = NULL) {
-    parent::resetCache($ids);
-    // Clear the user access cache.
-    drupal_static_reset('user_access');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function deleteRoleReferences(array $rids) {
     // Remove the role from all users.
     db_delete('users_roles')
diff --git a/core/modules/user/lib/Drupal/user/UserBCDecorator.php b/core/modules/user/lib/Drupal/user/UserBCDecorator.php
index eed508b..82b3908 100644
--- a/core/modules/user/lib/Drupal/user/UserBCDecorator.php
+++ b/core/modules/user/lib/Drupal/user/UserBCDecorator.php
@@ -76,4 +76,10 @@ public function removeRole($rid) {
     $this->decorated->removeRole($rid);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function hasPermission($permission) {
+    return $this->decorated->hasPermission($permission);
+  }
 }
diff --git a/core/modules/user/lib/Drupal/user/UserInterface.php b/core/modules/user/lib/Drupal/user/UserInterface.php
index 4dc1481..25a3de0 100644
--- a/core/modules/user/lib/Drupal/user/UserInterface.php
+++ b/core/modules/user/lib/Drupal/user/UserInterface.php
@@ -50,4 +50,15 @@ public function addRole($rid);
    */
   public function removeRole($rid);
 
+  /**
+   * Whether a user has a certain permission.
+   *
+   * @param string $permission
+   *   The permission string to check.
+   *
+   * @return bool
+   *   TRUE if the user has the permission, FALSE otherwise.
+   */
+  public function hasPermission($permission);
+
 }
diff --git a/core/modules/user/tests/Drupal/user/Tests/Plugin/Core/Entity/UserTest.php b/core/modules/user/tests/Drupal/user/Tests/Plugin/Core/Entity/UserTest.php
new file mode 100644
index 0000000..0758f63
--- /dev/null
+++ b/core/modules/user/tests/Drupal/user/Tests/Plugin/Core/Entity/UserTest.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\user\Tests\Plugin\Core\Entity\UserTest.
+ */
+
+namespace Drupal\user\Tests\Plugin\Core\Entity;
+
+use Drupal\Tests\Core\Session\UserSessionTest;
+use Drupal\user\Plugin\Core\Entity\User;
+
+/**
+ * Tests the user object.
+ *
+ * @see \Drupal\user\Plugin\Core\Entity\User
+ */
+class UserTest extends UserSessionTest {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'User object',
+      'description' => 'Tests the user object.',
+      'group' => 'User',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function createUserSession(array $rids = array()) {
+    $user = $this->getMockBuilder('Drupal\user\Plugin\Core\Entity\User')
+      ->disableOriginalConstructor()
+      ->setMethods(array('getRoles', 'id'))
+      ->getMock();
+    $user->expects($this->any())
+      ->method('id')
+      // @todo Also test the uid = 1 handling.
+      ->will($this->returnValue(0));
+    $user->expects($this->any())
+      ->method('getRoles')
+      ->will($this->returnValue($rids));
+    return $user;
+  }
+
+}
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 41278fc..7f181d7 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -473,28 +473,7 @@ function user_access($string, AccountInterface $account = NULL) {
 
   // Make sure we are working with the BC decorator.
   $account = $account instanceof User ? $account->getBCEntity() : $account;
-
-  // User #1 has all privileges:
-  if ($account->uid == 1) {
-    return TRUE;
-  }
-
-  // To reduce the number of SQL queries, we cache the user's permissions
-  // in a static variable.
-  // Use the advanced drupal_static() pattern, since this is called very often.
-  static $drupal_static_fast;
-  if (!isset($drupal_static_fast)) {
-    $drupal_static_fast['perm'] = &drupal_static(__FUNCTION__);
-  }
-  $perm = &$drupal_static_fast['perm'];
-  if (!isset($perm[$account->uid])) {
-    $perm[$account->uid] = array();
-    foreach (user_role_permissions($account->getRoles()) as $role_permissions) {
-      $perm[$account->uid] += array_fill_keys($role_permissions, TRUE);
-    }
-  }
-
-  return isset($perm[$account->uid][$string]);
+  return $account->hasPermission($string);
 }
 
 /**
@@ -1942,8 +1921,6 @@ function user_role_grant_permissions($rid, array $permissions = array()) {
     $role->grantPermission($permission);
   }
   $role->save();
-  // Clear the user access cache.
-  drupal_static_reset('user_access');
 }
 
 /**
@@ -1965,7 +1942,6 @@ function user_role_revoke_permissions($rid, array $permissions = array()) {
   }
   $role->save();
   // Clear the user access cache.
-  drupal_static_reset('user_access');
 }
 
 function user_multiple_cancel_confirm($form, &$form_state) {
diff --git a/core/tests/Drupal/Tests/Core/Session/UserSessionTest.php b/core/tests/Drupal/Tests/Core/Session/UserSessionTest.php
new file mode 100644
index 0000000..d019d3f
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Session/UserSessionTest.php
@@ -0,0 +1,145 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Session\UserSessionTest.
+ */
+
+namespace Drupal\Tests\Core\Session;
+
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\Session\UserSession;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * Tests the user session object.
+ *
+ * @see \Drupal\Core\Session\UserSession
+ */
+class UserSessionTest extends UnitTestCase {
+
+  /**
+   * The user sessions used in the test
+   *
+   * @var \Drupal\Core\Session\AccountInterface[]
+   */
+  protected $users = array();
+
+  public static function getInfo() {
+    return array(
+      'name' => 'User session object',
+      'description' => 'Tests the user session object.',
+      'group' => 'Session',
+    );
+  }
+
+  /**
+   * Provides test data for getHasPermission().
+   *
+   * @return array
+   */
+  public function providerTestHasPermission() {
+    $data = array();
+    $data[] = array('example permission', array('user_one', 'user_two'), array('user_last'));
+    $data[] = array('another example permission', array('user_two'), array('user_one', 'user_last'));
+    $data[] = array('final example permission', array(), array('user_one', 'user_two', 'user_last'));
+
+    return $data;
+  }
+
+  /**
+   * Setups a user session for the test.
+   *
+   * @param array $rids
+   *   The rids of the user.
+   *
+   * @return \Drupal\Core\Session\AccountInterface
+   *   The created user session.
+   */
+  protected function createUserSession(array $rids = array()) {
+    return new UserSession(array('roles' => $rids));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $roles = array();
+    $roles['role_one'] = $this->getMockBuilder('Drupal\user\Plugin\Core\Entity\Role')
+      ->disableOriginalConstructor()
+      ->setMethods(array('hasPermission'))
+      ->getMock();
+    $roles['role_one']->expects($this->any())
+      ->method('hasPermission')
+      ->will($this->returnValueMap(array(
+        array('example permission', TRUE),
+        array('another example permission', FALSE),
+        array('last example permission', FALSE),
+      )));
+
+    $roles['role_two'] = $this->getMockBuilder('Drupal\user\Plugin\Core\Entity\Role')
+      ->disableOriginalConstructor()
+      ->setMethods(array('hasPermission'))
+      ->getMock();
+    $roles['role_two']->expects($this->any())
+      ->method('hasPermission')
+      ->will($this->returnValueMap(array(
+        array('example permission', TRUE),
+        array('another example permission', TRUE),
+        array('last example permission', FALSE),
+      )));
+
+    $role_storage = $this->getMockBuilder('Drupal\User\RoleStorageController')
+      ->setMethods(array('load'))
+      ->getMock();
+    $role_storage->expects($this->any())
+      ->method('load')
+      ->will($this->returnValueMap(array(
+        array(array(), $roles),
+        array(array('role_one'), array($roles['role_one'])),
+        array(array('role_two'), array($roles['role_two'])),
+        array(array('role_one', 'role_two'), array($roles['role_one'], $roles['role_two'])),
+      )));
+
+    $entity_manager = $this->getMockBuilder('Drupal\Core\Entity\EntityManager')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $entity_manager->expects($this->any())
+      ->method('getStorageController')
+      ->with($this->equalTo('user_role'))
+      ->will($this->returnValue($role_storage));
+    $container = new ContainerBuilder();
+    $container->set('plugin.manager.entity', $entity_manager);
+    \Drupal::setContainer($container);
+
+    $this->users['user_one'] = $this->createUserSession(array('role_one'));
+    $this->users['user_two'] = $this->createUserSession(array('role_one', 'role_two'));
+    $this->users['user_last'] = $this->createUserSession();
+  }
+
+  /**
+   * Tests the has permission method.
+   *
+   * @param string $permission
+   *   The permission to check.
+   * @param \Drupal\Core\Session\AccountInterface[] $sessions_with_access
+   *   The users with access.
+   * @param \Drupal\Core\Session\AccountInterface[] $sessions_without_access
+   *   The users without access.
+   *
+   * @dataProvider providerTestHasPermission
+   *
+   * @see \Drupal\Core\Session\UserSession::hasPermission().
+   */
+  public function testHasPermission($permission, array $sessions_with_access, array $sessions_without_access) {
+    foreach ($sessions_with_access as $name) {
+      $this->assertTrue($this->users[$name]->hasPermission($permission));
+    }
+    foreach ($sessions_without_access as $name) {
+      $this->assertFalse($this->users[$name]->hasPermission($permission));
+    }
+  }
+
+}
