diff --git a/core/includes/entity.inc b/core/includes/entity.inc
index 03c3ffa..89bc9f5 100644
--- a/core/includes/entity.inc
+++ b/core/includes/entity.inc
@@ -288,6 +288,27 @@ 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
+ *   An entity access controller instance.
+ */
+function entity_access_controller($entity_type) {
+  $controllers = &drupal_static(__FUNCTION__, array());
+  if (!isset($controllers[$entity_type])) {
+    $type_info = entity_get_info($entity_type);
+    $class = $type_info['access_controller_class'];
+    $controllers[$entity_type] = new $class($entity_type);
+  }
+  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 1d56590..295b7c7 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -250,9 +250,17 @@ public function getIterator() {
 
   /**
    * Implements AccessibleInterface::access().
-   */
-  public function access(\Drupal\user\User $account = NULL) {
-    // TODO: Implement access() method.
+   *
+   * In addition to the access operations 'view' and 'edit' as defined by
+   * \Drupal\Core\TypedData\AccessibleInterface::access() the implementation
+   * supports the access operations 'create', 'update' and 'delete'.
+   */
+  public function access($operation = 'view', \Drupal\user\Plugin\Core\Entity\User $account = NULL) {
+    if ($operation == 'edit') {
+      // Map the 'edit' operation to 'update'.
+      $operation = 'update';
+    }
+    return entity_access_controller($this->entityType)->{"{$operation}Access"}($this, LANGUAGE_DEFAULT, $account);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/EntityAccessController.php b/core/lib/Drupal/Core/Entity/EntityAccessController.php
new file mode 100644
index 0000000..ae59203
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/EntityAccessController.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Entity\EntityAccessController.
+ */
+
+namespace Drupal\Core\Entity;
+
+use Drupal\user\Plugin\Core\Entity\User;
+
+/**
+ * Base class for entity access controllers.
+ *
+ * Defaults to FALSE (access denied) for 'view', 'create', 'update' and 'delete'
+ * access checks.
+ */
+class EntityAccessController implements EntityAccessControllerInterface {
+
+  /**
+   * Implements \Drupal\Core\Entity\EntityAccessControllerInterface::viewAccess().
+   */
+  public function viewAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+    return FALSE;
+  }
+
+  /**
+   * Implements \Drupal\Core\Entity\EntityAccessControllerInterface::createAccess().
+   */
+  public function createAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+    return FALSE;
+  }
+
+  /**
+   * Implements \Drupal\Core\Entity\EntityAccessControllerInterface::updateAccess().
+   */
+  public function updateAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+    return FALSE;
+  }
+
+  /**
+   * Implements \Drupal\Core\Entity\EntityAccessControllerInterface::deleteAccess().
+   */
+  public function deleteAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+    return FALSE;
+  }
+}
diff --git a/core/lib/Drupal/Core/Entity/EntityAccessControllerInterface.php b/core/lib/Drupal/Core/Entity/EntityAccessControllerInterface.php
new file mode 100644
index 0000000..d9d2500
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/EntityAccessControllerInterface.php
@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Entity\EntityAccessControllerInterface.
+ */
+
+namespace Drupal\Core\Entity;
+
+// @todo Don't depend on module level code.
+use Drupal\user\Plugin\Core\Entity\User;
+
+/**
+ * Defines a common interface for entity access controller classes.
+ */
+interface EntityAccessControllerInterface {
+
+  /**
+   * Checks 'view' access for a given entity or entity translation.
+   *
+   * @param \Drupal\Core\Entity\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\Plugin\Core\Entity\User $account
+   *   (optional) The user for whom to check access for, or NULL to check access
+   *   for the current user. Defaults to NULL.
+   *
+   * @return bool
+   *   TRUE if access was granted, FALSE otherwise.
+   */
+  public function viewAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL);
+
+  /**
+   * Checks 'create' access for a given entity or entity translation.
+   *
+   * @param \Drupal\Core\Entity\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\Plugin\Core\Entity\User $account
+   *   (optional) The user for whom to check access for, or NULL to check access
+   *   for the current user. Defaults to NULL.
+   *
+   * @return bool
+   *   TRUE if access was granted, FALSE otherwise.
+   */
+  public function createAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL);
+
+  /**
+   * Checks 'update' access for a given entity or entity translation.
+   *
+   * @param \Drupal\Core\Entity\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\Plugin\Core\Entity\User $account
+   *   (optional) The user for whom to check access for, or NULL to check access
+   *   for the current user. Defaults to NULL.
+   *
+   * @return bool
+   *   TRUE if access was granted, FALSE otherwise.
+   */
+  public function updateAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL);
+
+  /**
+   * Checks 'delete' access for a given entity or entity translation.
+   *
+   * @param \Drupal\Core\Entity\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\Plugin\Core\Entity\User $account
+   *   (optional) The user for whom to check access for, or NULL to check access
+   *   for the current user. Defaults to NULL.
+   *
+   * @return bool
+   *   TRUE if access was granted, FALSE otherwise.
+   */
+  public function deleteAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL);
+}
diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php
index 9b751d4..bf4733d 100644
--- a/core/lib/Drupal/Core/Entity/EntityInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityInterface.php
@@ -16,6 +16,10 @@
  *
  * When implementing this interface which extends Traversable, make sure to list
  * IteratorAggregate or Iterator before this interface in the implements clause.
+ *
+ * In addition to the access operations 'view' and 'edit' as supported by
+ * \Drupal\Core\TypedData\AccessibleInterface::access() the implementation has
+ * to support the access operations 'create', 'update' and 'delete'.
  */
 interface EntityInterface extends ComplexDataInterface, AccessibleInterface, TranslatableInterface {
 
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index b8c38ce..f1deb4c 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -59,6 +59,10 @@
  *   Drupal\Core\Entity\EntityListController.
  * - render_controller_class: The name of the class that is used to render the
  *   entities. Defaults to Drupal\Core\Entity\EntityRenderController.
+ * - render_controller_class: The name of the class that is used for access
+ *   checks. The class must implement
+ *   Drupal\Core\Entity\EntityAccessControllerInterface. Defaults to
+ *   Drupal\Core\Entity\EntityAccessController.
  * - static_cache: (optional) Boolean indicating whether entities should be
  *   statically cached during a page request. Used by
  *   Drupal\Core\Entity\DatabaseStorageController. Defaults to TRUE.
@@ -197,6 +201,7 @@ class EntityManager extends PluginManagerBase {
     ),
     'list_controller_class' => 'Drupal\Core\Entity\EntityListController',
     'render_controller_class' => 'Drupal\Core\Entity\EntityRenderController',
+    'access_controller_class' => 'Drupal\Core\Entity\EntityAccessController',
     'static_cache' => TRUE,
     'translation' => array(),
     'bundles' => array(),
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php b/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php
index 0db4657..8d58ff2 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/EntityTranslation.php
@@ -233,8 +233,12 @@ public function isEmpty() {
   /**
    * Implements AccessibleInterface::access().
    */
-  public function access(\Drupal\user\User $account = NULL) {
-    // @todo implement
+  public function access($operation = 'view', \Drupal\user\Plugin\Core\Entity\User $account = NULL) {
+    if ($operation == 'edit') {
+      // Map the 'edit' operation to 'update'.
+      $operation = 'update';
+    }
+    return entity_access_controller($this->parent->entityType())->{"{$operation}Access"}($this->parent, $this->langcode, $account);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/Field.php b/core/lib/Drupal/Core/Entity/Field/Type/Field.php
index e7a5960..cc5951c 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/Field.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/Field.php
@@ -10,7 +10,7 @@
 use Drupal\Core\Entity\Field\FieldInterface;
 use Drupal\Core\TypedData\TypedDataInterface;
 use Drupal\Core\TypedData\Type\TypedData;
-use Drupal\user\User;
+use Drupal\user\Plugin\Core\Entity\User;
 use ArrayIterator;
 use IteratorAggregate;
 use InvalidArgumentException;
@@ -296,7 +296,7 @@ public function __clone() {
   /**
    * Implements AccessibleInterface::access().
    */
-  public function access(User $account = NULL) {
+  public function access($operation = 'view', User $account = 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..e12d77a 100644
--- a/core/lib/Drupal/Core/TypedData/AccessibleInterface.php
+++ b/core/lib/Drupal/Core/TypedData/AccessibleInterface.php
@@ -15,12 +15,20 @@
   /**
    * Checks data value access.
    *
-   * @param \Drupal\user\User $account
-   *   (optional) The user account to check access for. Defaults to the current
-   *   user.
+   * @param string $operation
+   *   (optional) The operation to be performed. Possible values are:
+   *   - edit
+   *   - view
+   *   Defaults to 'view'.
+   * @param \Drupal\user\Plugin\Core\Entity\User $account
+   *   (optional) The user for whom to check access for, or NULL to check access
+   *   for the current user. Defaults to NULL.
    *
    * @return bool
-   *   TRUE if the given user has access; otherwise FALSE.
+   *   TRUE if the given user has access for the given operation, FALSE
+   *   otherwise.
+   *
+   * @todo Don't depend on module level code.
    */
-  public function access(\Drupal\user\User $account = NULL);
+  public function access($operation = 'view', \Drupal\user\Plugin\Core\Entity\User $account = 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..9958b62
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php
@@ -0,0 +1,133 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\system\Tests\Entity\EntityAccessTest.
+ */
+
+namespace Drupal\system\Tests\Entity;
+
+use Drupal\Core\Language\Language;
+use Drupal\Core\TypedData\AccessibleInterface;
+use Drupal\simpletest\WebTestBase;
+use Drupal\user\Plugin\Core\Entity\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');
+
+  /**
+   * Asserts entity access correctly grants or denies access.
+   */
+  function assertEntityAccess($ops, AccessibleInterface $object, User $account = NULL) {
+    foreach ($ops as $op => $result) {
+      $message = format_string("Entity access returns @result with operation '@op'.", array(
+        '@result' => isset($result) ? 'null' : ($result ? 'true' : 'false'),
+        '@op' => $op,
+      ));
+
+      $this->assertEqual($result, $object->access($op, $account), $message);
+    }
+  }
+
+  /**
+   * 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.
+    $custom_user = $this->drupalCreateUser();
+    $this->assertEntityAccess(array(
+      'create' => TRUE,
+      'update' => TRUE,
+      'delete' => TRUE,
+      'view' => FALSE,
+    ), $entity, $custom_user);
+  }
+
+  /**
+   * Ensures that the default controller is used as a fallback.
+   */
+  function testEntityAccessDefaultController() {
+    // Remove the access controller definition from the test entity.
+    variable_set('entity_test_default_access_controller', TRUE);
+
+    // Check that the default access controller is used for entities that don't
+    // have a specific access controller defined.
+    $controller = entity_access_controller('entity_test');
+    $this->assertTrue(is_a($controller, '\Drupal\Core\Entity\EntityAccessController'), 'The default entity controller is used for the entity_test entity type.');
+
+    $entity = entity_create('entity_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_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module
index 43daa4a..b35553b 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,10 @@ function entity_test_entity_info_alter(&$info) {
   if (variable_get('entity_test_translation')) {
     $info['entity_test']['translation']['entity_test'] = TRUE;
   }
+  // Optionally unset the access controller to test the fallback.
+  if (variable_get('entity_test_default_access_controller')) {
+    unset($info['entity_test']['access_controller_class']);
+  }
 }
 
 /**
@@ -24,6 +28,12 @@ function entity_test_permission() {
       'title' => t('Administer entity_test content'),
       'description' => t('Manage entity_test content'),
     ),
+    'view test entity' => array(
+      'title' => t('View test entities'),
+    ),
+    'view test entity translations' => array(
+      'title' => t('View translations of test entities'),
+    ),
   );
   return $permissions;
 }
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..186bafa
--- /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\Plugin\Core\Entity\User;
+
+/**
+ * Access controller for the test entity.
+ */
+class EntityTestAccessController implements EntityAccessControllerInterface {
+
+  /**
+   * Implements EntityAccessInterface::view().
+   */
+  public function viewAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+    if ($langcode != LANGUAGE_DEFAULT) {
+      return user_access('view test entity translations', $account);
+    }
+    return user_access('view test entity', $account);
+  }
+
+  /**
+   * Implements EntityAccessInterface::create().
+   */
+  public function createAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+    return TRUE;
+  }
+
+  /**
+   * Implements EntityAccessInterface::update().
+   */
+  public function updateAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+    return TRUE;
+  }
+
+  /**
+   * Implements EntityAccessInterface::delete().
+   */
+  public function deleteAccess(EntityInterface $entity, $langcode = LANGUAGE_DEFAULT, User $account = NULL) {
+    return TRUE;
+  }
+}
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTest.php
index 240c5c7..80d2166 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTest.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTest.php
@@ -19,6 +19,7 @@
  *   label = @Translation("Test entity"),
  *   module = "entity_test",
  *   controller_class = "Drupal\entity_test\EntityTestStorageController",
+ *   access_controller_class = "Drupal\entity_test\EntityTestAccessController",
  *   form_controller_class = {
  *     "default" = "Drupal\entity_test\EntityTestFormController"
  *   },
