diff --git a/core/includes/entity.api.php b/core/includes/entity.api.php
index 7c9c539..190658b 100644
--- a/core/includes/entity.api.php
+++ b/core/includes/entity.api.php
@@ -512,3 +512,49 @@ function hook_entity_field_info_alter(&$info, $entity_type) {
     $info['definitions']['mymodule_text']['class'] = '\Drupal\anothermodule\EntityComputedText';
   }
 }
+
+/**
+ * Control access to fields.
+ *
+ * This hook is invoked from \Drupal\Core\Entity\Field\Type\Field::access() to
+ * let modules grant or deny operations on fields.
+ *
+ * @param string $operation
+ *   The operation to be performed. See
+ *   \Drupal\Core\TypedData\AccessibleInterface::access() for possible values.
+ * @param \Drupal\Core\Entity\Field\Type\Field $field
+ *   The entity field object on which the operation is to be performed.
+ * @param \Drupal\user\Plugin\Core\Entity\User $account
+ *   The user account to check.
+ *
+ * @return bool|NULL
+ *   TRUE if access hould be allowed, FALSE if access should be denied and NULL
+ *   if the implementation has no opinion.
+ */
+function hook_entity_field_access($operation, $field, $account) {
+  if ($field->getName() == 'field_of_interest' && $operation == 'edit') {
+    return user_access('edit field of interest', $account);
+  }
+}
+
+/**
+ * Alters the default access behaviour for a given field.
+ *
+ * @param array $grants
+ *   An array of grants gathered by hook_entity_field_access(). The array is
+ *   keyed by the module that defines the field's access control; the values are
+ *   grant responses for each module (Boolean or NULL).
+ * @param array $context
+ *   Context array on the performed operation with the following keys:
+ *   - operation: The operation to be performed (string).
+ *   - field: The entity field object (\Drupal\Core\Entity\Field\Type\Field).
+ *   - account: The user account to check access for
+ *     (Drupal\user\Plugin\Core\Entity\User).
+ */
+function hook_entity_field_access_alter(array &$grants, array $context) {
+  $field = $context['field'];
+  if ($field->getName() == 'field_of_interest' && $grants['node'] === FALSE) {
+    // Override node module's restriction to no opinion.
+    $grants['node'] = NULL;
+  }
+}
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/Field.php b/core/lib/Drupal/Core/Entity/Field/Type/Field.php
index 49418a0..260e1fd 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/Field.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/Field.php
@@ -289,6 +289,48 @@ public function __clone() {
    * Implements \Drupal\Core\TypedData\AccessibleInterface::access().
    */
   public function access($operation = 'view', User $account = NULL) {
-    // TODO: Implement access() method. Use item access.
+    global $user;
+    if (!isset($account) && $user->uid) {
+      $account = user_load($user->uid);
+    }
+    $access = $this->defaultAccess($operation, $account);
+    // Invoke hook and collect grants/denies for field access. Our default
+    // access flag is masked under the umbrella of the system module.
+    $grants = array('system' => $access);
+    foreach (module_implements('entity_field_access') as $module) {
+      $grants = array_merge($grants, array($module => module_invoke($module, 'entity_field_access', $operation, $this, $account)));
+    }
+    // Allow other modules to alter the returned grants/denies.
+    $context = array(
+      'operation' => $operation,
+      'field' => $this,
+      'account' => $account,
+    );
+    drupal_alter('entity_field_access', $grants, $context);
+
+    // One grant being FALSE is enough to deny access immediately.
+    if (in_array(FALSE, $grants, TRUE)) {
+      return FALSE;
+    }
+    // At least one grant has the explicit opinion to allow access.
+    if (in_array(TRUE, $grants, TRUE)) {
+      return TRUE;
+    }
+    // All grants are NULL and have no opinion - deny access in that case.
+    return FALSE;
+  }
+
+  /**
+   * Contains the default access logic of this field.
+   *
+   * See \Drupal\Core\TypedData\AccessibleInterface::access() for the parameter
+   * doucmentation.
+   *
+   * @return bool
+   *   TRUE if access to this field is allowed per default, FALSE otherwise.
+   */
+  public function defaultAccess($operation = 'view', User $account = NULL) {
+    // Grant access per default.
+    return TRUE;
   }
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldAccessTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldAccessTest.php
new file mode 100644
index 0000000..f6f4e69
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldAccessTest.php
@@ -0,0 +1,62 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Entity\FieldAccessTest.
+ */
+
+namespace Drupal\system\Tests\Entity;
+
+use Drupal\simpletest\DrupalUnitTestBase;
+
+/**
+ * Tests the functionality of field access.
+ */
+class FieldAccessTest extends DrupalUnitTestBase {
+
+  /**
+   * Modules to load code from (no schema installation needed).
+   *
+   * @var array
+   */
+  public static $modules = array('field_sql_storage', 'system', 'text');
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Field access tests',
+      'description' => 'Test Field level access hooks.',
+      'group' => 'Entity API',
+    );
+  }
+
+  protected function setUp() {
+    parent::setUp();
+    // Install field and user module schema, register entity_test text field.
+    $this->enableModules(array('field', 'entity_test', 'user'));
+  }
+
+  /**
+   * Tests hook_entity_field_access() and hook_entity_field_access_alter().
+   *
+   * @see entity_test_entity_field_access()
+   * @see entity_test_entity_field_access_alter()
+   */
+  function testFieldAccess() {
+    $values = array(
+      'name' => $this->randomName(),
+      'user_id' => 1,
+      'field_test_text' => array(
+        'value' => 'no access value',
+        'format' => 'full_html',
+      ),
+    );
+    $entity = entity_create('entity_test', $values);
+    $this->assertFalse($entity->field_test_text->access('view'), 'Access to the field was denied.');
+
+    $entity->field_test_text = 'access alter value';
+    $this->assertFalse($entity->field_test_text->access('view'), 'Access to the field was denied.');
+
+    $entity->field_test_text = 'standard value';
+    $this->assertTrue($entity->field_test_text->access('view'), 'Access to the field was granted.');
+  }
+}
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 2752729..2594e78 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.module
+++ b/core/modules/system/tests/modules/entity_test/entity_test.module
@@ -251,3 +251,26 @@ function entity_test_entity_test_insert($entity) {
     throw new Exception("Test exception rollback.");
   }
 }
+
+/**
+ * Implements hook_entity_field_access().
+ *
+ * @see \Drupal\system\Tests\Entity\FieldAccessTest::testFieldAccess()
+ */
+function entity_test_entity_field_access($operation, $field, $account) {
+  if ($field->getName() == 'field_test_text' && $field->value == 'no access value') {
+    return FALSE;
+  }
+}
+
+/**
+ * Implements hook_entity_field_access_alter().
+ *
+ * @see \Drupal\system\Tests\Entity\FieldAccessTest::testFieldAccess()
+ */
+function entity_test_entity_field_access_alter(array &$grants, array $context) {
+  $field = $context['field'];
+  if ($field->getName() == 'field_test_text' && $field->value == 'access alter value') {
+    $grants['system'] = FALSE;
+  }
+}
