diff --git a/includes/callback_node_access.inc b/includes/callback_node_access.inc
new file mode 100644
index 0000000..f350fb9
--- /dev/null
+++ b/includes/callback_node_access.inc
@@ -0,0 +1,56 @@
+<?php
+/**
+ * @file
+ * Search Api data alteration to add node access data to the index.
+ */
+class SearchApiAlterNodeAccess extends SearchApiAbstractAlterCallback {
+  public function supportsIndex(SearchApiIndex $index) {
+    // Currently only node access is supported.
+    return 'node' == $index->item_type;
+  }
+
+
+  public function propertyInfo() {
+    return array(
+      'search_api_access_node' => array(
+        'label' => t('Node Access'),
+        'description' => t('Data needed to apply node access.'),
+        'type' => 'list<string>',
+      ),
+    );
+  }
+
+  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.
+          // We assume we'll never have an entity with the name '_all'.
+          $items[$nid]->search_api_access_node = array('node_access__all');
+        }
+      }
+    }
+  }
+}
diff --git a/search_api.info b/search_api.info
index 7d3b55b..92feb69 100644
--- a/search_api.info
+++ b/search_api.info
@@ -12,6 +12,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 249298b..3e062aa 100644
--- a/search_api.module
+++ b/search_api.module
@@ -729,6 +729,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;
 }
@@ -1206,6 +1211,57 @@ function search_api_get_processors() {
 }
 
 /**
+ * Implements hook_search_api_query_alter().
+ *
+ * Add node access to the query.
+ *
+ * @param SearchApiQueryInterface $query
+ *   The SearchApiQueryInterface object representing the search query.
+ */
+function search_api_search_api_query_alter(SearchApiQueryInterface $query) {
+  $index = $query->getIndex();
+  if ($index->item_type == 'node') {
+    $account = $GLOBALS['user'];
+    try {
+      $access_query = _search_api_build_node_access_query($account);
+      if ($access_query) {
+        $query->filter($access_query);
+      }
+    }
+    catch (SearchApiException $e) {
+      watchdog("search_api", 'User %name (UID:!uid) cannot search: @message', array('%name' => $account->name, '!uid' => $account->uid, '@message' => $e->getMessage()));
+    }
+  }
+}
+
+/**
+  * Build a node access subquery.
+  *
+  * @param $account
+  * The user object, who searches.
+  *
+  * @return SearchApiQueryFilter
+  */
+function _search_api_build_node_access_query($account) {
+  if (!user_access('access content', $account)) {
+    throw new SearchApiException(t('The current user has no node access, uid: @uid', array('@uid' => $account->uid)));
+  }
+
+  // Only filter for user which don't have full node access.
+  if (!user_access('bypass node access', $account)) {
+    $node_access_query = new SearchApiQueryFilter('OR');
+      // Get node access grants.
+    $grants = node_access_grants('view', $account);
+    foreach ($grants as $realm => $gids) {
+      foreach ($gids as $gid) {
+        $node_access_query->condition('search_api_access_node', "node_access_$realm:$gid");
+      }
+    }
+    return $node_access_query;
+  }
+}
+
+/**
  * Utility function for determining whether a field of the given type contains
  * text data.
  *
