diff --git a/contrib/search_api_views/includes/query.inc b/contrib/search_api_views/includes/query.inc
index 46a6e31..62a60f9 100644
--- a/contrib/search_api_views/includes/query.inc
+++ b/contrib/search_api_views/includes/query.inc
@@ -93,6 +93,35 @@ class SearchApiViewsQuery extends views_plugin_query {
   }
 
   /**
+   * Defines the options used by this query plugin.
+   *
+   * Adds an option to bypass access checks.
+   */
+  public function option_definition() {
+    return parent::option_definition() + array(
+      'search_api_bypass_access' => array(
+        'default' => FALSE,
+      ),
+    );
+  }
+
+  /**
+   * Add settings for the UI.
+   *
+   * Adds an option for bypassing access checks.
+   */
+  public function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+
+    $form['search_api_bypass_access'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Bypass access checks'),
+      '#description' => t('If the underlying search index has access checks enabled, this option allows to disable them for this view.'),
+      '#default_value' => $this->options['search_api_bypass_access'],
+    );
+  }
+
+  /**
    * Builds the necessary info to execute the query.
    */
   public function build(&$view) {
@@ -100,6 +129,11 @@ class SearchApiViewsQuery extends views_plugin_query {
 
     // Let the pager modify the query to add limits.
     $this->pager->query();
+
+    // Add the "search_api_bypass_access" option to the query, if desired.
+    if (!empty($this->options['search_api_bypass_access'])) {
+      $this->query->setOption('search_api_bypass_access', TRUE);
+    }
   }
 
   /**
diff --git a/includes/callback_node_access.inc b/includes/callback_node_access.inc
new file mode 100644
index 0000000..937256f
--- /dev/null
+++ b/includes/callback_node_access.inc
@@ -0,0 +1,93 @@
+<?php
+/**
+ * @file
+ * Contains the SearchApiAlterNodeAccess class.
+ */
+
+/**
+ * Adds node access information to node indexes.
+ */
+class SearchApiAlterNodeAccess extends SearchApiAbstractAlterCallback {
+
+  /**
+   * Check whether this data-alter callback is applicable for a certain index.
+   *
+   * Returns TRUE only for indexes on nodes.
+   *
+   * @param SearchApiIndex $index
+   *   The index to check for.
+   *
+   * @return boolean
+   *   TRUE if the callback can run on the given index; FALSE otherwise.
+   */
+  public function supportsIndex(SearchApiIndex $index) {
+    // Currently only node access is supported.
+    return $index->item_type === 'node';
+  }
+
+  /**
+   * Declare the properties that are (or can be) added to items with this callback.
+   *
+   * Adds the "search_api_access_node" property.
+   *
+   * @see hook_entity_property_info()
+   *
+   * @return array
+   *   Information about all additional properties, as specified by
+   *   hook_entity_property_info() (only the inner "properties" array).
+   */
+  public function propertyInfo() {
+    return array(
+      'search_api_access_node' => array(
+        'label' => t('Node access information'),
+        'description' => t('Data needed to apply node access.'),
+        'type' => 'list<token>',
+      ),
+    );
+  }
+
+  /**
+   * Alter items before indexing.
+   *
+   * Items which are removed from the array won't be indexed, but will be marked
+   * as clean for future indexing. This could for instance be used to implement
+   * some sort of access filter for security purposes (e.g., don't index
+   * unpublished nodes or comments).
+   *
+   * @param array $items
+   *   An array of items to be altered, keyed by item IDs.
+   */
+  public function alterItems(array &$items) {
+    static $account;
+
+    if (!isset($account)) {
+      // Load the anonymous user.
+      $account = drupal_anonymous_user();
+    }
+
+    if ($this->index->item_type == 'node') {
+      // Load all notes as once.
+      foreach ($items as $nid => &$item) {
+        // Check whether all users have access to the node.
+        if (!node_access('view', $item, $account)) {
+          // Get node access grants.
+          $result = db_query('SELECT * FROM {node_access} WHERE (nid = 0 OR nid = :nid) AND grant_view = 1', array(':nid' => $item->nid));
+
+          // Store all grants together with it's realms in the item.
+          foreach ($result as $grant) {
+            if (!isset($items[$nid]->search_api_access_node)) {
+              $items[$nid]->search_api_access_node = array();
+            }
+            $items[$nid]->search_api_access_node[] = "node_access_$grant->realm:$grant->gid";
+          }
+        }
+        else {
+          // Add the generic view grant if we are not using node access or the
+          // node is viewable by anonymous users.
+          $items[$nid]->search_api_access_node = array('node_access__all');
+        }
+      }
+    }
+  }
+
+}
diff --git a/includes/query.inc b/includes/query.inc
index 958f303..1f9c9a0 100644
--- a/includes/query.inc
+++ b/includes/query.inc
@@ -33,7 +33,12 @@ interface SearchApiQueryInterface {
    *     implementation to use.
    *   - 'search id': A string that will be used as the identifier when storing
    *     this search in the Search API's static cache.
-   *   All options are optional.
+   *   - search_api_access_account: The account which will be used for entity
+   *     access checks, if available and enabled for the index.
+   *   - search_api_bypass_access: If set to TRUE, entity access checks will be
+   *     skipped, even if enabled for the index.
+   *   All options are optional. Third-party modules might define and use other
+   *   options not listed here.
    *
    * @throws SearchApiException
    *   If a search on that index (or with those options) won't be possible.
@@ -356,6 +361,10 @@ class SearchApiQuery implements SearchApiQueryInterface {
    *     implementation to use.
    *   - 'search id': A string that will be used as the identifier when storing
    *     this search in the Search API's static cache.
+   *   - search_api_access_account: The account which will be used for entity
+   *     access checks, if available and enabled for the index.
+   *   - search_api_bypass_access: If set to TRUE, entity access checks will be
+   *     skipped, even if enabled for the index.
    *   All options are optional.
    *
    * @throws SearchApiException
diff --git a/search_api.admin.inc b/search_api.admin.inc
index 0add170..2cf109b 100644
--- a/search_api.admin.inc
+++ b/search_api.admin.inc
@@ -1395,7 +1395,7 @@ function search_api_admin_index_workflow_submit(array $form, array &$form_state)
               $type = "list<$type>";
             }
           }
-          $options['fields'][$key] = array(
+          $index->options['fields'][$key] = array(
             'name' => $field['label'],
             'type' => $type,
             'boost' => '1.0',
diff --git a/search_api.info b/search_api.info
index e08bf39..f5885cb 100644
--- a/search_api.info
+++ b/search_api.info
@@ -13,6 +13,7 @@ files[] = includes/callback_add_url.inc
 files[] = includes/callback_add_viewed_entity.inc
 files[] = includes/callback_bundle_filter.inc
 files[] = includes/callback_language_control.inc
+files[] = includes/callback_node_access.inc
 files[] = includes/datasource.inc
 files[] = includes/datasource_entity.inc
 files[] = includes/datasource_external.inc
diff --git a/search_api.module b/search_api.module
index e1df61b..9c435d1 100644
--- a/search_api.module
+++ b/search_api.module
@@ -736,6 +736,11 @@ function search_api_search_api_alter_callback_info() {
     'description' => t('Lets you determine the language of items in the index.'),
     'class' => 'SearchApiAlterLanguageControl',
   );
+  $callbacks['search_api_alter_node_access'] = array(
+    'name' => t('Node access'),
+    'description' => t('Add node access information to the index.'),
+    'class' => 'SearchApiAlterNodeAccess',
+  );
 
   return $callbacks;
 }
@@ -1213,6 +1218,79 @@ function search_api_get_processors() {
 }
 
 /**
+ * Implements hook_search_api_query_alter().
+ *
+ * Adds node access to the query, if enabled.
+ *
+ * @param SearchApiQueryInterface $query
+ *   The SearchApiQueryInterface object representing the search query.
+ */
+function search_api_search_api_query_alter(SearchApiQueryInterface $query) {
+  $index = $query->getIndex();
+  // Only add node access if the "search_api_access_node" field is indexed for
+  // the index, and unless disabled explicitly by the query.
+  if (!empty($index->options['fields']['search_api_access_node']['indexed']) && !$query->getOption('search_api_bypass_access')) {
+    $account = $query->getOption('search_api_access_account', $GLOBALS['user']);
+    if (is_numeric($account)) {
+      $account = user_load($account);
+    }
+    if (is_object($account)) {
+      try {
+        _search_api_query_add_node_access($account, $query);
+      }
+      catch (SearchApiException $e) {
+        watchdog('search_api', 'An error occurred while altering node access to a search query: @message.', array('@message' => $e->getMessage()), WATCHDOG_WARNING);
+      }
+    }
+    else {
+      watchdog('search_api', 'An illegal user UID was given for node access: @uid.', array('@uid' => $query->getOption('search_api_access_account', $GLOBALS['user'])), WATCHDOG_WARNING);
+    }
+  }
+}
+
+/**
+  * Build a node access subquery.
+  *
+  * @param $account
+  *   The user object, who searches.
+  *
+  * @return SearchApiQueryFilter
+  */
+function _search_api_query_add_node_access($account, SearchApiQueryInterface $query) {
+  if (!user_access('access content', $account)) {
+    // Simple hack for returning no results.
+    $query->condition('status', 0);
+    $query->condition('status', 1);
+    watchdog('search_api', 'User !name tried to execute a search, but cannot access content.', array('!name' => theme('username', array('account' => $account))), WATCHDOG_);
+    return;
+  }
+
+  // Only filter for user which don't have full node access.
+  if (!user_access('bypass node access', $account)) {
+    // Filter by node "published" status.
+    if (user_access('view own unpublished content')) {
+      $filter = $query->createFilter('OR');
+      $filter->condition('status', NODE_PUBLISHED);
+      $filter->condition('uid', $account->uid);
+      $query->filter($filter);
+    }
+    else {
+      $query->condition('status', NODE_PUBLISHED);
+    }
+    // Filter by node access grants.
+    $filter = $query->createFilter('OR');
+    $grants = node_access_grants('view', $account);
+    foreach ($grants as $realm => $gids) {
+      foreach ($gids as $gid) {
+        $filter->condition('search_api_access_node', "node_access_$realm:$gid");
+      }
+    }
+    $filter->condition('search_api_access_node', 'node_access__all');
+    $query->filter($filter);
+  }
+}
+
+/**
  * Utility function for determining whether a field of the given type contains
  * text data.
  *
