Archiv der Kategorie Technik

Featurities meets Fallstrick: Die Spring Security 3.0 Konfigurationsodyssey

Mit dem Majorrelease 3.0 wurde dem Modul Spring Security eine Menge von neuen Features angeignet. Spring Security ist die Modulkomposition, welches für das Java Framework Spring quasi die gesamte Authentifizierung, Autorisierung, Legitimierung jedwegiger Art ermöglicht.

Leider wurden mit dem Release 2.x auf 3.0 eine Reihe von API-Changes vollzogen. Zugegeben, die waren auch sicher alle sinnvoll, weil Komponenten wie die Authentifizierung weiter geteilt wurden und man somit wesentlich flexibler ist, neue Anforderungen zu ermöglichen (Baukastenprinzip). Aber die Dokumentation ist – gesamtheitlich betrachtet – irgendwie immer noch mies und oft nicht aktualisiert. Oder man findet im Internet einfach nur (alte) Beispiele.

Die http-Direktive

Im Namespace von Spring Security existiert das Tagelement http, mit welchem man kurze, knappe und verständliche Konfigurationen anlegen kann. Der Vorteil liegt klar auf der Hand: Man muss nicht alle Beans, Listener und Provider anlegen, denn das geschieht automatisch. Tja, wären da nicht ein paar Einschränkungen in der Funktionsvielfalt.

Konkretes Beispiel: Remember Me

Just wurde das Minor-Release 3.0.1 veröffentlicht, und nur wenige Tage später zu erfahren, dass Remember Me kaputt sei. Egal, fahren wir erstmal weiter mit 3.0.0.

Laut Dokumentation ist es am einfachsten, wenn man die Direktive remember-me (Security Namespace) innerhalb der http-Direktive (Security Namespace) verwendet. Ohne irgendeine Angabe wird ein stinknormales, Cookie basiertes Tokenverfahren ohne (echten) privaten Schlüssel verwendet. Reicht für den ersten Einsatz erstmal auf, soll ja erstmal funktionieren.

Fehlermöglichkeit 1a: Man loggt sich ein, und es passiert nichts (kein “RememberMe”-Cookie).
Lösung: Wenn man einen eigenen Auth-Filter einsetzt, muss man diesem auch den RememberMe-Service “setten”. Außerdem muss der SecurityChainFilter (web.xml!) auch auf die login-Seite verweisen. Es dürfen auch keine Filter bei der Konfigurierung von intercepted Urls (speziell hier: login, logout) gemacht werden.

Fehlermöglichkeit 1b: Es passiert noch immer nichts?
Lösung: Vielleicht wurde vergessen, einen Parameternamen für den Request zu setzen. Der Standardname ist ein typischer Springname, der natürlich unschön ist. Und den kann man nicht über die RememberMe-Direktive setzen, also muss man eh einen eigenen Service definieren. Bäm. Referenzierung geht dann zwar noch, aber für mehr ist die RememberMe-Direktive dann nicht mehr zu gebrauchen.

Fehlermöglichkeit 2a: Man besucht die Seite ohne Login, aber mit Cookie – und die Loginseite kommt (Log sagt kein gültiger Auth).
Lösung: Man kann der Log trauen, wenn sie zwar beim Einloggen nun einen Token ablegt (kann man zum Beispiel sehr einfach mit diesem Firefox-Addon inkl. Editor(!) verifizieren), dieses aber beim erneuten Besuchen der Seite (bzw. ohne JSPSESSION-Cookie) nicht verwendet bzw. wird nicht erkannt. Schlussendlich half u.a. das Umbenennen der Userservices-Bean in “userService”. Außerdem sollte die Loginseite keinen Filter/Access haben (s.o.) Lieber 2x prüfen!

Fehlermöglichkeit 2b: Es erscheint eine Ausnahme, dass der Key falsch sei.
Lösung: Dazu muss man wissen: Sobald  man eine individualisierte RememberMe-Konfiguration nutzt, wird auch der Key nicht mehr vernünftig auf alle Komponenten (Provider, Filter, Manager) gesetzt. Beim Anlegen wird also der eigene Key verwendet, beim Auslesen der Standardkey. Yes! (s.o.)

Fehlermöglichkeit 3: Man besucht die Seite ohne Login, aber mit Cookie – aber wie in 2 nur die Loginseite.
Lösung: Im Logger/Debugger kann man nun feststellen, dass zwar das Cookie gefunden wurde, das Token gefunden und validiert wurde aber dann keine Rechte existieren – aha? Wahrscheinlich fehlt im Provider noch ein zusätzliches Setting der Komponenten. Am besten von RememberMe Service/Filter/Provider jeweils alle möglichen Properties durchgehen. Jaja, wie gesagt.. ;)

Fehlermöglichkeit 4: Das Ausloggen (beispielsweise logout.html) hat nach Aktivierung von Rememberme plötzlich keine Auswirkungen mehr.
Lösung: Zwar wird die Seite gefunden, aber es wird kein “Logout” gemacht. Auch hier sollte man prüfen, ob ein SecurityChainFilter (web.xml) auch für die logout-Seite greift.

Fehlermöglichkeit 5: Das Besuchen der Seite wirft einen Fehler (ggf. “mit weißer Seite”), dass keine neue Session erstellt werden kann.
Lösung: Richtig, nach einem Request ist ja dann auch zu spät. Das Attribut create-session in der Direktive http sollte daher auf “ifRequeried” gestellt sein.

Fazit:

  • SecurityChainFilter immer prüfen
  • Intercepted Urls prüfen
  • RememberMe-Direktive innerhalb der http-Direktive ist quasi abgesehen von der services-ref unbrauchbar.

Anmerkung:

Natürlich kann man sich das Problem mit den SecurityChains vom Hals schaffen, indem man stupide ein /* filtert. Das hat jedoch zur Auswirkung, das Spring Security auch jeden verdammten Request anguckt; bei zusätzlichen (statischen) Inhalten wie Javascript, Stylesheets, Bildern, Flash u.ä. ist das ein Overhead, der unnötig ist.

Tags: , , ,

HTML5 & Forms

Als Ergänzung zu meinem XForms-Vortrag in der FH, hier ein paar nette Details zu den Neuerungen von HTML5/Forms. Jaja, was für eine Überaschung. Okay, nach dem Tod von XHTML2 (und demnach die Integration von XForms in XHTML) auch wieder nicht…

Ganz allgemein scheint Dive Into HTML5 aber auch empfehlenswert zu sein, wenn auch noch in Arbeit. Nett gemacht.

Tomcat Webapp Deployment/Build

Im Zuge eins kommenden Artikels/Howto über Webapp Deployment/Build suche ich noch bestehende Lösungen, gerne auch Scripts a la Ant. Auf letzterem basiert auch meine bisherige Vorgehensweise..

Update: Inkl. dem Buildprozess.

Google

Google hat also nebst so Allerweltssachen wie Mails, Cloud, Docs und Search also auch

  • ein paar Javascript Tools
  • verlinken als CDN die bekanntesten JS-Frameworks, gleichzeitig Anbieter von entsprechenden APIs für Maps, Analytics & Co.
  • Google Wave, “was vollkommen neues”, was irgendwie noch keiner (nicht viele) richtig einzurordnen / anzuwenden kann
  • ein eigenes Dateisystem (hm, man sieht noch nicht viel davon)
  • ein eigenes Betriebssystem (soll ja kommen, man hört nichts mehr)
  • ein eigenen Browser mitsamt Javascriptengine
  • just eine Internet-Protokollerweiterung/ersetzung namens SPDY von/für HTTP – mitsamt Spezifikationen, also nichts halbgares
  • “nur ein paar” technische Paper, etwa zu Map/Reduce oder BigTable
  • nebst den nicht zählbaren Rechenzentren (geschweige Containern, ganz zu schweigen Rechnern) auch noch eigene Datenleitungen…

Und die Leute haben da noch Angst vor der Datenkrake – da bahnt sich doch ein ganz anderes – liebes? – Monster an.

Und Hinweise, Links? Google! Ha!

Howto: trac auf Mac OS X 10.6

Für den privaten Gebrauch – und für unterwegs, auf dem mobilen – kann sowohl ein Subversion als auch trac sicherlich eine Offline-Alternative sein.

Tatsächlich suchte ich nun eine Möglichkeit, “mal eben” eine trac-Instanz aufzusetzen: Auf dem Webserver erschien mir zu komplex und kompliziert, eine neue VM auf dem Macbook etwas ineffizient, oder? :) Also warum nicht direkt unter OS X, bzw. technisch gesehen BSD?

Die initiale Suche nach der Thematik bringt einen schnell zu einer Fink-Lösung – also mal ehrlich, schon etwas aufwendig?

Tatsächlich gibt es auch einfachere Varianten, etwa hier. Aber auch hier wird noch einiges runtergeladen, kompiliert.. ach, Informatiker sind von Grund her faul. Nein, das ist es auch nicht.

# python2.6 ist bereits installiert.
python -version
> Python 2.6.1

Die abgespeckte, und funktionstüchtige Methode lautet daher – kurz und knapp:

sudo easy_install Pygments
sudo easy_install Genshi
sudo easy_install Trac

Das war es, trac ist installiert.

Für diejenigen, die nun auch eine Schnelleinweisung in eine erste Konfiguration brauchen, ein Schnelldurchlauf. Bemerk: Natürlich können auch andere Pfade für die Repositories verwendet werden, wichtig ist nur: Der eigene Benutzer muss später Lese- und Schreibrechte besitzen. Das heißt im Klartext: Entweder nachher “umgranten” mit chown, oder woanders hinpacken. Auf /usr/local hat in der Regel nur der Superuser Zugriff, d.h. hier muss mit sudo gearbeitet werden.

# ein neues Subversion Repository anlegen
svnadmin create /usr/local/svn

# ein neues trac Repository anlegen - man kann alle Standardwerte nutzen, evtl. Pfad ändern
trac-admin /usr/local/trac initenv

Trac liefert einen kleinen Miniserver mit, der für diesen Zweck erst einmal reicht.

tracd --port 8000 /usr/local/trac/

Voilá.

Selbstverständlich brauchen wir noch einen User, zum Einloggen. Beispiel für den Benutzer user.


htpasswd -c /usr/local/trac/trac.htpasswd admin
> New password:
> New password:
> Re-type new password:
> Adding password for user admin

htpasswd /usr/local/trac/trac.htpasswd knalli
> New password:
> New password:
> Re-type new password:
> Adding password for user knalli

Jetzt den Server mit erweiterteten Parametern starten, also beispielsweise:

tracd --port 8000 --basic-auth=trac,/usr/local/trac/trac.htpasswd,trac /usr/local/trac

Eine Konfiguration über Apache2 ist dauerhaft eventuell zu empfehlen, aber für’s erste reicht es auch so. Ebenfalls empfiehlt die Dokumenation den Einsatz des Parameters –auth (Digest), dazu bitte jene konsultieren.

Best practices in papayaCMS 5 – oder: Sinnvolle Aufteilung der Modulklassen.

Vor wenigen Tagen wurde papayaCMS 5.0 veröffentlicht. Da eventuell der ein oder andere mit dem System nun herumexperimentiert, wird man als Entwickler auch schnell Module entwickeln wollen. Vor allem bei größeren Modulen (bestehend aus vielen Ausgabe-Klassen) empfiehlt sich eine weit granulärere Aufteilung als zwingend vorgeschrieben ist.

Ich erspare mir die Erklärung der Basispapayasystems, dafür möge man bitte die entsprechenden Dokumentionen (PDF) konsultieren. Nur so viel sei gesagt: Um die Daten von der Datenbank auf den Bildschirm zu bekommen, braucht man grundsätzlich nur eine Ausgabeklasse (Seite, Box, usw.) und ein entsprechendes XSL-Template. Aber, aber..

Ineffiziente Strukturierung der Klassen

Ineffiziente Strukturierung der Klassen

Prinzipiell gilt: MVC-Kenner werden einen sehr leichten Einstieg haben, die anderen sollten bei Fragen zunächst diese Thematik verinnerlichen.

Damit eine Klasse Zugriff auf die Datenbank hat, muss es von base_db (sys_base_db.php) abgeleitet werden. Vor allem bei kleinen Seitenmodulen passiert es häufig, dass der Entwickler die Kurzversion nimmt: Generalisierung der Datenbankklasse, 1-2 Queries und XML-Ausgabe. Spätestens, wenn man die gleiche Funktionalität – etwa “last blog post titles” – nicht nur als Seiten- sondern auch als Boxmodul benötigt, entsteht doppelte Code. Doppelter und redundanter Code. Schlecht. Ein guter Ansatz, um die Zuständigkeiten zu prüfen und zu ermitteln, ob Operationen sich nicht generalisieren und delegieren lassen können. Und ja, wir kommen dem Ziel MVC damit praktisch sehr nahe.

Alleine aus diesem praktische Grunde (und selbstverständlich ist die Verwendung von Delegation grundsätzlich in Modulsystem zu befürworten) empfiehlt sich der Einsatz einer Datenbasisklasse. Diese Klasse erweitert die o.g. Datenbankklasse, die Ausgabemodule instanziieren nur noch diese Basisklasse. Und für unsere Freunde der notorischen Delegationsverweigerer: Mit “Seitenklasse erweitert Datenbankklasse” kommt ihr nicht weiter… und das gilt selbstverständlich auch für alle anderen Arten von Modulen. Tatsächlich ist hier Delegation unbedingt notwendig.

Ein weiterer Vorteil ist die bessere Wartbarkeit und Übersichtlichkeit (vgl. MVC). Selbstständlich werden auch schreibene Operationen in dieser Klasse implementiert. Der Einsatz von Interfaces oder mehrerer Datenbasisklassen steht jedem frei. Man sollte aber nicht mit Kanonen auf Spatzen schießen, ein komplexes System wird durch mehr Komplexität nicht wirklich einfacher.

Beispiel mit Kompetenzen: Delegation

Beispiel mit Kompetenzen: Delegation

Die weitere Aufgabe des Ausgabemoduls (vgl. Controller) ist natürlich auch die Ausgabe.. ja, kein Witz ;) Auch hier gibt es eine ähnliche Konstellation wie bei den Datenbankabfragen, aber in der umgekehrten Sicht: Es gibt in größeren System häufig gleiche oder ähnliche Ausgaben. Auch hier kann die Faulheit einen schnell dazu verleiten, den XML-Code im Controller selbst zu schreiben – ist ja praktisch. Aber auch hier gilt: Spätestens wenn man selber [beim Refactoring] anfängt, und klasseninterne Helpermethoden zur Sub-XML-Generierung zu bauen oder gleichen XML-Code an mehr als 1-2 Stellen verwendet.. richtig: Delegation! Hier empfiehlt sich der Einsatz einer Ausgabebasisklasse. Jene Klasse hat im Wesentlichen nur eine Aufgabe: Für einen oder mehrere Parameter (idR eine Datenarraystruktur) ein geschäftsklassenabhängiges XML zu bauen. Es gibt weder eine Verbindung zur Datenbank noch zu anderen Ausgabemodulen.

Ebenfalls für diese Klassen gilt: Eine weitere strukturelle Aufteilung der Ausgabebasisklassen kann, muss aber nicht vorteilhaft sein.

Selbstverständlich lassen sich beide Klasse auch in Nicht-Ausgabemodulen verwenden, etwa einem Connectormodul. Das ist vor allem für Module interessant, die bestimmte Daten und/oder XML (aus welchen Gründen auch immer) weitergeben müssen.

Tipp 1: Vor allem bei mehreren Seiten- oder Boxmodulen kann es durchaus sinnvoll sein, eine gemeinsame Oberklasse (base_myplugin_content) zu erstellen. Hier lassen sich auch direkt die notwendigen Basisklassen korrekt instanziieren.

Komplexes Beispiel mit Oberklassen für Module

Komplexes Beispiel mit Oberklassen für Module

Tipp 2: Die Verwendung des Singleton-Pattern steht einem frei: In der Regel hat es aber wenig Sinn, die Datenbasisklasse mehrfach zu erstellen (kann vorkommen, wenn Boxen in Seiten des gleichen Modules auftauchen). Daher: getInstance().

In der Zusammenfassung:

  • Trennung der Zuständigkeiten und Verwendung von Delegation, aber: Generalisierung geht auch
  • Verwendung von Datenbasisklassen, bspw. base_myplugin_data extends base_db
  • Verwendung von Ausgabebasisklassen, bspw. base_myplugin_output extends base_object
  • delegierte Verwendung der o.g. Klassen in Seiten- und Boxmodulen

Tomcat, Eclipse und der Manager

Während es natürlich mit der Standard-Standalone-Variante keine Probleme macht, hat die Integration von Tomcat in Eclipse einen kleinen Nachteil: Dadurch, dass Eclipse neben den Webapps auch die komplette Konfiguration mitsamt der Module übernimmt, fehlt daher die Manager-Webapp. Häufig ist dies egal und zu vernachlässigen, aber falls man doch mal Zugang braucht, sei es zum (testweisen) Deployen, oder einfach einer Konfiguration außerhalb Eclipse:

Als erstes fügt man ein externes Webapp-Modul ein, der Suchpfad ist “${catalina_home}/webapp/manager”. Dabei sollte man beachten, dass scheinbar der Pfad ausgeschrieben werden muss (bzw. eine gültige Catalina-Home-Variable). Dieser Schritt hat der server.xml eine Zeile (meist gaaaanz unten) wie folgt hinzugefügt:

<Context docBase="YOURPATH/webapps/manager" path="/manager" reloadable="false"/>

Wie man sich gerne überzeugen lassen kann, funktioniert die Anwendung jedoch noch nicht. Der Grund: Der Tomcatmanager muss im priviligierten Modus gestartet werden, welcher zusätzlich eine Benutzerkennung erwartet. Zunächst muss die o.g. eingefügt Zeile wie folgt angepasst werden (hier wichtig: privileged=true).

<Context antiJARLocking="false" antiResourceLocking="false" docBase="YOURPATH/webapps/manager" path="/manager" privileged="true" reloadable="false"/>

Wie bereits erwähnt, erwartet der Manager einen authentifizierten Benutzer. Man kann sich das Leben aber einfach machen, und die vorgefertige tomcat-users.xml nutzen. Man kann die Einträge auskommentieren; wichtig ist jedoch, das mindestens ein Benutzer existiert, dem die Rollen admin und manager zugewiesen sind. Beispielsweise:

<user username="manager" password="manager" roles="admin,manager"/>

Soweit, so gut. Nicht vergessen sollte man jedoch das (Re)Publish des Servers, denn sonst deployt Eclipse die neuen Einstellungen nicht in das interne Tomcat-Home-Verzeichnis.

Der Manager findet sich dann – bei einer Standardinstallation – unter der Adresse http://localhost:8080/manager/html

Multiple Joins in Access

Ja, das fällt auf jeden Fall in eine Rubrik “Thinks I’ve learned”. Access kann auch in der neuen 2007er Version keine zusammenhängende/aneinandergereihte JOINs – auf diese Eigenart muss man aber erstmal kommen, wenn einem die “Fehlender Operator”-Meldung um die Ohren gehauen wird. Abhilfe schafft ein Konstrukt, das mehr als ärmlich ist: Einfach eine Verschachtelung der einzelnen Teilausdrücke, will heißen (X steht für die unterschiedlichen Join-Arten, die ON Klausel habe ich der Übersicht weggelassen): aus TabelleA X JOIN TabelleB JOIN TabelleC wird ein ((TabelleA) JOIN TabelleB) JOIN TabelleC.

Java-Anwendungen mittels Ant-Script in eine Drop deployen

Im Anschluss an meine vorangegangenen Artikel über das automatische Builden und Deployen einer Java-Anwendung (als Jar, als Mac-Applikation und als Windows-Exe) folgt nun ein Howto, wie man eine Drop von drop.io automatisch aktualisieren kann.

Leider gibt es derzeit weder eine Java-API-Implementierung der drop.io API noch einen Ant-Build-Task, letzteres ist dann natürlich keine Überraschung. Daher verwende ich iOrb, ein drop.io command line interface, entwickelt in Ruby. Daher ist auf dem System Ruby zwingend erforderlich — für Alternativen bin ich selbstverständlich offen.

Nach einer beispiellos simplen Installation ist iorb als Command verfügbar. In meinem Falle beginnen die Deploy-Apps mit “application-”, daher reicht ein Pattern, um alle zu löschen.

<target name="deployToDropio" depends="makeAll">
  <!-- Delete all application assets -->
  <exec executable="iorb">
    <arg line="destroy ${drop.drop_name}:/application-*/ --force" />
  </exec>
  <!-- Upload new versions -->
  <exec executable="iorb">
    <arg line="add deploy/Application.jar --drop-name ${drop.drop_name}" />
  </exec>
  <exec executable="iorb">
    <arg line="add deploy/Application.exe --drop-name ${drop.drop_name}" />
  </exec>
  <!-- zip mac app dir -->
  <zip destfile="deploy/Application.app.zip" basedir="deploy/Application.app" />
  <exec executable="iorb">
    <arg line="add deploy/Application.app.zip --drop-name ${drop.drop_name}" />
  </exec>
</target>

Vor dem ersten Starten muss/will iOrb eine Profildatei in ~/.iorbrc anlegen. Dafür ist ein API-Token von drop.io notwendig.

Dort werden auch im Yaml-Format die angelegten Drops gespeichert. Falls man bereits im Vorfeld eine Drop angelegt hat, so kann man diese Datei auch später im einen Texteditor entsprechend bearbeiten. Dort werden auch die Passwörter und Tokens abgelegt.

Starten einer JAR unter Mac OSX – Bad version number?

Wir hatten dieses Thema bereits indirekt durch den ApplicationBundler im Beitrag vorletzter Woche besprochen.. aber hier nochmal zum eigentlichen Problem und dessen Lösung.

Der Fehler verursacht die aktuell ausgewählte Java-Version (und das ist bei den meisten Leuten der Standard Java 1.5.0). Wenn jedoch die Jar mit Java 1.6 kompiliert wurde, kann der 1.5-Loader damit nichts anfangen bzw. schützt aus naheliegenden Gründen vor der Ausführung. Zwar ist auf Mac OS X System der Version 10.5 auch Java 1.6 installiert, dieses ist jedoch kein Standard.

Es gibt dafür nun drei Möglichkeiten:

  1. Der Benutzer startet die JAR nicht mit Doppelklick bzw. dem Kommando “java -jar JarFile.jar” sondern mit der direkten Angabe im Frameworkverzeichnis.. diese Lösung ist inakzeptabel.
  2. Der Benutzer stellt seine Default-JVM auf 1.6 um.. das machen die Entwickler eh, aber der Benutzer wird auch damit überfordert sein. Auch keine akzeptable Lösung.
  3. Man erstellt eine Mac-Application mit entsprechenden Application-Bootloader und einer Art Manifest, wo eingestellt wird, welche Java-Version verwendet werden muss. Damit findet sich Java 1.6 sogar dann, wenn man als Default 1.4 eingestellt hat. Außerdem hat es den positiven Nebeneffekt, dass die “technische” Jar vor dem Benutzer durch ein Anwendungsymbol kaschiert wird.

Tags: ,