dokuWiki et intégration : SSO, annuaire, modèles & co

Publié le 06 juillet 2010 par Olivier Duval

Préambule

Avant de parler plus spécialement de dokuWiki , un point sur le mot intégration d'applications. Que signifie "intégrer une application ou un progiciel ou un module ou ..." et comment le permet-on ?

On peut avoir plusieurs niveaux d'intégration entre 2 applications (Web j'entends), toutes doivent être, si possible, transparentes pour l'utilisateur :

  • inclure des données d'une application A dans une application B : l'utilisateur se trouve sur la A, et des informations du site B sont incluses dans le 1er sous diverses formes : flux RSS, widget, OpenSocial, à l'aide d'une API (service Web SOAP, REST, XmlRPC, ...) ou non (fichiers, base de données ou LDAP partagés entre 2 ou N applis) de la part du site B (on peut considérer un flux comme une mini API),
  • à partir d'une application A, qu'un utilisateur puisse se connecter sur une application B pour y interagir (Google docs, Wiki, enquêtes, etc), avec bien souvent des droits bien particuliers (autorisations),

2 applications seules dans leur coin fonctionnent en général bien, mise à part tout le process de leur mise en place (développement etc), la difficulté devient exponentielle lorsqu'il s'agit d'intégrer de façon transparente pour l'utilisateur une application dans une autre pour y apporter un service supplémentaire.

L'intégration d'une application est souvent rendu possible à l'aide d'API sous forme de services Web (données, annuaire, ...) et aussi également par l'ajout d'un (Web)SSO qui permet à l'utilisateur de passer d'une application vers une application avec son même login et mot de passe et ce, sans se reconnecter s'il l'était déjà, s'authentifier qu'une seule fois est très important afin de faciliter l'usage des outils.

dokuWiki

Wiki

Une petite définition d'un Wiki, selon Wikipédia

Un wiki est un site web dont les pages sont modifiables par tout ou partie des visiteurs du site. Il permet ainsi l'écriture et l'illustration collaboratives de documents.

Wikipédia en est son meilleur représentant, une encyclopédie enrichie par une multitudes de contributeurs. Tous les moteurs de Wiki sont basés à peu près sur le même principe, c'est à dire des balises simples pour formater le texte d'une page (gras, liens, titres, ...), l'écriture de ce billet est effectué de cette façon, à l'aide de balises Wiki, d'autres moteur de blog implémentent Markdown ou Textile qui suivent le même principe .

La collaboration sur un document est effectuée contributeur par contributeur, c'est à dire qu'une page ne peut être éditée à plusieurs (elle sera bloquée si elle est éditée par une personne). Pour ce type de collaboration simultanée, Google docs est plus approprié (que l'on ait un compte google ou non), plus dédié à de court document je pense, à la "Word".

Exemples d'utilisation d'un Wiki :

  • élaboration d'une documentation sur un projet afin de garder un historique
  • documentation d'une plateforme
  • manuels utilisateur ou tutoriels

le choix de dokuWiki

Pourquoi dokuWiki ?

Parmi la pléthore de moteurs qui existent sur le marché OpenSource ou non, dokuWiki est intéressant par sa simplicité (pas de base de données par exemple) et surtout par son architecture qui le rend extensible et modulaire, que cela soit du point de vue de l'authentification, des modèles (de charte graphique), API (XmlRPC et API interne), avec bien entendu bon nombre de plugins proposés (> 600) (cf. les fonctionnalités), sans compter une architecture de développement de plugins bien pensée (évènements et hooks utilisées dans les actions).

L'un des points négatifs de mon point de vue étant qu'il soit développé en PHP mais on s'y remet vite aux $, mais aussi, des releases officielles peu fréquentes, malgré un trunk assez actif.

Principes d'authentification

dokuWiki propose un système d'authentification extensible, qui peut être étendu à d'autres protocoles d'authent (LDAP, CAS, Shibboleth, OpenID, NTLM, AD, spécifique, etc). Par défaut, dokuWiki gère les logins et mots de passe sous forme de fichier texte (le mode plain).

Il suffit de dériver de la classe auth_basic (l'interface/classe abstraite qui représente le contrat d'authent), d'implémenter les méthodes obligatoires et certaines optionnelles, et ensuite de préciser la stratégie utilisée pour l'authentification (la nouvelle classe dérivée d'auth_basic) dans le fichier de configuration (l'entrée $conf['authtype'] = 'maclasse') : dokuWiki se chargera d'instancier cette dernière et d'appeler les méthodes voulues qui sont précisées dans la classe dérivée, à l'aide du tableau cando définit dans la classe de base auth_basic.

Par exemple addUser à true provoquera l'appel de la méthode createUser($user,$pwd,$name,$mail,$grps=null) qui devra être implémentée.

Souvent, les méthodes utilisées permettent de se brancher sur une base de comptes existantes (bdd, LDAP, ...qu'on appellera référentielle) pour les besoins d'authent et d'annuaire (login mais aussi nom, prénom, email, ...), afin de lire le compte et/ou ses groupes d'accès ou bien de le modifier, la page authentication_backend précise les méthodes à utiliser le cas échéant.

Dans ce cas, nous avons bien de la fédération d'identités (en gros 1 login partagé par plusieurs applications, un référentiel hébergé ou réparti sur un ou plusieurs points) mais la bannière de login pourra être dupliquée sur l'application A, dokuWiki, etc, on ne répond pas dans ce cas au besoin de SSO : 1 login et ne pas se réauthentifier sur dokuWiki si on l'était déjà sur l'application A.

dokuWiki propose la méthode trustExternal qui surpasse toutes les autres (elles seront alors ignorées à quelques exceptions près), elle nous permettra d'implémenter le SSO. Son activation s'effectue via le tableau cando avec la clé external qu'on mettra à true dans le constructeur de la classe dérivée.

$this->cando['external'] = true;

Le SSO, comment ?

On peut voir au moins 2 manières d'implémenter du SSO, ou plus précisemment détecter une authentification existante sur un site tiers :

  • par l'usage d'un cookie de domaine, qui contiendra un jeton : nous dira si l'internaute est déjà passé par une application et qu'il s'est donc déjà authentifié : si non, soit une redirection s'effectuera afin de le rediriger vers un point central d'authent, soit sur l'application cliente elle-même. Cette solution est adapté aux applications qui se basent sur un même domaine, évite un aller-retour lors de l'arrivée de l'internaute (s'il était déjà connecté sur une application partenaire), le jeton contenu dans le cookie est bien évidemment à vérifier afin d'éviter toute faille de sécurité. S'il n'existe pas de cookie avec le jeton, il est redirigé vers l'application qui centralise l'authentification, se connecte, le cookie est fixé avec le jeton, puis il est redirigé vers l'application appelante dokuWiki.

Cela est schématisé par l'image suivante, si l'internaute était arrivé par l'application A, le cookie de domaine serait déjà garni lors de l'arrivée sur dokuWiki, cela réduit d'un aller-retour systématique, ce qui n'est pas le cas dans la solution par session ci-après

En revanche, une très grosse contrainte technique des navigateurs sur HTTP peut rendre difficile son utilisation, comme le précise le RFC 2109, section 4.3.2 Rejecting Cookies

A Set-Cookie from request-host y.x.foo.com for Domain=.foo.com would be rejected, because H is y.x and contains a dot

autrement dit, cela interdit les sous-domaines qui contiendraient un . (point ou dot), d'aller plus loin de 2 niveaux que domain.ntld. Par exemple www.info.domain.ntld et www.finance.domain.ntld ne pourront pas partager le même cookie de domaine (qui serait fixé sur domain.ntld), alors que pour docs.google.com et calendar.google.com cela est rendu possible. Cette contrainte devrait être levée dans les futures versions des navigateurs, en attendant, il faudra trouver une autre solution.

  • en jouant sur l'ouverture de session, puis ensuite par un jeu de redirection : la 1ère fois que l'internaute arrive sur dokuWiki, à l'ouverture de session, il est instantanément redirigé vers la plateforme/application qui centralise l'authentification (application A), vers une page spéciale qui gèrera ensuite le retour vers dokuWiki : si l'internaute était connecté sur application A alors il est immédiatement redirigé vers dokuWiki, sinon il se connecte puis est redirigé vers dokuWiki. L'URL de retour contient notamment en paramètre un jeton de type HMAC , avec une clé secrète partagée entre l'application qui gère le login et dokuWiki afin de s'assurer de qui envoie les informations vers le Wiki lors de l'authentification, notamment la clé utilisateur (id, email, ...). Ce type de challenge est assez courant, on retrouve ce principe de redirections sur OpenID ou OAuth.

le principe :

Même si elle impose automatiquement une redirection, la 2è solution est moins contraignante, nous choisirons donc cette dernière pour dokuWiki.

Dans les 2 cas, le principe se base sur de la redirection HTTP qui joue un challenge entre le site client et le site qui permet de s'authentifier. L'URL de retour vers le Wiki contient le jeton HMAC, ainsi qu'une clé identifiant l'utilisateur qui s'est authentifié, clé qui pourra être utilisé dans un service d'annuaire pour en connaitre plus sur lui (nom, prénom, email). On pourra ajouter également la date/heure de connexion de l'utilisateur afin de limiter l'usage du lien / jeton dans le temps.

Concernant les groupes d'accès, dokuWiki stocke le(s) groupe(s) dans un fichier texte ~dokuwiki/conf/acl.auth.php, la clé de configuration ACL doit être activée $conf['useacl']=1; . On pourra s'aider d'un service d'habilitations, inclus ou non dans le service d'annuaire, afin de déterminer à quels groupes appartient l'utilisateur qui arrive sur le Wiki.

un exemple de groupes d'accès, déclinés selon les droits dokuWiki :

*       @ALL    0
*       @rpc    4
*       @geeks  16
*       @geeks%2dro     1

Le groupe geeks a tous les droits d'édition sur les pages, ainsi que sur les fichiers, pour une population de contributeurs. Le groupe rpc pourrait être affecté lors de l'accès à l'API (XmlRPC ou la syndication) par un système tiers, avec des droits moins forts (pas de suppression par exemple). Le groupe geeks-ro (les caractères du type - doivent être fournis en code hexa) pourrait être réservé pour des utilisateurs qui n'ont droit qu'à une consultation seule (et non à l'édition). Tout le reste (@ALL) aucun droit.

exemples de code

On aura besoin : de la classe dérivée, de son constructeur, et de la méthode trustExternal qui contiendra l'initialisation de l'utilisateur et groupes appartenant à dokuWiki. Hormis l'authentification et l'affectation des groupes, l'authent est souvent couplé à un service d'annuaire, afin de connaitre a minima l'email, le nom, prénom et civilité du connecté. A défaut de service d'annuaire (SOAP, REST), ces derniers pourraient être renvoyés en même temps que l'URL de retour, en tant que paramètres.

Ce ne sont que des bouts de code pour démontrer le principe en 3 étapes, le principal étant dans trustExternal

Les parties dont on a besoin :

la classé dérivée que nous nommerons auth_provider, hérite de auth_basic, dokuWiki sait quel provider instancier par la clé de configuration $conf['authtype']='provider', la classe sera contenue dans le fichier ~dokuwiki/inc/auth/provider.class.php

class auth_provider extends auth_basic
{
  ...
}

son constructeur

function auth_provider() {
      $this->cando['external'] = true; // on utilise le mode external pour l'authent, seule trustExternal sera appelée de façon implicite
      $this->cando['logoff'] = true; // pour usage manuel, vidage cookie et session, permet d'ajouter à une page ?do=logout pour un logoff
 
      $this->success = true;
      return;
    }

et la méthode trustExternal pour l'initialisation de l'utilisateur dokuWiki avec ses groupes si le challenge SSO a réussi

function trustExternal($user,$pass,$sticky=false)
 {     
      // session déjà ouverte ? on initialise l'utilisateur dokuWiki
      if(isset($_SESSION[DOKU_COOKIE]['auth']['buid']))       
      {
            $this->_setUser($_SESSION[DOKU_COOKIE]['auth']['buid']);
      } else { // retour de la redirection de l'appli de login ?
                        if($_GET["token"]!=null)
            {
              // vérif signature / clé
              if($this->_verifSignature($_GET["token"]) & isset($_GET['ui'])))
              {                                       
                  // appel à un service d'annuaire pour l'obtention des infos utilisateurs
                  $response = $this->annuaireGetUser($_GET['ui']);
                  if($response)
                   {
                     // init utilisateur dokuWiki
                                       $this->_setUser($response->user);
                  }
              }
                        // non alors redirection vers l'app login              
            } else {                           
                              header("Location: http://"APPLICATION_A"/logon/?return_url="rawurlencode("http://"SERVEUR_WIKI$_SERVER['REQUEST_URI']));
                        }
 
      }
 
      $this->success = false;
      return false;   
 }
 
  // initialise l'utilisateur dokuWiki
  function _setUser($user)
  {             
         global $USERINFO; // l'utilisateur dokuWiki
     $grps = array();   // groupe(s) de l'utilisateur
 
          // profil utilisateur dokuWiki         
     $USERINFO['name'] = $user->Nom;
     $USERINFO['mail'] = $user->Email;     
     array_unshift($grps,$user->groupe);         
     $USERINFO['grps'] = $grps;
 
          // obligatoire
     $_SERVER['REMOTE_USER'] = $user->Email;
 
     // session de cache dokuWiki     
     $_SESSION[DOKU_COOKIE]['auth']['user'] = $user->Nom;
     $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO;
     $_SESSION[DOKU_COOKIE]['auth']['time'] = time();
  }

et voilà !

Le SSO est une réelle valeur ajoutée pour l'utilisateur, quoique plus désarmant de devoir se connecter plusieurs fois, voire, avec un compte différent. Dans un autre contexte, OpenID, permet également de fédérer son identité / authentification, afin de ne pas recréer pour une ième fois un login et mot de passe.

Modèles, plugins

dokuWiki, dans sa conception permet d'étendre et de développer son propre modèle de charte. Le mieux étant de parti d'un template existant et de le modifier. Le modèle est stocké sur ~dokuwiki/lib/tpl/<template>.

Egalement, le nombre de plugins proposé est assez conséquent, 650 ! bien entendu, on veillera à ce que le plugin soit maintenu, que ce dernier ait des contributeurs actifs. Les plugins seront à installer dans le répertoire ~dokuwiki/lib/plugins/<plugin>. Chaque plugin est désactivable par la simple création d'un fichier vide disabled. Pour l'authentification, il nous faudra au moins le plugin acl. Parmi les intéressants, on notera tag qui permet de marquer une page par des tags et de retrouver toutes les pages relatives à un tag, mais aussi note ou odt, il reste le plugin PDF à tester.