Das Problem: Es sollen individuelle SVN Verzeichnisse erstellt werden. Ein Bereich also, in dem eine Person beliebig viele Repositories erstellen kann, die nur von ihr selbst angesehen und bearbeitet werden können. Mann könnte jetzt meinen – vorallem mit viel jugendlichem Leichtsinn – das sei ein einfaches, allgemein bekanntes Problem. Pustekuchen! Das muss ein ganz fremder wohl ganz abstruser Wunsch sein.
Verschiedene Dinge schränken die Chance ein, eine einigermassen vernünftige und allgemeine Lösung zu finden 🙁 .Doch der Reihe nach. Was ist gegeben:
- Ein wunderbarer Server, mit SSL und allem.
- Das Einrichten eines neuen Users sollte ungefähr 0,01 Sekunden menschlicher Arbeitskraft bedürfen.
- Die Benutzer sind in einem Active Directory gespeichert.
- Apache2, PHP, und alles was man will ist installiert.
Einschränkende Dinge
Die grösste Peinlichkeit ist die Direktive SVNParentPath (von mod_dav_svn). An sich sollte die Direktive ein Basisverzeichnis für die SVN-Repositories angeben aber:
- Der Parameter (das Verzeichnis) ist absolut undynamisch. Er nimmt keine Umgebungsvariable, Regexp oder sonst was, sondern ganz stur nur einen festen Pfad.
- Kann nicht in einer .htaccess Datei gesetzt werden
Gna! Das ist echt 2002! So wird das nichts mit dynamisch erstellten SVN-Verzeichnissen. Aus reinem Masochismus hane ich verschiedene Lösungen wie mod_rewrite, symbolischen Links, LocationMatch und alles was sonst noch so Zeit kostet ausprobiert. Keine Chance…
Erschwerend hinzu kommt, dass sich SVN überhaupt nicht mit der Alias Direktive anfreunden kann und konsistent stur auf eine jungfräuliche Location beharrt: Der Versuch wird mit einem Repository moved permanently, please relocate belohnt.
Die Krönung: mod_authz_path
mod_authz_path klingt vielversprechend. An Hand des Pfades sollte der User und das Realm festgelegt werden können. Jaaa, aber nicht als Location, sondern nur als Pfad (<Directory> im Apache), wie man mit dem Debuggen feststellen kann:
AuthNamePathMatch (.+) "Persoenliches SVN $1"
AuthzUserPathMatch (.+) $1
Mit dem Pfad findet sich aber – wie schon weiter oben geflucht – das SVN nicht zurecht.
Ganz nebenbei hat mich dieses Modul etwa viertausend Stunden gekostet. Wenn es geladen wird, funktioniert authnz_ldap nicht mehr richtig. Nicht überhaupt nicht, sondern einfach das require Statement spielt verrückt und verhält sich unberechenbar. Nach langer Zeit habe ich dann das auch gemerkt :irre: . Aber erst nachdem ich unter viel Wehklagen in einer Panik die anderen Server getestet habe, ob die Authentifizierung dort auch nur scheinbar funktioniert.
Die Lösung
Nach ein paar Jahren die Lösung: Gutes, altes PHP.
Auf dem SSL-Port des Servers ist eine Website, auf der sich die User mal als erstes über Apache am Active Directory authentifizieren müssen. Dadurch habe ich Zugriff auf den Benutzername mittels $_SERVER['AUTHENTICATE_SAMACCOUNTNAME']. Loggt sich jemand ein den wir noch nicht kennen, dann:
- Wird der svn-Grundpfad erstellt, wenn er noch nicht existiert.
- Wird eine Apache-Konfiguration für diesen neuen Grundpfad erstellt, die das ParentPath und den entsprechenden User beinhaltet.
- Wird Apache neu gestartet → und der Benutzer währenddessen zum Warten gebracht. Das ist leider notwendig zum Einlesen der Konfigurationserweiterung.
Die neue Konfigurationsdatei wird „lokal“ erstellt und in der Grundkonfiguration /etc/apache2/sites-available/isvn.myhost.ch eingebunden:
Include /data/www/isvn.myhost.ch/config/svn.conf
PHP Code für die neuen Benutzer:
$svn_user=strtolower($_SERVER['AUTHENTICATE_SAMACCOUNTNAME']);
if(!$svn_user) { echo "Hacker, you will be disintegrated!"; exit(1); }
$svn_root=SVNROOT
."/".$svn_user;
if(!is_dir($svn_root)) { // NEW USER
// Make svn Parent Path
mkdir($svn_root);
// Write Config
$fh = fopen(SVNCONF
, 'a') or
die("can't open file");
$svnDirective =<<<EOD
<Location /svn/lp/$svn_user>
DAV svn
SVNParentPath /data/svn/lp/$svn_user
AuthName "Persoenliches SVN von $svn_user"
AuthType Basic
AuthBasicProvider ldap
AuthLDAPBindDN ldapanonymous@myhost.local
AuthLDAPBindPassword g3h31M3Sp+
AuthLDAPUrl ldap://adc1.myhost.local/ou=Accounts,dc=myhost,dc=local?sAMAccountName?sub
require ldap-user $svn_user
</Location>
EOD;
fwrite($fh, $svnDirective);
fclose($fh);
// Make th User wait
echo "<html><head><title>SVN wird eingerichtet</title>\n";
echo '<meta http-equiv="refresh" content="5; URL=https://isvn.myhost.ch">'."\n";
echo "</head><body>".implode(" ", explode(".",$_SERVER['AUTHENTICATE_SAMACCOUNTNAME'])).": Ihr SVN Grundverzeichnis wird erstellt. Bitte warten Sie.</body></html>";
// Restart Apache
exec('echo "/usr/bin/sudo /etc/init.d/apache2 restart" | /usr/bin/at now');
exit();
}
Damit der User www-data den Apache restarten darf, muss ihm das per /etc/sudoers erlaubt werden:
www-data ALL=NOPASSWD: /etc/init.d/apache2
Dass die Berechtigungen auch für alle benötigten Zusatzdateien reichen, machen wir den Trick mit dem at.
Somit haben wir also eine hochindividuelle Konfigurationsdatei für alle User und Ihr SVN Grundverzeichnis. Die PHP-Seite ermöglicht es, SVN-Repositories anzulegen und zeigt auch gerade den annektierten Platz an:

Die SVN-Steuerkonsole
Die Quellen
Hier sind die PHP-Quellen. Für weitere Details stehe ich gerne zur Verfügung.