Drupal und Image: Zugriff auf Bilder (und Artikel) beschränken

Hinweis: Wer nur am Uppsala-Blog und weniger an Content-Management-Software interessiert ist, kann hier aufhören zu lesen.

Ich hatte schon länger vor, den Zugriff auf die (mit Image hochgeladenen) Bilder in diesem Blog derart zu beschränken, dass nur angemeldete Benutzer die Bilder in voller Auflösung betrachten können. Zunächst hatte ich nach einem Modul gesucht, das den Zugriff nach dem Typ des Nodes beschränkt um die Bilder nicht einzeln schützen zu müssen, war aber nicht fündig geworden. Also habe ich zunächst einmal Simple Access installiert, mit dem man den Zugriff auf einzelne Nodes für bestimmte Nutzergruppen freigeben kann, zumal ich inzwischen eventuell auch einzelne Artikel schützen wollte.

1. Bilder mit Simple Access schützen
Hinweis: Wer nur den Zugriff auf Bilder beschränken will, braucht Simple Access nicht und kann mit 2. fortfahren.
Nach der Installation hatte ich wenig Lust, sämtliche Bilder von Hand zu schützen und habe deswegen meine SQL-Kenntnisse etwas aufgefrischt:
Mit SELECT DISTINCT * FROM `node_access` a INNER JOIN `files` f ON a.nid = f.nid ORDER BY a.nid ASC habe ich zunächst alle Bilder anzeigen lassen um sicher zu sein, dass ich keine Artikel bearbeite. Den Simple-Access-Schutz aktiviert man für alle Bilder dann mit UPDATE `node_access` As a INNER JOIN `files` AS f ON a.nid = f.nid SET `gid` = 1 (SQL in phpMyAdmin-Notation.)

2. Direktzugriff auf Dateien unterbinden
Nun werden zwar alle Image-Nodes durch Simple Access geschützt, nur merkte ich, dass der direkte Zugriff auf die Bilddateien problemlos möglich war, obwohl in den Drupal-Dateisystem-Einstellungen als Download-Methode "privat" eingestellt war. Zunächst dachte ich an ein Problem mit den Verzeichnisrechten und spielte mit htaccess-Befehlen rum, bis ich merkte, dass der Pfad, über den man die Bilder erreicht, ein virtueller ist:
Während der eigentliche Pfad http://www.tilman.de/uppsala/files/images/chor01.preview.jpg sehr wohl geschützt war, war das Bild in der Artikeln als http://www.tilman.de/uppsala/system/files/images/chor01.preview.jpg verlinkt. (Ich kenne die Architektur von Drupal nicht und habe keine Ahnung, welchem Zweck dieses "system"-Verzeichnis dient.) Auch der Zugriff über GET-Parameter http://www.tilman.de/uppsala/?q=system/files/images/chor01.preview.jpg war möglich.
Des Rätsels Lösung: Das Image-Modul, welches ich für das Einbinden der Bilder verwende, schert sich nicht um Benutzerrechte und gibt die Bilder an jeden, der danach fragt. (Autsch.)
Der entscheidende Hinweis war dann im Drupal-Forum: http://drupal.org/node/26601#comment-54855
Den geposteten Code habe ich dann etwas angepasst und damit die Funktion image_file_download in image.module ersetzt:

// edit
// see http://drupal.org/node/26601#comment-54855 and http://www.tilman.de/uppsala/?q=node/84
function image_file_download($file) {
  // get image from database
  $result = db_fetch_object(db_query("SELECT f.*, n.type FROM {files} f LEFT JOIN {node} n ON f.nid=n.nid WHERE f.filepath='%s'", $file));

  if ($result->type == 'image') {
    // only allow download if its our node, and the user has privilege or it is only a thumbnail
    if (user_access('view original images') || strpos($file, '.thumbnail.')) {
      $headers = array('Content-Type: ' . $result->filemime);
      return $headers;
    }
  }

  // otherwise, its some other modules responsibility
  return -1;
}

Ergebnis: Bilder werden nur noch an registrierte Benutzer herausgegeben, oder wenn ".thumbnail." im Dateinamen vorkommt. (Drupal fügt zum eigentlichen Datei noch die Bildgröße als Suffix hinzu.)

Anmerkung: Der Zugriff über den physischen Pfad auf die Bilder war bei mir immer noch möglich, was aber wohl eher mit einer Fehlkonfiguration oder meinen Spielereien zu tun hat. Dieses Problem ließ sich dann wirklich mit einer htaccess-Datei im Verzeichnis files/images mit dem Inhalt

Deny from all
lösen.