Wie überprüfe ich den Entitätszugriff, wenn Ansichtsfelder angezeigt werden?

8

Ich habe eine benutzerdefinierte Entität. In ist ein Zugriffsrückruf definiert, der hook_entity_info()aufgerufen wird, wenn ich auf die Entität zugreife. Auch in Ansichten, in denen ich gerenderte Objekte anzeigen möchte, wird das Objekt nur angezeigt, wenn der Benutzer das Objekt sehen darf.

Wenn ich jedoch die Ansicht so ändere, dass "Felder" anstelle von "gerenderten Entitäten" angezeigt werden, wird der Zugriffsrückruf nicht mehr aufgerufen, und Benutzer können alle Felder (und Eigenschaften) der Entität unabhängig von Berechtigungen anzeigen. Wenn Sie sich die ausgeführte Abfrage ansehen, ist dies sinnvoll, die Feldwerte werden zusammengefügt und die Entität wird nie wirklich geladen.

Wie sollte man also den Entitätszugriff für Ansichten implementieren, wenn Felder angezeigt werden (in Drupal 7)?

Ich habe festgestellt, wie bei Verwendung von hook_field_access in Ansichten nach Entitätstypen gesucht werden kann. Ich gehe jedoch davon aus, dass dies nur für verknüpfte Felder und nicht für Eigenschaften von Basisentitäten funktioniert, sodass dies nur ein Teil einer Lösung wäre.

Neograph734
quelle
Haben Sie überlegt, einen benutzerdefinierten view_mode zu verwenden?
Darvanen
@Darvanen ja, aber ich brauche das System, um narrensicher zu sein. Ich kann also nicht davon ausgehen, dass jeder gerenderte Entitäten verwendet.
Neograph734
@ Neograph734 Deine Entität ist ein Knoten oder etwas anderes?
mchar
@mchar Es handelt sich um eine benutzerdefinierte Entität, die mit dem Entitäts-API-Modul erstellt wurde.
Neograph734
Ich denke (richtig, wenn ich falsch liege ), dass die Lösung für Ihr Problem die Implementierung von hook_node_grants und hook_node_access_records ist, aber ich bin nicht sicher, ob diese Hooks für benutzerdefinierte Entitäten gelten (hoffentlich ja), zumindest ist dies die effizienteste Methode, um den Zugriff zu steuern soweit es nodesDrupal betrifft .
mchar

Antworten:

7

Angenommen, für Ihr Modul zur Bereitstellung von Entitäten ist die Views-Integration bereits eingerichtet, und Sie müssen sich keine Gedanken über die Paginierung machen . Sie können hook_views_pre_render()die Ergebnisse aufrufen und Ihren Zugriffsrückruf für jede Entität in Ihrer Basistabelle aufrufen und Einträge herausfiltern, die der Benutzer nicht herausfiltert Zugang haben zu:

/**
 * Implements hook_views_pre_render().
 */
function MYMODULE_views_pre_render(&$view) {
  global $user;

  // Iterate over View results for our custom entity
  if ($view->base_table == 'my_entity_base_table') {
    foreach ($view->result as $index => $row) {

      // Presuming eid is the entity PK
      $results = entity_load('my_entity_machine_name', array($row->eid));
      if (!empty($results)) {
        $entity = $results[$row->eid];

        // If the custom access callback returns FALSE, remove from results.
        if (!MYMODULE_my_entity_access_callback('view', $entity, $user)) {
          unset($view->result[$index]);
        }
      }
    }
  }
}

Wenn Paginierung ein Problem darstellt, ist es ein schwierigeres Problem. Durch Anpassen der Ansichtsergebnisse wird ein konsistenter Versatz unterbrochen (z. B. Seite 1 liefert möglicherweise 4 Ergebnisse, Seite 2 liefert möglicherweise 10 Ergebnisse). Außerdem können die Ergebnisse der SQL-Abfrage nicht an Informationen angepasst werden, die nur durch Ausführen von PHP bekannt sind.

In diesen Fällen müssen Sie Ihre Methode anpassen (z. B. hook_views_query_alter()wenn der Zugriffsrückruf auf einer DB-Abfrage basiert, Ihre View-Pager-Optionen geändert werden usw.), um den Zugriffsrückruf zu berücksichtigen, der die Ansicht manipuliert.

Shawn Conn
quelle
1
Obwohl dies definitiv ein Anfang ist, wird der Pager für Ansichten unterbrochen (nicht 25 Elemente pro Seite, aber weniger, manchmal sogar 0). Gibt es eine Möglichkeit, dies zu verarbeiten, bevor die Paginierung berechnet wird?
Neograph734
1
Es scheint, dass die Paginierung bereits in der Abfrage enthalten ist: ... LIMIT 25 OFFSET 0Die einzige Möglichkeit, die Paginierung am Laufen zu halten, besteht darin, vor Abschluss der Abfrage zu handeln. Ich kann meinen Zugriffsrückruf jedoch nicht in die Abfrage einbetten. Also kann es nicht gemacht werden?
Neograph734
1
Ich habe nicht einmal darüber nachgedacht, da es nicht erwähnt wurde. Auf jeden $queryFall enthält das Ansichtsobjekt die Eigenschaft, welche Pager-Eigenschaft bearbeitet werden kann, um die geänderte Anzahl widerzuspiegeln.
Shawn Conn
1
Ich auch nicht, Shawn, aber ich bin bei der Implementierung Ihrer Lösung darauf gestoßen. Dies funktioniert noch nicht ganz (Sie setzen die Gesamtmenge der Elemente auf die Menge, die die Abfrage zurückgegeben hat, sodass sie niemals größer sein wird als die Anzahl der Elemente pro Seite (25)). Ich werde dies mit einer separaten Zählabfrage versuchen, um die Gesamtmenge der Elemente zu erhalten.
Neograph734
1
Entschuldigung, das war eine voreilige Bearbeitung, als ich Zeit hatte. Ich dachte, ich hätte genau dieses Problem zuvor gelöst, aber als wir diesen Code noch einmal besuchten, war es ein Fall, in dem wir mit einer unscharfen Paginierung auskommen konnten. Ich habe die Antwort entsprechend aktualisiert.
Shawn Conn
1

Am Ende habe ich es geschafft, eine Arbeitsmethode zu finden, die ähnlich funktioniert wie die Ansichten mit Knoten.

Stellen Sie in hook_views_data()(oder hook_views_data_alter()) sicher, dass Sie den access query tagTabellenschlüssel hinzufügen . Sie können sehen, dass Ansichten dies auch für Knoten in tun node_views_data().

$data['example_table']['table']['base'] = array(
  'field' => 'nid', // This is the identifier field for the view.
  'title' => t('Example table'),
  'help' => t('Example table contains example content and can be related to nodes.'),
  'weight' => -10,

  'access query tag' => 'my_entity_access' // <- Add this.
);

Fügen Sie dann Ihre eigene Implementierung von hinzu hook_query_TAG_alter. Dadurch wird jede Abfrage geändert, bei der dieses Tag hinzugefügt wird. Aufgrund unserer obigen Änderung wird dies automatisch auf alle Views-Datenlisten angewendet, das Tag kann jedoch auch manuell hinzugefügt werden .

Es gibt einige großartige Tricks, _node_query_node_access_alter()die von node_query_node_access_alter () aufgerufen werden (Implementierung der Knotenmodule von hook_query_TAG_alter).

function mymodule_query_my_entity_access_alter(QueryAlterableInterface $query) {
  global $user;

  // Read meta-data from query, if provided.
  if (!$account = $query->getMetaData('account')) {
    $account = $user;
  }
  if (!$op = $query->getMetaData('op')) {
    $op = 'view';
  }

  // From here every query will be different depending on your own needs.
  // Since my entity has a privacy parameter that is either public or private,
  // I chose to implement this as follows:

  // Prepare a database OR.
  $or = db_or();

  // If the user has public view permissions, add it to the OR.
  if (user_access('view public my_entities', $account)) {
    $or->condition('example_table.privacy', 'public');
  }

  // If the user has non-public view permissions, add it to the OR.
  if (user_access('view private my_entities', $account)) {
    $or->condition('example_table.privacy', 'public', '<>');
  }

  // Add the compiled set of rules to the query. 
  $query->condition($or);
}
Neograph734
quelle