Seite 1 von 1

Hooks (nein, nicht der mit dem Krokodil...)

Verfasst: Di 24. Mär 2020, 10:26
von shadowcat
Bei der Erarbeitung des BotTrap Moduls hatte ich ein Problem... Ich wollte NICHT, dass man - neben dem Aktivieren im Backend - noch was machen muß, egal ob es sich um das Einfügen eines include im Template handelt oder um ein Droplet, was in diesem Fall sowieso zu spät gewesen wäre. Andererseits wollte ich diesen BotTrap auch nicht als Bestandteil des Core haben. Also was tun?

Praktisch wäre es halt, man könnte sich in den normalen Ablauf an passender Stelle einklinken. Und damit sind wir bei Hooks oder Events, was so ein bißchen das selbe und so ein bißchen doch nicht das selbe ist. :lol:

Ich habe mich eine Weile umgeschaut, und die einzige verfügbare Klasse, der ich genug zutraue (und die vor allem noch aktiv gepflegt wird), wäre eine Komponente des Symfony-Frameworks gewesen. Da wir von Symfony schon das eine oder andere nutzen, wäre es naheliegend, auch den EventDispatcher zu nutzen. Ich habe mich trotzdem dagegen entschieden, und zwar weil man für jedes Event, welches man verarbeiten will, eine eigene PHP-Klasse schreiben muß. Das erschien mir recht viel Aufwand für eine eigentlich recht geringe Anforderung. Daher habe ich mich dafür entschieden, eine eigene Klasse zu schreiben.

https://symfony.com/doc/current/compone ... tcher.html

Derzeit gibt es genau einen Hook, nämlich den, den ich für meine Anforderung brauchte. Beim Verarbeiten der Anfrage soll möglichst früh erkannt werden, dass es sich bei dem Besucher um einen "bösen Bot" handelt, um diesen aussperren zu können. Das wäre in unserem Fall im Router in der Methode dispatch(). Die einzige Änderung in der Router-Klasse ist diese Zeile:

\CAT\Hook::executeHook('router.before.dispatch');

Wenn es zu diesem Hook keine registrierten externen Funktionen gibt, passiert auch weiter nichts.

Unser BotTrap Admin Tool kann nun diesen Hook "abonnieren". Dazu ist ein Eintrag in der Datenbank notwendig. In diesem steht, welche Funktion in welcher Klasse ausgeführt werden soll, und welche Priorität diese hat. Die Priorität reicht von 0 (niedrigste) bis 100 (höchste). Wenn es nun mehrere Abonnenten gibt, bestimmt die Priorität, in welcher Reihenfolge diese angesprochen werden.

Sofern nun also das BotTrap Admin Tool installiert und aktiviert ist - was das Modul selbst entscheidet, anhand einer Einstellung -, kann die dortige Methode trap() entscheiden, einen Besucher auszuschließen, und das zu ein so frühen Zeitpunkt, dass er weder an Frontend-Seiten noch an den Anmelde-Dialog des Backends rankommt. Dementsprechend hat dieser Abonnent auch die höchste Priorität 100.

Das Abonnieren eines solchen Hooks ist natürlich nicht so ganz ungefährlich; "böse" Module könnten sich in den Ablauf einklinken und "schlimme Dinge" tun. Aber sehen wir es realistisch: Ein scheinbar nützliches Modul vom Typ "page" (z.B. eine Bildergalerie) kann auch im Hintergrund "schlimme Dinge" tun, ohne dass man es merkt.

Die Hook-Klasse selbst ist schon fertig, es fehlen vielleicht noch ein paar Kleinigkeiten, z.B. um ein Abonnement auch wieder aufzuheben, aber der Kern steht schon mal. Die Klasse holt sich die vorhandenen Hooks und Abos aus der Datenbank und führt sie aus, man kann eigene Hooks zur Laufzeit hinzufügen (um sie z.B. in einem eigenen Modul zu benutzen) und existierende abonnieren. Bisher gibt es wie gesagt nur diesen einen Hook an sehr zentraler Stelle, sicherlich werden uns mit der Zeit noch weitere Stellen einfallen, wo sich das sinnvoll nutzen läßt. Schon mit diesem einen Hook ließe sich z.B. bewirken, dass bestimmte Routen (=URLs) kennwortgeschützt sein sollen, auch wenn sie das normalerweise nicht wären.

Re: Hooks (nein, nicht der mit dem Krokodil...)

Verfasst: Mi 25. Mär 2020, 10:51
von creativecat
Puh! Das klingt hammermäßig gut!

Re: Hooks (nein, nicht der mit dem Krokodil...)

Verfasst: Mi 25. Mär 2020, 15:02
von shadowcat
Ich habe die Klasse nochmal überarbeitet, jetzt wird auch geprüft, ob der Hook aus der Klasse aufgerufen wurde, zu der er gehört. So kann ich einen definierten Hook nicht so ohne weiteres mißbrauchen oder versehentlich an der falschen Stelle ausführen. Jetzt überlege ich, ob ich nicht die ganze Zugangsprüfung für die Backend-Bereiche auf Hooks umbaue... :lol: Zumindest die Prüfung, ob eine Route grundsätzlich ein Login erfordert, wäre leicht darauf umzustellen. Bisher prüfen die Methoden ja alle selbst auf die notwendigen Rechte, das ginge dann auch anders: Ich definiere, dass z.B. die Route /page/edit/<ID> nur zugreifbar ist, wenn der Benutzer das Recht 'page_edit' hat. Dann braucht die Methode selbst das gar nicht mehr zu prüfen. Eigentlich wollte ich das von Anfang an so haben, aber manchmal verirrt man sich dann trotzdem...

Die Kehrseite ist, dass man alle Routen irgendwo hinterlegen muß, also z.B. in der Datenbank. Also in etwa so:

Tabelle <prefix>routes

ID | route | permission
1 | /page/edit | page_edit

Natürlich ist obiges Beispiel noch etwas doof, weil man genausogut das notwendige Recht aus der Route extrahieren könnte (mach aus page/edit page_edit und du hast es), aber dann müßte man auch bestimmen, dass das bei allen Routen so sein muß, und so weit wollte ich nicht gehen. :mrgreen: