« Authentification Frankiz » : différence entre les versions

De WikiBR
m (Bugs vécu -> bugs vécus)
Aucun résumé des modifications
 
(15 versions intermédiaires par 6 utilisateurs non affichées)
Ligne 1 : Ligne 1 :
{{Archive|Frankiz ne fonctionne plus.}}
Frankiz permet à des sites externes (binets en particulier) d'utiliser sa base de données pour authentifier les utilisateurs.
Frankiz permet à des sites externes (binets en particulier) d'utiliser sa base de données pour authentifier les utilisateurs.


Le webmaster du site en question doit auparavant faire une demande de mise en place de ce système en envoyant un mail à l'équipe frankiz ([mailto:web@eleves.polytechnique.fr web@eleves.polytechnique.fr]).
Le webmaster du site en question doit auparavant faire une demande de mise en place de ce système en envoyant un mail à l'équipe frankiz ([mailto:web@eleves.polytechnique.fr web@eleves.polytechnique.fr]).
Il doit alors donner :
Il doit alors donner :
* l'url complète de la page de login de son site
* l'url complète de la page de login de son site (plus précisément, l'URL de callback)
* les informations auxquelles il souhaite accéder
* les informations auxquelles il souhaite accéder
* une justification de la demande (et oui, on ne partage évidemment pas notre base de données avec n'importe qui !)
* une justification de la demande (et oui, on ne partage évidemment pas notre base de données avec n'importe qui !)


Si la demande est acceptée, un web va alors inscrire ton site dans notre base de donnée de sites externes et générer une clé qu'il va te communiquer. Cette opération se déroule via l'interface d'administration de frankiz. Cette clé va te permettre d'authentifier les échanges d'informations entre frankiz et le site externe.
Si la demande est acceptée, un admin Frankiz va alors inscrire ton site dans notre base de donnée de sites externes et générer une clé qu'il va te communiquer. Cette opération se déroule via l'interface d'administration de frankiz. Cette clé va te permettre d'authentifier les échanges d'informations entre frankiz et le site externe.


==Liste des informations disponibles==
Tu peux demander l'accès aux informations
* names : prénom, nom, surnom, identifiant Frankiz
* email : adresse mail
* photo : URL vers la photo TOL
* promo : nom du premier groupe promo
* sport : nom de la section sportive (si applicable)
* rights : droits dans les groupes associés
* binets_admin : liste des binets dont l'utilisateur est administrateur
==Code de la page d'identification en PHP==
Ci dessous un exemple commenté de script d'authentification, à mettre sur le site externe :
Ci dessous un exemple commenté de script d'authentification, à mettre sur le site externe :


==Code de la page d'identification==
<nowiki>
 
<?php
<code php>
include 'fkz_auth.php';
<?php
if(!isset($_GET['response'])){
include 'fkz_auth.php';
  frankiz_do_auth();
if(!isset($_GET['response'])){
}
  frankiz_do_auth();
$auth = frankiz_get_response();
}
// et voila !
$auth = frankiz_get_response();
// les données sont stockées dans $auth = array(key => value, ...);</nowiki>
// et voila !
// les données sont stockées dans $auth = array(key => value, ...);
</code>


==Code des fonctions utilisées dans ''fkz_auth.php''==
==Code des fonctions utilisées dans ''fkz_auth.php''==


<code php>
<nowiki>
 
<?php
<?php
/**
/**
  * Clé secrète fournie par l'équipe frankiz lors de l'inscription du site.
  * Clé secrète fournie par l'équipe frankiz lors de l'inscription du site.
  * Cette clé sert à signer les requêtes et à authentifier le site.
  * Cette clé sert à signer les requêtes et à authentifier le site.
  */
  */
$FKZ_KEY = "000";
$FKZ_KEY = "000";
 
function frankiz_do_auth(){
function frankiz_do_auth(){
    global $FKZ_KEY;
  global $FKZ_KEY;
  /**
  /**
    * Prendre le timestamp permet d'éviter le rejet de la requête
  * Prendre le timestamp permet d'éviter le rejet de la requête
    */
  */
  $timestamp = time();
  $timestamp = time();
  /**
  /**
    * url de la page de login, doit correspondre *exactement* à celle entrée dans
  * url de la page de login, doit correspondre *exactement* à celle entrée dans
    * la base de données de Frankiz (définie lors de l'inscription)
  * la base de données de Frankiz (définie lors de l'inscription)
    */
  */
  $site = 'http://monsite/login';
  $site = 'http://monsite/login';
  /**
  /**
    * Champ non utile pour l'authentification et retransmis tel quel par frankiz.  
  * Champ non utile pour l'authentification et retransmis tel quel par frankiz.  
    * Il est prévu pour pouvoir mettre en place un système de redirection après  
  * Il est prévu pour pouvoir mettre en place un système de redirection après  
    * authentification, vers la page à partir de laquelle le client avait tenté de se connecter.
  * authentification, vers la page à partir de laquelle le client avait tenté de se connecter.
    */
  */
  $location  = "..."  
  $location  = "...";
  /**
  /**
    * Nature de la requête.
  * Nature de la requête.
    * Fkz renverra ici à la fois les noms de la personne mais aussi ses droits dans différents groupes.
  * Fkz renverra ici à la fois les noms de la personne mais aussi ses droits dans différents groupes.
    * Il faut cependant que le site ait les droits sur les informations en question (à définir lors de son inscription).
  * Il faut cependant que le site ait les droits sur les informations en question (à définir lors de son inscription).
    */
  */
  $request = json_encode(array('names', 'rights', 'email', 'sport', 'promo', 'photo'));
  $request = json_encode(array('names', 'rights', 'email', 'sport', 'promo', 'photo'));
 
  $hash = md5($timestamp . $site . $FKZ_KEY . $request);
 
  $remote  = 'https://www.frankiz.net/remote?timestamp=' . $timestamp .
      '&site=' . $site .
      '&location=' . $location .
      '&hash=' . $hash .
      '&request=' . $request;
  header("Location:" . $remote);
  exit();
}
 
function frankiz_get_response(){
  global $FKZ_KEY;
  // Read request
  $timestamp = (isset($_GET['timestamp']) ? $_GET['timestamp'] : 0);
  $response  = (isset($_GET['response'])  ? urldecode($_GET['response'])  : '');
  $hash      = (isset($_GET['hash'])      ? $_GET['hash']      : '');
  $location  = (isset($_GET['location'])  ? $_GET['location']  : '');
 
  // Frankiz security protocol
  if (abs($timestamp - time()) > 600)
      die("Délai de réponse dépassé. Annulation de la requête");
  if (md5($timestamp . $FKZ_KEY . $response) != $hash)
      die("Session compromise.");
    
    
   $hash = md5($timestamp . $site . $FKZ_KEY . $request);
   $response = json_decode($response, true);
    
  $response['location'] = $location;
   $remote  = 'https://www.frankiz.net/remote?timestamp=' . $timestamp .
 
      '&site=' . $site .
   // Set empty fields
      '&location=' . $location .
   $fields = array('hruid',
      '&hash=' . $hash .
    'firstname', 'lastname', 'nickname',
      '&request=' . $request;
    'promo', 'photo', 'location');
     header("Location:" . $remote);
  foreach ($fields as $k) {
     exit();
      if (!isset($response[$k]))
}
      $response[$k] = '';
  }
  return $response;
}</nowiki>
 
==Avec Django==
=== Version sale ===
L'interfaçage de Frankiz directement avec Django n'est pas trivial, pour le faire proprement il faudrait coder un backend d'authentification. En attendant, le code ci dessous est un peu sale mais marche. Il sauve l'username frankiz dans la session, ce qui est raisonnablement safe normalement.
 
<nowiki>
def logout(request):
     del request.session["username"]
    request.session.modified = True
     return redirect("/")  
   
   
function frankiz_get_response(){
def fkz_answer(request):
     global $FKZ_KEY;
     if not "timestamp" in request.GET.keys() or not "response" in request.GET.keys() or not "hash" in request.GET.keys():  
    // Read request
        return redirect("/login_required/")
    $timestamp = (isset($_GET['timestamp']) ? $_GET['timestamp'] : 0);
     response = request.GET.get("response")
     $response = (isset($_GET['response']) ? urldecode($_GET['response'])  : '');
     ts = request.GET.get("timestamp")
     $hash      = (isset($_GET['hash'])      ? $_GET['hash']      : '');
     h = request.GET.get("hash")
     $location  = (isset($_GET['location']) ? $_GET['location']  : '');
 
     if abs(int(time.time()) - int(ts)) > 600:
    // Frankiz security protocol
        return redirect("/login_required/")
     if (abs($timestamp - time()) > 600)
 
      die("Délai de réponse dépassé. Annulation de la requête");
     if hashlib.md5(ts + FKZ_KEY + response).hexdigest() != h:
     if (md5($timestamp . $FKZ_KEY . $response) != $hash)
        return redirect("/login_required/")
      die("Session compromise.");
 
   
     data = json.loads(response)
     $response = json_decode($response, true);
     request.session['username'] = data['hruid']
     $response['location'] = $location;
    return redirect("/")
 
     // Set empty fields
def fkz_do_login(request):
     $fields = array('hruid',
     ts = time.time()
      'firstname', 'lastname', 'nickname',
    page = "http://***/fkz_answer/"
      'promo', 'photo', 'location');
     r = json.dumps(["names", "email"])
    foreach ($fields as $k) {
    h = hashlib.md5(ts + page + FKZ_KEY + r).hexdigest()
      if (!isset($response[$k]))
    return redirect("https://www.frankiz.net/remote?"+urlencode([('timestamp',ts),('site',page),('hash',h),('request',r)]))</nowiki>
      $response[$k] = '';
 
    }
Il existe cependant une autre méthode utilisant les mêmes identifiants
    return $response;
}
?>
</code>


==Avec Django==
=== Authentification par le CAS ===
L'interfaçage avec Django n'est pas trivial, pour le faire proprement il faudrait coder un backend d'authentification. En attendant, le code ci dessous est un peu sale mais marche. Il sauve l'username frankiz dans la session, ce qui est raisonnablement safe normalement.


<code python>
* Demander sur [https://panix.binets.fr Panix] à utiliser cette authentification. Préciser les éventuels attributs supplémentaires nécessaires (promo, section, ...).
* Installer le paquet django_cas_ng:
  pip install django-cas-ng
* Ajouter ce paquet à la liste des applications :
  INSTALLED_APPS = [
    ...
    'django_cas_ng',
    ...
  ]
* Configurer la connection au serveur CAS du BR :
  AUTHENTICATION_BACKENDS = (
    'django.contrib.auth.backends.ModelBackend',
    'django_cas_ng.backends.CASBackend',
  )
  CAS_SERVER_URL = "https://cas.binets.fr/"
* Renseigner les pages de connexion (dans {{c|my_project/urls.py}} par exemple):
  from django_cas_ng.views import login, logout
 
  urlpatterns = [
    ...
    url(r'^login$', login, name='cas_ng_login'),
    url(r'^logout$', logout, name='cas_ng_logout'),
    ...
  ]
* Ajouter l'URL d'entrée :
  LOGIN_URL = "login"


def logout(request):
* Attention à forcer l'authentification pour les pages concernées :  
    del request.session["username"]
  from django.contrib.auth.decorators import login_required
    request.session.modified = True
    return redirect("/")
    
    
def fkz_answer(request):  
  @login_required
if not "timestamp" in request.GET.keys() or not "response" in request.GET.keys() or not "hash" in request.GET.keys():
  def my_awesome_view(request):
return redirect("/login_required/")
    ...
response = request.GET.get("response")
 
ts = request.GET.get("timestamp")
À noter :
h = request.GET.get("hash")
* le nom de l'utilisateur connecté se trouve dans : request.user.username (accessible dans n'importe quelle vue).
* les attributs du [[LDAP]] peuvent être disponibles (demander les attributs nécessaires lors de la mise en place du CAS). Ils sont alors disponibles dans request.session['attributes'] .
if abs(int(timestamp()) - int(ts)) > 600:
* plus d'option de configuration sont présentés [https://github.com/mingchen/django-cas-ng/tree/master/django_cas_ng ici]
return redirect("/login_required/")
 
   
==Avec Drupal==
if hashlib.md5(ts + FKZ_KEY + response).hexdigest() != h:
 
return redirect("/login_required/")
L'implémentation de l'authentification avec un CMS comme Drupal n'est pas forcément évidente. Si vous avez une question, vous pouvez contacter [mailto:antoine.sauvage@polytechnique.edu Antoine Sauvage]
data = json.loads(response)
request.session['username'] = data['hruid']
return redirect("/")
def fkz_do_login(request):
ts = timestamp()
page = "http://***/fkz_answer/"
r = json.dumps(["names", "email"])
h = hashlib.md5(ts + page + FKZ_KEY + r).hexdigest()
return redirect("http://www.frankiz.net/remote?"+urlencode([('timestamp',ts),('site',page),('hash',h),('request',r)]))
</code>


==Bugs vécus==
==Bugs vécus==


Si authentification fonctionne sur certains navigateurs, vérifier les variables $_GET car elles sont peut être doublement encodées. Dans ce cas, soit refaire '''urldecode''', soit un  
Si l'authentification ne fonctionne pas sur certains navigateurs, vérifier les variables $_GET car elles sont peut être doublement encodées. Dans ce cas, soit refaire '''urldecode''', soit un  
  $s = str_replace('%22','"',$s);
  $s = str_replace('%22','"',$s);
Si il y a un problème avec les encodages en \u''machinchose'' pour les caractères accentués, voila une fonction qui peut vous servir (reconversion en UTF-8) :
Si il y a un problème avec les encodages en \u''machinchose'' pour les caractères accentués, voila une fonction qui peut vous servir (reconversion en UTF-8) :
Ligne 145 : Ligne 199 :
  }
  }


Avec Drupal :


L'implémentation de l'authentification avec un CMS comme Drupal n'est pas forcément évidente. Si vous avez une question, vous pouvez contacter antoine.sauvage@polytechnique.edu


[[Catégorie:Dev]]
[[Catégorie:Dev]]

Version actuelle datée du 18 mars 2023 à 16:26

Warning.png Article archivé.
Frankiz ne fonctionne plus.
Warning.png

Frankiz permet à des sites externes (binets en particulier) d'utiliser sa base de données pour authentifier les utilisateurs.

Le webmaster du site en question doit auparavant faire une demande de mise en place de ce système en envoyant un mail à l'équipe frankiz (web@eleves.polytechnique.fr). Il doit alors donner :

  • l'url complète de la page de login de son site (plus précisément, l'URL de callback)
  • les informations auxquelles il souhaite accéder
  • une justification de la demande (et oui, on ne partage évidemment pas notre base de données avec n'importe qui !)

Si la demande est acceptée, un admin Frankiz va alors inscrire ton site dans notre base de donnée de sites externes et générer une clé qu'il va te communiquer. Cette opération se déroule via l'interface d'administration de frankiz. Cette clé va te permettre d'authentifier les échanges d'informations entre frankiz et le site externe.

Liste des informations disponibles

Tu peux demander l'accès aux informations

  • names : prénom, nom, surnom, identifiant Frankiz
  • email : adresse mail
  • photo : URL vers la photo TOL
  • promo : nom du premier groupe promo
  • sport : nom de la section sportive (si applicable)
  • rights : droits dans les groupes associés
  • binets_admin : liste des binets dont l'utilisateur est administrateur

Code de la page d'identification en PHP

Ci dessous un exemple commenté de script d'authentification, à mettre sur le site externe :

<?php
include 'fkz_auth.php';
if(!isset($_GET['response'])){
  frankiz_do_auth();
}
$auth = frankiz_get_response();
// et voila !
// les données sont stockées dans $auth = array(key => value, ...);

Code des fonctions utilisées dans fkz_auth.php

<?php
/**
 * Clé secrète fournie par l'équipe frankiz lors de l'inscription du site.
 * Cette clé sert à signer les requêtes et à authentifier le site.
 */
$FKZ_KEY = "000";

function frankiz_do_auth(){
   global $FKZ_KEY;
  /**
   * Prendre le timestamp permet d'éviter le rejet de la requête
   */
  $timestamp = time();
  /**
   * url de la page de login, doit correspondre *exactement* à celle entrée dans
   * la base de données de Frankiz (définie lors de l'inscription)
   */
  $site = 'http://monsite/login';
  /**
   * Champ non utile pour l'authentification et retransmis tel quel par frankiz. 
   * Il est prévu pour pouvoir mettre en place un système de redirection après 
   * authentification, vers la page à partir de laquelle le client avait tenté de se connecter.
   */
  $location  = "...";
  /**
   * Nature de la requête.
   * Fkz renverra ici à la fois les noms de la personne mais aussi ses droits dans différents groupes.
   * Il faut cependant que le site ait les droits sur les informations en question (à définir lors de son inscription).
   */
  $request = json_encode(array('names', 'rights', 'email', 'sport', 'promo', 'photo'));
  
  $hash = md5($timestamp . $site . $FKZ_KEY . $request);
  
  $remote  = 'https://www.frankiz.net/remote?timestamp=' . $timestamp .
      '&site=' . $site .
      '&location=' . $location .
      '&hash=' . $hash .
      '&request=' . $request;
   header("Location:" . $remote);
   exit();
}

function frankiz_get_response(){
   global $FKZ_KEY;
   // Read request
   $timestamp = (isset($_GET['timestamp']) ? $_GET['timestamp'] : 0);
   $response  = (isset($_GET['response'])  ? urldecode($_GET['response'])  : '');
   $hash      = (isset($_GET['hash'])      ? $_GET['hash']      : '');
   $location  = (isset($_GET['location'])  ? $_GET['location']  : '');

   // Frankiz security protocol
   if (abs($timestamp - time()) > 600)
      die("Délai de réponse dépassé. Annulation de la requête");
   if (md5($timestamp . $FKZ_KEY . $response) != $hash)
      die("Session compromise.");
   
   $response = json_decode($response, true);
   $response['location'] = $location;

   // Set empty fields
   $fields = array('hruid',
     'firstname', 'lastname', 'nickname',
     'promo', 'photo', 'location');
   foreach ($fields as $k) {
      if (!isset($response[$k]))
      $response[$k] = '';
   }
   return $response;
}

Avec Django

Version sale

L'interfaçage de Frankiz directement avec Django n'est pas trivial, pour le faire proprement il faudrait coder un backend d'authentification. En attendant, le code ci dessous est un peu sale mais marche. Il sauve l'username frankiz dans la session, ce qui est raisonnablement safe normalement.

def logout(request):
    del request.session["username"]
    request.session.modified = True
    return redirect("/") 
 
def fkz_answer(request): 
    if not "timestamp" in request.GET.keys() or not "response" in request.GET.keys() or not "hash" in request.GET.keys(): 
        return redirect("/login_required/")
    response = request.GET.get("response")
    ts = request.GET.get("timestamp")
    h = request.GET.get("hash")

    if abs(int(time.time()) - int(ts)) > 600:
        return redirect("/login_required/")

    if hashlib.md5(ts + FKZ_KEY + response).hexdigest() != h:
        return redirect("/login_required/")

    data = json.loads(response)
    request.session['username'] = data['hruid']
    return redirect("/")

def fkz_do_login(request):
    ts = time.time()
    page = "http://***/fkz_answer/"
    r = json.dumps(["names", "email"])
    h = hashlib.md5(ts + page + FKZ_KEY + r).hexdigest()
    return redirect("https://www.frankiz.net/remote?"+urlencode([('timestamp',ts),('site',page),('hash',h),('request',r)]))

Il existe cependant une autre méthode utilisant les mêmes identifiants

Authentification par le CAS

  • Demander sur Panix à utiliser cette authentification. Préciser les éventuels attributs supplémentaires nécessaires (promo, section, ...).
  • Installer le paquet django_cas_ng:
 pip install django-cas-ng
  • Ajouter ce paquet à la liste des applications :
 INSTALLED_APPS = [
   ...
   'django_cas_ng',
   ...
 ]
  • Configurer la connection au serveur CAS du BR :
 AUTHENTICATION_BACKENDS = (
   'django.contrib.auth.backends.ModelBackend',
   'django_cas_ng.backends.CASBackend',
 )
 CAS_SERVER_URL = "https://cas.binets.fr/"
  • Renseigner les pages de connexion (dans my_project/urls.py par exemple):
 from django_cas_ng.views import login, logout
 
 urlpatterns = [
   ...
   url(r'^login$', login, name='cas_ng_login'),
   url(r'^logout$', logout, name='cas_ng_logout'),
   ...
 ]
  • Ajouter l'URL d'entrée :
 LOGIN_URL = "login"
  • Attention à forcer l'authentification pour les pages concernées :
 from django.contrib.auth.decorators import login_required
 
 @login_required
 def my_awesome_view(request):
   ...

À noter :

  • le nom de l'utilisateur connecté se trouve dans : request.user.username (accessible dans n'importe quelle vue).
  • les attributs du LDAP peuvent être disponibles (demander les attributs nécessaires lors de la mise en place du CAS). Ils sont alors disponibles dans request.session['attributes'] .
  • plus d'option de configuration sont présentés ici

Avec Drupal

L'implémentation de l'authentification avec un CMS comme Drupal n'est pas forcément évidente. Si vous avez une question, vous pouvez contacter Antoine Sauvage

Bugs vécus

Si l'authentification ne fonctionne pas sur certains navigateurs, vérifier les variables $_GET car elles sont peut être doublement encodées. Dans ce cas, soit refaire urldecode, soit un

$s = str_replace('%22','"',$s);

Si il y a un problème avec les encodages en \umachinchose pour les caractères accentués, voila une fonction qui peut vous servir (reconversion en UTF-8) :

function jsonClean($s) {
  return preg_replace("/\\\\u([a-f0-9]{4})/e", "iconv('UCS-4LE','UTF-8',pack('V', hexdec('U$1')))",$s);
}