Plugin: Uncaught SmartyException: directory not allowed by security setting

Moin zusammen!
Moin @devnullroot‍

Ich versuche hier mal ein bisschen Licht ins Dunkel zu bringen.
Vorsicht: Textwand.

Das Verhalten tritt nur dann auf, wenn der Cache aktiviert ist und das Plugin-eigene Template Directory erst nach bestimmten Bedingungen  ausgeführt wird.
Dazu mal zwei Beispiele:

Beispiel 1 - falsche If-Condition:

public static function getSubscribedEvents()
{
    return [
        'Enlight_Controller_Action_PostDispatchSecure' => 'onPostDispatch'
    ];
}

public function onPostDispatch(\Enlight_Controller_ActionEventArgs $eventArgs)
{
    $userData = $this->container->get('modules')->Admin()->sGetUserData();
    if ($userData['name'] === 'Max Mustermann') {
        return;
    }

    $eventArgs->getSubject()->View()->addTemplateDir('Views/');
}

Warum ist dies nun ein Problem, und was hat das mit dem Cache zu tun?

Dies ist ein PostDispatch-Event, d.h. der Code wird für jeden Kunden in jedem Controller ausgeführt.
Nun gehen wir das Beispiel einmal durch.
Der Cache ist aktiv, wurde jedoch frisch geleert.
Der Kunde John Doe surft die Home-Seite an, das Event wird getriggered und er läuft in den Code rein.
John Doe heißt offensichtlich nicht “Max Mustermann”, sodass er auch nicht in den Early-Exit reinläuft und die Zeile “addTemplateDir” wird ausgeführt.
In dem Views-Ordner des Plugins wird die Home-Seite minimal angepasst, diese angepasste Version der Home-Seite landet nun im Cache.
Irgendwo im Cache findet man jetzt einen Verweis auf diese Anpassung der Home-Seite, das kann so aussehen:
 

'123456789090353523wvgsdfbvdg43g3' => 
    array (
      0 => '/meinSystem/meineShopInstallation/custom/plugins/SwagTestPlugin/Resources/views/frontend/home/index.tpl',
      1 => 1511339915,
      2 => 'snippet',
    ),

Jetzt betritt Max Mustermann den Shop und surft direkt die Home-Seite an.
Was passiert nun?
Der zuvor genannte PHP-Code verhindert im Falle von Max Mustermann, dass zuvor das Plugin-eigene Template Directory offiziell hinzugefügt wird.
Da der Cache aktiviert ist, wird die o.g. Zeile aus dem Cache ausgeführt - er versucht die Datei /meinSystem/meineShopInstallation/custom/plugins/SwagTestPlugin/Resources/views/frontend/home/index.tpl zu laden.
Und hier kommt die Smarty-Security ins Spiel.
Das Verzeichnis /meinSystem/meineShopInstallation/custom/plugins/SwagTestPlugin/Resources/views/ wurde Smarty nie bekannt gemacht und gilt somit als “nicht vertrauenswürdig” => Es knallt.

Die Meldung “Unknown service tag ‘s’” ist durchaus ein Bug, der immer dann auftritt, wenn eine Fehlermeldung ausgegeben werden soll, so wie in diesem Fall.

War dieses Beispiel soweit verständlich?
Dies ist immer dann ein Problem, wenn das addTemplateDir nach einer Bedingung genutzt wird, die bspw. für einen Kunden zutrifft, für den anderen Kunden jedoch nicht.
Wenn man das o.g. Beispiel von der Reihenfolge ändert, also erst kommt Max Mustermann, dann John Doe, würde es übrigens nicht knallen - da direkt die Template-Version gecached werden würde, die die Plugin-eigene Anpassung nicht beinhaltet.
Dadurch tritt auch ein gewissen “Randomness”-Gefühl auf.

Dieses Beispiel erklärt aber noch nicht, warum das Verhalten dann auch beim Threadersteller auftritt.
Aber auch dazu habe ich soeben ein kleines Beispiel vorbereitet.

Beispiel 2 - addTemplateDir nur bei bestimmten Events:

 

public static function getSubscribedEvents()
{
    return [
        'Enlight_Controller_Action_PostDispatchSecure_Frontend' => 'onPostDispatchFrontend'
    ];
}

public function onPostDispatchFrontend(\Enlight_Controller_ActionEventArgs $eventArgs)
{
    $eventArgs->getSubject()->View()->addTemplateDir('Views/');
}

In diesem Beispiel gibt es eigentliche keine Bedingung, warum knallt es jetzt beim Threadersteller?

Dazu folgendes Szenario, das dem Szenario des Threaderstellers zu 99% gleicht:
Das Plugin passt in seinem eigenen Views-Ordner eine Produkt-Box an.

Erneut ist der Cache aktiviert und frisch geleert.
Der Kunde Max Mustermann surft durch den Shop und landet in einem Kategorie-Listing.
Beim Surfen durch das Produktlisting knallt es plötzlich, die Meldung “Unknown tag ‘s’” taucht wieder auf.

Warum knallt es jetzt, es war doch kein zweiter Kunde beteiligt, der eine Cache-Datei generiert hat.
Der Kunde ruft das Listing auf, welches Produktboxen beinhaltet.
Der Verweis auf die Template-Anpassung durch das Plugin landet im Cache.
Beim Surfen durch das Kategorie-Listing scrollt Max Mustermann etwas herunter und löst Infinite Scrolling aus - und hier kommt der Kniff.
Die Template-Anpassung an den Produktboxen ist durch das initiale Aufrufen des Listings bereits gecached, zusammen mit dem Verweis auf die Anpassung.
Infinite Scrolling benötigt jene Produkt-Boxen natürlich ebenfalls, jedoch funktioniert Infinite Scrolling über einen Widget -Controller.
Das Event, das das Plugin nutzt, ist jedoch lediglich für  Frontend -Controller, entsprechend wird beim Infinite Scrolling nie das Template Directory des Plugins hinzugefügt.

Auch hier wird also eine Anpassung aus einem Ordner zu laden versucht, der Smarty im aktuellen Request nie bekannt gemacht wurde.

Nun hat unser Threadersteller sein Event geändert, auf “Enlight_Controller_Action_PostDispatch”.
Dieses Event wird für alle Arten von Controllers aufgerufen, unabhängig davon ob es ein Frontend - oder ein Widget -Controller ist.
Ergo: Es funktioniert mit diesem Event auch mit aktiviertem Cache.

War auch dieses Beispiel verständlich?

Diese Probleme konnten natürlich erst mit Einschalten der Smarty Security auftreten und waren daher in vorherigen Shopware-Versionen nie ein Problem.
Daher der allgemeine Tipp:
Registriert eure Plugin eigene Views Ordner so früh wie möglich, also nicht erst nach bestimmten Bedingungen, und nutzt keine Events, die ja auch irgendwo direkt eine Bedingung beinhaltet, wie eben bspw. die Unterscheidung des Moduls.

Solltet ihr zwingend eine Bedingung benötigen, bevor eine Template-Anpassung greift, so registriert den Views Ordner trotzdem vor einer Bedingung und packt die Bedingung selbst dann in das jeweilige Template.
Dadurch wird die Template-Datei  immer  geladen, die eigentliche Anpassung wird jedoch durch die Bedingung  innerhalb der Template-Datei  verhindert.

War das alles soweit verständlich?
Bei Fragen gerne zurückschreiben, dann verlinkt mich bitte kurz, sodass ich eine Benachrichtigung bekomme.

Gruß,
Patrick  Shopware

16 „Gefällt mir“