diff --git a/core/includes/entity.api.php b/core/includes/entity.api.php
index 7c9c539..9144759 100644
--- a/core/includes/entity.api.php
+++ b/core/includes/entity.api.php
@@ -512,3 +512,27 @@ function hook_entity_field_info_alter(&$info, $entity_type) {
     $info['definitions']['mymodule_text']['class'] = '\Drupal\anothermodule\EntityComputedText';
   }
 }
+
+/**
+ * Alters the default access behaviour for a given field.
+ *
+ * This hook is invoked from \Drupal\Core\Entity\Field\Type\Field::access() to
+ * let modules alter access to operations on fields. As soon as one hook
+ * implementation changes $access to FALSE the access() method will return FALSE
+ * and the remaining hook implementations will not be invoked.
+ *
+ * @param bool $access
+ *   Access flag reference that can be altered. TRUE if the operation is
+ *   allowed, and FALSE if the operation is denied.
+ * @param string $operation
+ *   The operation to be performed. Possible values: 'edit', 'view'.
+ * @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.
+ */
+function hook_entity_field_access_alter(&$access, $operation, $field, $account) {
+  if ($field->getName() == 'field_of_interest' && $operation == 'edit') {
+    $access = user_access('edit field of interest', $account);
+  }
+}
diff --git a/core/lib/Drupal/Core/Entity/EntityNG.php b/core/lib/Drupal/Core/Entity/EntityNG.php
index e16c6fb..ff80133 100644
--- a/core/lib/Drupal/Core/Entity/EntityNG.php
+++ b/core/lib/Drupal/Core/Entity/EntityNG.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Entity;
 
+use Drupal\Core\Language\Language;
 use Drupal\Core\TypedData\ContextAwareInterface;
 use Drupal\Core\TypedData\TypedDataInterface;
 use Drupal\Component\Uuid\Uuid;
diff --git a/core/lib/Drupal/Core/Entity/Field/Type/Field.php b/core/lib/Drupal/Core/Entity/Field/Type/Field.php
index 34baa61..60ff158 100644
--- a/core/lib/Drupal/Core/Entity/Field/Type/Field.php
+++ b/core/lib/Drupal/Core/Entity/Field/Type/Field.php
@@ -281,6 +281,23 @@ 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)) {
+      $account = $user;
+    }
+    // Grant access per default.
+    $access = TRUE;
+    // We do not use drupal_alter() here because it only allows two context
+    // parameters which would make the signature of the hook very ugly.
+    foreach (module_implements('entity_field_access_alter') as $module) {
+      $function = $module . '_entity_field_access_alter';
+      $function($access, $operation, $this, $account);
+      // If a hook implementation altered $access to FALSE we can return
+      // immediately.
+      if ($access === FALSE) {
+        return FALSE;
+      }
+    }
+    return $access;
   }
 }
diff --git a/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityWrapper.php b/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityWrapper.php
index a2e49e3..f96f114 100644
--- a/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityWrapper.php
+++ b/core/modules/jsonld/lib/Drupal/jsonld/JsonldEntityWrapper.php
@@ -111,6 +111,7 @@ public function getTypeUri() {
   public function getProperties() {
     // Properties to skip.
     $skip = array('id');
+    $properties = array();
 
     // Create language map property structure.
     foreach ($this->entity->getTranslationLanguages() as $langcode => $language) {
diff --git a/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php b/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php
index 3f45421..83214b8 100644
--- a/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php
+++ b/core/modules/rest/lib/Drupal/rest/Plugin/rest/resource/EntityResource.php
@@ -13,6 +13,7 @@
 use Drupal\Core\Entity\EntityStorageException;
 use Drupal\rest\Plugin\ResourceBase;
 use Drupal\rest\ResourceResponse;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
 use Symfony\Component\HttpKernel\Exception\HttpException;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -44,6 +45,11 @@ public function get($id) {
     $definition = $this->getDefinition();
     $entity = entity_load($definition['entity_type'], $id);
     if ($entity) {
+      foreach ($entity as $field_name => $field) {
+        if (!$field->access('view')) {
+          unset($entity->{$field_name});
+        }
+      }
       return new ResourceResponse($entity);
     }
     throw new NotFoundHttpException(t('Entity with ID @id not found', array('@id' => $id)));
@@ -74,6 +80,11 @@ public function post($id, EntityInterface $entity) {
     if (!$entity->isNew()) {
       throw new BadRequestHttpException();
     }
+    foreach ($entity as $field_name => $field) {
+      if (!$field->access('create')) {
+        throw new AccessDeniedHttpException(t('Access denied on creating field @field.', array('@field' => $field_name)));
+      }
+    }
     try {
       $entity->save();
       $url = url(strtr($this->plugin_id, ':', '/') . '/' . $entity->id(), array('absolute' => TRUE));
@@ -109,6 +120,18 @@ public function put($id, EntityInterface $entity) {
     if ($original_entity == FALSE) {
       throw new NotFoundHttpException();
     }
+    foreach ($entity as $field_name => $field) {
+      if (isset($entiy->{$field_name})) {
+        if ($entiy->{$field_name} != $original_entity->{$field_name} && !$original_entity->{$field_name}->access('update')) {
+          throw new AccessDeniedHttpException(t('Access denied on updating field @field.', array('@field' => $field_name)));
+        }
+      }
+      else {
+        if (isset($original_entity->{$field_name}) && !$original_entity->{$field_name}->access('delete')) {
+          throw new AccessDeniedHttpException(t('Access denied on deleting field @field.', array('@field' => $field_name)));
+        }
+      }
+    }
     $info = $entity->entityInfo();
     // Make sure that the entity ID is the one provided in the URL.
     $entity->{$info['entity_keys']['id']} = $id;
@@ -150,9 +173,19 @@ public function patch($id, EntityInterface $entity) {
       throw new NotFoundHttpException();
     }
     // Overwrite the received properties.
-    foreach ($entity->getProperties() as $name => $property) {
-      if (isset($entity->{$name})) {
-        $original_entity->{$name} = $property;
+    foreach ($entity as $field_name => $field) {
+      if (isset($entity->{$field_name})) {
+        if (empty($entity->{$field_name})) {
+          if (!$original_entity->{$field_name}->access('delete')) {
+            throw new AccessDeniedHttpException(t('Access denied on deleting field @field.', array('@field' => $field_name)));
+          }
+        }
+        else {
+          if (!$original_entity->{$field_name}->access('update')) {
+            throw new AccessDeniedHttpException(t('Access denied on updating field @field.', array('@field' => $field_name)));
+          }
+        }
+        $original_entity->{$field_name} = $field;
       }
     }
     try {
diff --git a/core/modules/rest/lib/Drupal/rest/Tests/CreateTest.php b/core/modules/rest/lib/Drupal/rest/Tests/CreateTest.php
index c5b298a..26bd7aa 100644
--- a/core/modules/rest/lib/Drupal/rest/Tests/CreateTest.php
+++ b/core/modules/rest/lib/Drupal/rest/Tests/CreateTest.php
@@ -69,6 +69,19 @@ public function testCreate() {
     }
 
     $loaded_entity->delete();
+
+    // Try to create an entity with an access protected field.
+    // @see entity_test_entity_field_access_alter()
+    $entity->field_test_text->value = 'no access value';
+    $serialized = $serializer->serialize($entity, 'drupal_jsonld');
+    $this->httpRequest('entity/' . $entity_type, 'POST', $serialized, 'application/vnd.drupal.ld+json');
+    $this->assertResponse(403);
+    $this->assertFalse(entity_load_multiple($entity_type, NULL, TRUE), 'No entity has been created in the database.');
+
+    // Restore the valid test value.
+    $entity->field_test_text->value = $entity_values['field_test_text'][0]['value'];
+    $serialized = $serializer->serialize($entity, 'drupal_jsonld');
+
     // Try to create an entity without the CSRF token.
     $this->curlExec(array(
       CURLOPT_HTTPGET => FALSE,
diff --git a/core/modules/rest/lib/Drupal/rest/Tests/ReadTest.php b/core/modules/rest/lib/Drupal/rest/Tests/ReadTest.php
index 0578fc3..4955621 100644
--- a/core/modules/rest/lib/Drupal/rest/Tests/ReadTest.php
+++ b/core/modules/rest/lib/Drupal/rest/Tests/ReadTest.php
@@ -69,6 +69,17 @@ public function testRead() {
       $this->assertResponse(404);
       $this->assertEqual($response, 'Entity with ID 9999 not found', 'Response message is correct.');
 
+      // Make sure that field level access works and that the according field is
+      // not available in the response.
+      // @see entity_test_entity_field_access_alter()
+      $entity->field_test_text->value = 'no access value';
+      $entity->save();
+      $response = $this->httpRequest('entity/' . $entity_type . '/' . $entity->id(), 'GET', NULL, 'application/vnd.drupal.ld+json');
+      $this->assertResponse(200);
+      $this->assertHeader('content-type', 'application/vnd.drupal.ld+json');
+      $data = drupal_json_decode($response);
+      $this->assertFalse(isset($data['field_test_text']), 'Field access protexted field is not visible in the response.');
+
       // Try to read an entity without proper permissions.
       $this->drupalLogout();
       $response = $this->httpRequest('entity/' . $entity_type . '/' . $entity->id(), 'GET', NULL, 'application/vnd.drupal.ld+json');
diff --git a/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php b/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php
index fa65a2f..2ebcda3 100644
--- a/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php
+++ b/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php
@@ -76,6 +76,32 @@ public function testPatchUpdate() {
     $entity = entity_load($entity_type, $entity->id(), TRUE);
     $this->assertNull($entity->field_test_text->value, 'Test field has been cleared.');
 
+    // Enable access protection for the text field.
+    // @see entity_test_entity_field_access_alter()
+    $entity->field_test_text->value = 'no access value';
+    $entity->save();
+
+    // Try to empty a field that is access protected.
+    $this->httpRequest('entity/' . $entity_type . '/' . $entity->id(), 'PATCH', $serialized, 'application/vnd.drupal.ld+json');
+    $this->assertResponse(403);
+
+    // Re-load the entity from the database.
+    $entity = entity_load($entity_type, $entity->id(), TRUE);
+    $this->assertEqual($entity->field_test_text->value, 'no access value', 'Text field was not updated.');
+
+    // Try to update an access protected field.
+    $serialized = $serializer->serialize($patch_entity, 'drupal_jsonld');
+    $this->httpRequest('entity/' . $entity_type . '/' . $entity->id(), 'PATCH', $serialized, 'application/vnd.drupal.ld+json');
+    $this->assertResponse(403);
+
+    // Re-load the entity from the database.
+    $entity = entity_load($entity_type, $entity->id(), TRUE);
+    $this->assertEqual($entity->field_test_text->value, 'no access value', 'Text field was not updated.');
+
+    // Restore the valid test value.
+    $entity->field_test_text->value = $this->randomString();
+    $entity->save();
+
     // Try to update a non-existing entity with ID 9999.
     $this->httpRequest('entity/' . $entity_type . '/9999', 'PATCH', $serialized, 'application/vnd.drupal.ld+json');
     $this->assertResponse(404);
@@ -146,6 +172,33 @@ public function testPutUpdate() {
     $entity = entity_load($entity_type, $entity->id(), TRUE);
     $this->assertTrue($entity->field_test_text->isEmpty(), 'Property has been deleted.');
 
+    // Enable access protection for the text field.
+    // @see entity_test_entity_field_access_alter()
+    $entity->field_test_text->value = 'no access value';
+    $entity->save();
+
+    // Try to delete an access protected field.
+    $this->httpRequest('entity/' . $entity_type . '/' . $entity->id(), 'PUT', $serialized, 'application/vnd.drupal.ld+json');
+    $this->assertResponse(403);
+
+    // Re-load the entity from the database.
+    $entity = entity_load($entity_type, $entity->id(), TRUE);
+    $this->assertFalse($entity->field_test_text->isEmpty(), 'Text field was not deleted.');
+
+    // Try to update an access protected field.
+    $update_entity->field_test_text->value = $update_values['field_test_text'][0]['value'];
+    $serialized = $serializer->serialize($update_entity, 'drupal_jsonld');
+    $this->httpRequest('entity/' . $entity_type . '/' . $entity->id(), 'PUT', $serialized, 'application/vnd.drupal.ld+json');
+    $this->assertResponse(403);
+
+    // Re-load the entity from the database.
+    $entity = entity_load($entity_type, $entity->id(), TRUE);
+    $this->assertEqual($entity->field_test_text->value, 'no access value', 'Text field was not updated.');
+
+    // Restore the valid test value.
+    $entity->field_test_text->value = $this->randomString();
+    $entity->save();
+
     // Try to create an entity with ID 9999.
     $this->httpRequest('entity/' . $entity_type . '/9999', 'PUT', $serialized, 'application/vnd.drupal.ld+json');
     $this->assertResponse(404);
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..623fb44
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldAccessTest.php
@@ -0,0 +1,55 @@
+<?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.',
+      'group' => 'Entity API',
+    );
+  }
+
+  protected function setUp() {
+    parent::setUp();
+    // Install field module schema and register entity_test text field.
+    $this->enableModules(array('field', 'entity_test'));
+  }
+
+  /**
+   * Tests that hook_entity_field_access_alter() is called.
+   *
+   * @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.');
+  }
+}
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..56daa96 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,14 @@ function entity_test_entity_test_insert($entity) {
     throw new Exception("Test exception rollback.");
   }
 }
+
+/**
+ * Implements hook_entity_field_access_alter().
+ *
+ * @see \Drupal\system\Tests\Entity\FieldAccessTest::testFieldAccess()
+ */
+function entity_test_entity_field_access_alter(&$access, $operation, $field, $account) {
+  if ($field->getName() == 'field_test_text' && $field->value == 'no access value') {
+    $access = FALSE;
+  }
+}
