WebSVN pour dépôts publics et privés en même temps

Category: Configuration,PHPPh. Ivaldi @ 11 h 16 min

Sommaire de la page

Résumé

Suite à l'article décrivant, entre autre, comment utiliser SVN dans un tunnel SSH nous verrons ici comment configurer le serveur Apache pour donner un accès anonyme en lecture seul aux dépôts publics et configurer WebSVN de tel sorte que les dépôts publics et privés se trouvent sur la même page WebSVN sans avoir à entrer de mot de passe pour accéder aux dépôts publics ; ce qui n'est pas un comportement prévu originellement par WebSVN.
À la fin de cet article vous serez donc en mesure de configurer une instance WebSVN comme celle de ma page de projets sous contrôle SVN (j'utilise git maintenant mais l'article reste valable) pour laquelle j'ai créé un projet privé fictif accessible seulement à l'utilisateur demo, sans mot de passe.

Structure du dépôt

Dans cet article, on va supposer que les dépôts Subversion sont dans Quelque_Part/svn/repos/ ; ce répertoire contient lui même deux sous-répertoires :

  • Quelque_Part/svn/repos/publics contient les dépôts accessibles :
    • en lecture par tout le monde, via SVN ou WebSVN ;
    • en écriture pour ceux dont la clef SSH a été autorisée et seulement via SSH.
  • Quelque_Part/svn/repos/private contient les dépôts accessibles :
    • en lecture (via WebSVN) sur authentification par mot de passe ;
    • en écriture seulement pour ceux dont la clef SSH a été autorisée ;

Configurations

Dans la première partie nous présenterons une configuration très standard de WebSVN puis, dans la seconde partie, sera exposé une configuration plus ergonomique. Si vous n'êtes pas familier avec le fonctionnement de WebSVN, il est fortement conseillé de lire la première partie.

Configuration Standard

Cette configuration se retrouve partout sur le Web, elle n'a rien de bien originale. En revanche, elle n'ait pas toujours bien expliquée et je vais essayer d'éclaircir son fonctionnement.

Configuration de base d'Apache

Afin que WebSVN, qui est écrit en PHP faut-il le rappeler, puisse accéder à la configuration des dépôts SVN il faut installer le module libapache2-svn par la commande apt-get install libapache2-svn. L'utilisation de ce module permet d'accéder aux dépôts SVN à travers le protocole HTTP ; voici comment l'utiliser dans la configuration d'Apache.

<Location /public>
   Options Indexes +FollowSymLinks MultiViews
   DAV svn
   SVNParentPath Quelque_Part/svn/repos/public/
   SVNListParentPath on
   allow from all
   <LimitExcept GET PROPFIND HEAD OPTIONS REPORT CHECKOUT>
      deny from all
   </LimitExcept>
</Location>
  

Le module est chargé par l'instruction DAV svn ;
SVNParentPath Quelque_Part/svn/repos/public/ définit le répertoire, dit parent, contenant les dépôts ;
L'instruction SVNListParentPath on autorise à lister l'ensemble des dépôts du répertoire parent ; allow from all pour que tout le monde puisse accéder aux dépôts ;

<LimitExcept GET PROPFIND HEAD OPTIONS REPORT CHECKOUT>
   deny from all
</LimitExcept>
interdit tout accès sauf les opérations de lecture du dépôt.

Pour les dépôts privés voici comment l'on peut configurer Apache :
<Location /private>
   DAV svn
   SVNParentPath Quelque_Part/svn/repos/private/
   SVNListParentPath on

   Require valid-user
   AuthType Basic
   AuthName "Private Subversion Repository"
   AuthUserFile /etc/apache2/passwords/svn-access.pwd
</Location>
  

Les instructions sont identiques aux précédentes sauf qu'ici on spécifie qu'il est obligatoire d'être authentifié pour accéder aux dépôts. Pour créer le fichier d'authentification on utilise la commande
htpasswd -c /etc/apache2/passwords/svn-access.pwd Nom_Utilisateur
Pour ajouter un utilisateur il suffit de taper la même commande sans l'option -c. Plus d'informations en faisant htpasswd --help
Avec une telle configuration on peut accéder à tous les dépôts publics à l'adresse http://VOTRE_NOM_DE_DOMAINE/public/ sans authentification et à tous les dépôts privés à l'adresse http://VOTRE_NOM_DE_DOMAINE/private/ après authentification.
Il est aussi possible de récupérer la dernière version d'un dépôt public avec la commande
svn co http://VOTRE_DOMAINE/public/NOM_DU_DEPÔT/.
Toutes tentatives d'accès via svn aux dépôts privés sera rejetées ! Il ne reste plus qu'à faire communiquer Apache et SVN (via le programme svnserver) pour que l'utilisateur authentifié par Apache hérite de droits éventuels spécifiques aux dépôts. C'est l'objet de la section suivante.

Communication entre Apache et SVN

Nous voudrions maintenant que l'utilisateur demo, authentifié par Apache via le fichier svn-access.pwd, puisse voir le dépôt privé demonstration mais pas les autres dépôts... Rien de plus simple...
Il suffit de créé, par exemple, le fichier Quelque_Part/svn/repos/authz contenant les lignes suivantes

[demonstration:/]
demo=r
  

et de rajouter la ligne AuthzSVNAccessFile "Quelque_Part/svn/repos/authz" dans la configuration d'apache:

<Location /private>
   DAV svn
   SVNParentPath Quelque_Part/svn/repos/private/
   SVNListParentPath on

   Require valid-user
   AuthType Basic
   AuthName "Private Subversion Repository"
   AuthUserFile /etc/apache2/passwords/svn-access.pwd
   AuthzSVNAccessFile "Quelque_Part/svn/repos/authz"
</Location>
  

Après avoir redémarré le serveur Apache (par apache2ctl graceful sur un serveur en production) on doit pouvoir accéder au dépôt http://VOTRE_NOM_DE_DOMAINE/private/demonstration après authentification mais l'utilisateur demo n'aura pas accès aux autres dépôts, même en lecture.
En revanche, il ne semble plus possible d'avoir la liste des dépôts accessibles depuis l'adresse http://VOTRE_NOM_DE_DOMAINE/private/, si quelqu'un sait comment y remédier qu'il n'hésite pas à me contacter ou à laisser un commentaire...
Plus d'informations sur les autorisations basées sur le « path » se trouvent ICI. Attention, comme cela est souligné dans l'article, c'est gourmand en ressource.
Pour ma part, je ne donne aucun accès aux dépôts privés en http(s) puisque les utilisateurs de cet espace travaillent dans un tunnel ssh avec authentification par clé.

Configuration d'Apache pour WebSVN

Après avoir installé WebSVN, par apt-get install websvn il faut configurer Apache. En exclusivité pour PIPRIME.FR, voici la configuration utilisée par PIPRIME.FR sur svn.piprime.fr :

<VirtualHost *:80>
   ServerName svn.piprime.fr
   ServerAdmin adresse_courriel@sfr.fr
   DocumentRoot /usr/share/websvn

   <Directory />
      AllowOverride None
      Order allow,deny
      deny from all
   </Directory>

   <Directory /usr/share/websvn>
      AllowOverride None
      DirectoryIndex index.php
      Options FollowSymLinks
      Order allow,deny
      Allow from all
      php_admin_value safe_mode off
   </Directory>

   <Location /public>
      Options Indexes +FollowSymLinks MultiViews
      DAV svn
      SVNParentPath Quelque_Part/svn/repos/public/
      SVNListParentPath on
      AuthType Basic
      AuthName "Subversion Repository"
      AuthUserFile /etc/apache2/passwords/svn-access.pwd
      order allow,deny
      allow from all
      <LimitExcept GET PROPFIND HEAD OPTIONS REPORT CHECKOUT>
         deny from all
      </LimitExcept>
   </Location>

   # Logs:
   LogLevel warn
   ErrorLog /var/log/apache2/svn.piprime.fr.error.log
   CustomLog /var/log/apache2/svn.piprime.fr.access.log combined
   ServerSignature On
</VirtualHost>
  
Il n'y a rien de spécial à ajouter, il faut maintenant configurer WebSVN à proprement dit.

Configuration de WebSVN

La configuration de WebSVN ne semble pas poser pas de problèmes particuliers, les comentaires sont assez explicites :

<?php
// Pour avoir la coloration syntaxique du code
$config->setEnscriptPath("/usr/bin");
$config->setSedPath("/bin");
$config->useEnscript();

// La racine des dépôts publics
$config->parentPath("Quelque_Part/svn/repos/public");
// La racine des dépôts privés
$config->parentPath("Quelque_Part/svn/repos/private");
// Le fichier d'autorisation/authentification SVN
$config->useAuthenticationFile('Quelque_Part/svn/repos/authz');

// Le gabarit utilisé
$config->setTemplatePath("$locwebsvnreal/templates/calm/");
/* $config->setTemplatePath("$locwebsvnreal/templates/BlueGrey/"); */

// Pour ne pas avoir l'arborescence complète du projet pendant la navigation
// L'affichage est plus rapide avec ce comportement...
$config->useFlatView();

// Les projets sont, en général, codés en utf-8.
$config->setContentEncoding('utf-8');

// Autorise la génération des « tarballs »
$config->allowDownload();
// Dès le niveau 1
$config->setMinDownloadLevel(1);

// La coloration des fichier Asymptote .asy est la même que celle du C.
$extEnscript[".asy"] = "c";

// Parce que c'est obligatoire ;-)
set_time_limit(0);

// Number of spaces to expand tabs to in diff/listing view across all repositories
$config->expandTabsBy(8);
?>
  

Le prolème de cette configuration est le suivant :

  • en l'état actuel, vous ne verrez jamais aparaître les dépôts pour lesquels il faut être authentifié ; typiquement tous les dépôts de private pour lesquels vous aurez pris soin de désigner un groupe d'utilisateurs autorisés à lire le dépôt.
  • si on met en place le système d'authentification d'Apache dans <Directory /usr/share/websvn> il faudra s'identifier même pour accéder aux dépôts publics.

Pour ces raisons, j'ai décidé d'implémenter dans WebSVN mon propre système d'authetification. C'est bien ce que d'aucun appelleront un hack mais si quelqu'un a mieux à proposer surtout qu'il n'hésite pas à le partager...

Configuration « à la PIPRIME »

En conservant la configuration d'Apache que j'ai donné précédemment, on va modifier le gabarit et la configuration de WebSVN pour implémenter une authentification en PHP en essayant de modifier le moins possible le comportement original de WebSVN.
Le but, rappelons le, est d'avoir, sur la même instance/page de WebSVN, les projets publics accessibles sans authentification et les projets privés accessibles après authentification.

Commençons par le gabarit/template de WebSVN ; on y introduit le nouveau tag [websvn:CONNECTMESSAGE]. Je vous laisse adapter mon gabarit /usr/share/websvn/templates/calm/index.tmpl à votre goût mais il faut y trouver le tag en question.

<div id="select">
     [websvn:lang_form]
     <div>
        [websvn:lang_select]<span class="submit">[websvn:lang_submit]</span>
     </div>[websvn:lang_endform]
</div>

<h6 style='color:gray;text-align:left;font-weight:normal;line-height:1.7em;border-bottom:1px solid gray;padding:10px 5px 10px 5px;margin:-1.7em 2% 1em 2%;'>[websvn:CONNECTMESSAGE]</h6>

<div id="info">
<h2>À propos</h2>
Projets développés et hébergés sur <a href="http://www.piprime.fr">PIPRIME.FR</a>.<br />
Identifier vous pour accéder aux projets privés<br />
<i>(user=demo sans mot de passe pour tester cette fonctionalité)</i>.
<dl>
  <dd>En savoir plus sur Subversion à <a href="http://subversion.tigris.org">subversion.tigris.org</a>.</dd>
</dl>
</div>

<div id="wrap">
<h2 class="regular">[lang:PROJECTS]</h2>

[websvn-test:flatview]
    <dl class="projectlist">
      <dt>[lang:PROJECTS]:</dt>
      [websvn-startlisting]
      <dd>[websvn:projlink]</dd>
      [websvn-endlisting]
    </dl>
[websvn-else]
    <div class="projectlisthead">[lang:PROJECTS]:</div>
	<div class="projectlist">
	[websvn-startlisting]
	[websvn-test:isprojlink]
		<div class="project">[websvn:listitem]</div>
	[websvn-else]
		[websvn:listitem]
	[websvn-endtest]
	[websvn-endlisting]
	</div>
[websvn-endtest]
</div>
  

On crée ensuite un fichier, disons pi-auth.php, accessible à Apache (par exemple dans /usr/share/websvn/) et qui permet de s'authentifier, de se déconnecter et d'initialiser principalement la variable de session $_SESSION['REMOTE_USER']. Attention, Il faut configurer dans ce fichier le tableau $users qui contient les noms des utilisateurs et leur mot de passe correspondant.

<?php
session_start();

//utilisateur => mot de passe
$users = array('USER1' => 'PASS1',
               'USER2' => 'PASS2');

if(!isset($_SESSION['HTTP_REFERER'])) {
  $_SESSION['HTTP_REFERER'] = $_SERVER['HTTP_REFERER'];
}

# Come back
function comeback($ref) {
  header('Location: '.$ref);
  exit;
}

function reset_session() {
  $ref=$_SESSION['HTTP_REFERER'];
  $_SESSION = array();
  unset($_COOKIE[session_name()]);
  session_destroy();
  session_start();
  $_SESSION['HTTP_REFERER']=$ref;
}

function retry_message() {
  echo '<h2>Vous n\'êtes pas autorisé à continuer...</h2>';
  echo '[<a href=\'' . $_SERVER['PHP_SELF'] . '\'>S\'identifier à nouveau</a>]';
}

# LOGOUT
if (isset($_GET['logout']) && isset($_SESSION['REMOTE_USER'])) {
  $ref=$_SESSION['HTTP_REFERER'];
  reset_session();
  comeback($ref);
}

if (!isset($_SERVER['PHP_AUTH_USER']) ||
    (!isset($_SESSION['REMOTE_USER']) && !$_SESSION['login']))
  {
    header("WWW-Authenticate: Basic realm=\"Authentification WebSVN PIPRIME.FR\"");
    header("HTTP/1.0 401 Unauthorized");
    $_SESSION['login']=true;
    retry_message();
    exit;
  } else {
  $httpd_username = filter_var($_SERVER['PHP_AUTH_USER'],
                               FILTER_SANITIZE_STRING,
                               FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW);
  $httpd_password = filter_var($_SERVER['PHP_AUTH_PW'], FILTER_SANITIZE_STRING,
                               FILTER_FLAG_ENCODE_HIGH|FILTER_FLAG_ENCODE_LOW);
  if(isset($users["$httpd_username"]) &&
     $httpd_password == $users["$httpd_username"])
    {
      // utilisateur et mot de passe valide
      $_SESSION['REMOTE_USER'] = $httpd_username;
      comeback($_SESSION['HTTP_REFERER']);
    }
  else
    {
      reset_session();
      echo '<h1>Mauvaise Identité</h1>';
      retry_message();
    }
}

?>
  

Il ne reste plus qu'à définir la valeur du fameux tag [websvn:CONNECTMESSAGE] et positionner la valeur de la variable d'environnement $_SERVER['REMOTE_USER'] sur $_SESSION['REMOTE_USER'] dans le fichier de configuration de WebSVN /etc/websvn/config.php.
Pour se faire, il suffit d'insérer au début du fichier config.php donné précédemment le code PHP suivant :

session_start();

if(isset($_SESSION['REMOTE_USER'])) {
  $_SERVER['REMOTE_USER']=$_SESSION['REMOTE_USER'];
  $vars['CONNECTMESSAGE'] = '<a href=\'pi-auth.php?logout\'>Se déconnecter</a>';
} else {
  $vars['CONNECTMESSAGE'] = '<a href=\'pi-auth.php?login\'>S\'identifier</a>';
}
  

Ne pas oublier de faire correspondre les noms d'utilisateurs de l''authentification de pi-auth.php avec ceux du fichier Quelque_Part/svn/repos/authz
Vous devez obtenir le comportement de ma page de projets sous contrôle SVN pour laquelle j'ai créé un projet privé fictif accessible seulement à l'utilisateur demo, sans mot de passe ; je vous laisse tester...

Netographie

Étiquettes : , ,