diff --git a/core/includes/entity.inc b/core/includes/entity.inc
index 48ee728..0cbf147 100644
--- a/core/includes/entity.inc
+++ b/core/includes/entity.inc
@@ -340,6 +340,32 @@ function entity_page_label(EntityInterface $entity, $langcode = NULL) {
 }
 
 /**
+ * Returns the entity access controller for the given entity type.
+ *
+ * @see hook_entity_info()
+ *
+ * @param string $entity_type
+ *   The type of the entity.
+ *
+ * @return Drupal\Core\Entity\EntityAccessControllerInterface|bool
+ *   An entity access controller instance or FALSE if the given entity type does
+ *   not have an access controller.
+ */
+function entity_access_controller($entity_type) {
+  $controllers = &drupal_static(__FUNCTION__, array());
+  if (!array_key_exists($entity_type, $controllers)) {
+    $type_info = entity_get_info($entity_type);
+    if (isset($type_info['access controller class']) && $class = $type_info['access controller class']) {
+      $controllers[$entity_type] = new $class($entity_type);
+    }
+    else {
+      $controllers[$entity_type] = NULL;
+    }
+  }
+  return $controllers[$entity_type];
+}
+
+/**
  * Returns an entity form controller for the given operation.
  *
  * Since there might be different scenarios in which an entity is edited,
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index 8f462de..7df68d8 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -251,8 +251,17 @@ public function getIterator() {
   /**
    * Implements AccessibleInterface::access().
    */
-  public function access(\Drupal\user\User $account = NULL) {
-    // TODO: Implement access() method.
+  public function access($operation = 'view', \Drupal\user\User $user = NULL) {
+    if (!$controller = entity_access_controller($this->entityType)) {
+      // If no access controller is defined the entity is fully accessible by
+      // every user.
+      return FALSE;
+    }
+    if ($operation == 'edit') {
+      // Map the 'edit' operation to 'update'.
+      $operation = 'update';
+    }
+    return $controller->{"{$operation}Access"}($this, LANGUAGE_DEFAULT, $user);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/EntityAccessControllerInterface.php b/core/lib/Drupal/Core/Entity/EntityAccessControllerInterface.php
new file mode 100644
index 0000000..d6e261d
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/EntityAccessControllerInterface.php
@@ -0,0 +1,82 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Entity\EntityAccessControllerInterface.
+ */
+
+namespace Drupal\Core\Entity;
+
+/**
+ * Defines a common interface for entity access controller classes.
+ */
+interface EntityAccessControllerInterface {
+
+  /**
+   * Checks 'view' access for a given entity or entity translation.
+   *
+   * @param EntityInterface $entity
+   *   The entity to check 'view' access for.
+   * @param string $langcode
+   *   (optional) The language code for which to check access for. Defaults to
+   *   LANGUAGE_DEFAULT.
+   * @param \Drupal\user\User $user
+   *   (optional) The user for whom to check 'view' access for. Defaults to
+   *   the current user.
+   *
+   * @return bool
+   *   TRUE if access was granted, FALSE otherwise.
+   */
+  public function viewAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, \Drupal\user\User $user = NULL);
+
+  /**
+   * Checks 'create' access for a given entity or entity translation.
+   *
+   * @param EntityInterface $entity
+   *   The entity to check 'create' access for.
+   * @param string $langcode
+   *   (optional) The language code for which to check access for. Defaults to
+   *   LANGUAGE_DEFAULT.
+   * @param \Drupal\user\User $user
+   *   (optional) The user for whom to check 'create' access for. Defaults to
+   *   the current user.
+   *
+   * @return bool
+   *   TRUE if access was granted, FALSE otherwise.
+   */
+  public function createAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, \Drupal\user\User $user = NULL);
+
+  /**
+   * Checks 'update' access for a given entity or entity translation.
+   *
+   * @param EntityInterface $entity
+   *   The entity to check 'update' access for.
+   * @param string $langcode
+   *   (optional) The language code for which to check access for. Defaults to
+   *   LANGUAGE_DEFAULT.
+   * @param \Drupal\user\User $user
+   *   (optional) The user for whom to check 'update' access for. Defaults to
+   *   the current user.
+   *
+   * @return bool
+   *   TRUE if access was granted, FALSE otherwise.
+   */
+  public function updateAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, \Drupal\user\User $user = NULL);
+
+  /**
+   * Checks 'delete' access for a given entity or entity translation.
+   *
+   * @param EntityInterface $entity
+   *   The entity to check 'delete' access for.
+   * @param string $langcode
+   *   (optional) The language code for which to check access for. Defaults to
+   *   LANGUAGE_DEFAULT.
+   * @param \Drupal\user\User $user
+   *   (optional) The user for whom to check 'delete' access for. Defaults to
+   *   the current user.
+   *
+   * @return bool
+   *   TRUE if access was granted, FALSE otherwise.
+   */
+  public function deleteAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, \Drupal\user\User $user = NULL);
+}
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php b/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php
index 0db4657..3bc2cd2 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php
@@ -233,8 +233,17 @@ public function isEmpty() {
   /**
    * Implements AccessibleInterface::access().
    */
-  public function access(\Drupal\user\User $account = NULL) {
-    // @todo implement
+  public function access($operation = 'view', \Drupal\user\User $user = NULL) {
+    if (!$controller = entity_access_controller($this->parent->entityType())) {
+      // If no access controller is defined the entity translation is fully
+      // accessible by every user.
+      return FALSE;
+    }
+    if ($operation == 'edit') {
+      // Map the 'edit' operation to 'update'.
+      $operation = 'update';
+    }
+    return $controller->{"{$operation}Access"}($this->parent, $this->langcode, $user);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/Field.php b/core/lib/Drupal/Core/Entity/Field/Type/Field.php
index f4b4e5c..fcf59dd 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/Field.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/Field.php
@@ -297,7 +297,7 @@ public function __clone() {
   /**
    * Implements AccessibleInterface::access().
    */
-  public function access(User $account = NULL) {
+  public function access($operation = 'view', User $user = NULL) {
     // TODO: Implement access() method. Use item access.
   }
 }
diff --git a/core/lib/Drupal/Core/TypedData/AccessibleInterface.php b/core/lib/Drupal/Core/TypedData/AccessibleInterface.php
index 669f1d7..94bb8e7 100644
--- a/core/lib/Drupal/Core/TypedData/AccessibleInterface.php
+++ b/core/lib/Drupal/Core/TypedData/AccessibleInterface.php
@@ -15,12 +15,15 @@
   /**
    * Checks data value access.
    *
+   * @param string $operation
+   *   (optional) The operation to be performed. Defaults to 'view'.
    * @param \Drupal\user\User $account
    *   (optional) The user account to check access for. Defaults to the current
    *   user.
    *
    * @return bool
-   *   TRUE if the given user has access; otherwise FALSE.
+   *   TRUE if the given user has access for the given operation, FALSE
+   *   otherwise.
    */
-  public function access(\Drupal\user\User $account = NULL);
+  public function access($operation = 'view', \Drupal\user\User $user = NULL);
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php
new file mode 100644
index 0000000..2d9b10b
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php
@@ -0,0 +1,118 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\Entity\EntityAccessTest.
+ */
+
+namespace Drupal\system\Tests\Entity;
+
+use Drupal\simpletest\WebTestBase;
+use Drupal\Core\Language\Language;
+use Drupal\Core\TypedData\AccessibleInterface;
+use Drupal\user\User;
+
+/**
+ * Tests the entity access controller.
+ */
+class EntityAccessTest extends WebTestBase  {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Entity access',
+      'description' => 'Tests entity access.',
+      'group' => 'Entity API',
+    );
+  }
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('entity_test', 'entity_access_test');
+
+  /**
+   * Asserts entity access correctly grants or denies access.
+   */
+  function assertEntityAccess($ops, AccessibleInterface $object, User $user = NULL) {
+    foreach ($ops as $op => $result) {
+      $msg = t("Entity access returns @result with operation '@op'.", array('@result' => $result ? 'true' : 'false', '@op' => $op));
+      $this->assertEqual($result, $object->access($op, $user), $msg);
+    }
+  }
+
+  /**
+   * Ensures entity access is properly working.
+   */
+  function testEntityAccess() {
+    // Set up a non-admin user that is allowed to view test entities.
+    $user = $this->drupalCreateUser(array('view test entity'));
+    $this->drupalLogin($user);
+
+    $entity = entity_create('entity_test', array(
+      'name' => 'test',
+    ));
+    $entity->save();
+
+    // The current user is allowed to view, create, update and delete entities.
+    $this->assertEntityAccess(array(
+      'create' => TRUE,
+      'update' => TRUE,
+      'delete' => TRUE,
+      'view' => TRUE,
+    ), $entity);
+
+    // The custom user is not allowed to view test entities.
+    $user = $this->drupalCreateUser();
+    $this->assertEntityAccess(array(
+      'create' => TRUE,
+      'update' => TRUE,
+      'delete' => TRUE,
+      'view' => FALSE,
+    ), $entity, $user);
+
+    // Check that entity types without any access controller are fully
+    // protected.
+    $entity = entity_create('entity_access_test', array());
+    $this->assertEntityAccess(array(
+      'create' => FALSE,
+      'update' => FALSE,
+      'delete' => FALSE,
+      'view' => FALSE,
+    ), $entity);
+  }
+
+  /**
+   * Ensures entity access for entity translations is properly working.
+   */
+  function testEntityTranslationAccess() {
+    // Enable translations for the test entity type.
+    variable_set('entity_test_translation', TRUE);
+    module_enable(array('locale'));
+
+    // Set up a non-admin user that is allowed to view test entity translations.
+    $user = $this->drupalCreateUser(array('view test entity translations'));
+    $this->drupalLogin($user);
+
+    // Create two test languages.
+    foreach (array('foo', 'bar') as $langcode) {
+      $language = new Language(array(
+        'langcode' => $langcode,
+        'name' => $this->randomString(),
+      ));
+      language_save($language);
+    }
+
+    $entity = entity_create('entity_test', array(
+      'name' => 'test',
+      'langcode' => 'foo',
+    ));
+    $entity->save();
+
+    $translation = $entity->getTranslation('bar');
+    $this->assertEntityAccess(array(
+      'view' => TRUE,
+    ), $translation);
+  }
+}
diff --git a/core/modules/system/tests/modules/entity_access_test/entity_access_test.info b/core/modules/system/tests/modules/entity_access_test/entity_access_test.info
new file mode 100644
index 0000000..d285089
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_access_test/entity_access_test.info
@@ -0,0 +1,6 @@
+name = "Entity access test"
+description = "Support module for testing entity access."
+package = Testing
+version = VERSION
+core = 8.x
+hidden = TRUE
diff --git a/core/modules/system/tests/modules/entity_access_test/entity_access_test.module b/core/modules/system/tests/modules/entity_access_test/entity_access_test.module
new file mode 100644
index 0000000..574efab
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_access_test/entity_access_test.module
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * @file
+ * Helper module for entity access tests.
+ */
+
+/**
+ * Implements hook_permission().
+ */
+function entity_access_test_permission() {
+  return array(
+    'view test entity' => array(
+      'title' => t('View test entities'),
+    ),
+    'view test entity translations' => array(
+      'title' => t('View translations of test entities'),
+    ),
+  );
+}
+
+/**
+ * Implements hook_entity_info().
+ */
+function entity_access_test_entity_info() {
+  return array(
+    'entity_access_test' => array(
+      'label' => 'Entity without access controller',
+      'entity keys' => array(
+        'id' => 'id',
+      ),
+    ),
+  );
+}
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module
index 46def82..ba87cd1 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.module
+++ b/core/modules/system/tests/modules/entity_test/entity_test.module
@@ -13,6 +13,7 @@ function entity_test_entity_info() {
     'label' => t('Test entity'),
     'entity class' => 'Drupal\entity_test\EntityTest',
     'controller class' => 'Drupal\entity_test\EntityTestStorageController',
+    'access controller class' => 'Drupal\entity_test\EntityTestAccessController',
     'form controller class' => array(
       'default' => 'Drupal\entity_test\EntityTestFormController',
     ),
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestAccessController.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestAccessController.php
new file mode 100644
index 0000000..ced2a42
--- /dev/null
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/EntityTestAccessController.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\entity_test\EntityTestAccessController.
+ */
+
+namespace Drupal\entity_test;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityAccessControllerInterface;
+use Drupal\user\User;
+
+/**
+ * Access controller for the test entity.
+ */
+class EntityTestAccessController implements EntityAccessControllerInterface {
+
+  /**
+   * Implements EntityAccessInterface::view().
+   */
+  public function viewAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $user = NULL) {
+    if ($langcode != LANGUAGE_DEFAULT) {
+      return user_access('view test entity translations', $user);
+    }
+    return user_access('view test entity', $user);
+  }
+
+  /**
+   * Implements EntityAccessInterface::create().
+   */
+  public function createAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $user = NULL) {
+    return TRUE;
+  }
+
+  /**
+   * Implements EntityAccessInterface::update().
+   */
+  public function updateAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $user = NULL) {
+    return TRUE;
+  }
+
+  /**
+   * Implements EntityAccessInterface::delete().
+   */
+  public function deleteAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $user = NULL) {
+    return TRUE;
+  }
+}
