Ce TP est constitué de deux parties indépendantes: une introduction à WSGI (une façon générique d'écrire des applications web en python) avec l'utilisation d'un outil pour écrire des templates de pages web SimpleTAL et la mise en place d'un serveur LDAP, OpenLDAP.
Vous avez déjà vu comment écrire des applications CGI en python, un serveur HTTP en python ou encore en utilisant mod_python. Si le fait que, à chaque fois, la syntaxe et le façon de faire diffère un peu alors WSGI est fait pour vous!
def application(environ, start_response):
start_response('200 OK',[('Content-type','text/html')])
return ['<html><body>Hello World!</body></html>']
Ceci ressemble vaguement à la syntaxe de mod_python, mais regardons de plus près. Vous connaissez certainement déjà la syntaxe d'une réponse HTTP, et bien start_response permet de définir ce que le serveur qui implémentera votre application WSGI va répondre. Dans notre cas nous renvoyons l'état "200 OK" et précisons que le contenu est du type "text/html". Rien de bien transcendant ici.
from toto import application
from wsgiref import simple_server
httpd = simple_server.WSGIServer(('',8000),simple_server.WSGIRequestHandler)
httpd.set_app(application)
httpd.serve_forever()
Jusqu'ici tout va bien... vous avez une belle page HTML qui s'affiche avec un contenu super intéressant.
#! /usr/bin/env python from toto import application from wsgiref.handlers import CGIHandler CGIHandler().run(application)
Intéressant, la même application marche aussi en CGI!
Ces lignes de configuration dans le fichier httpd.conf dans la section de mod_python vous permettront d'exécuter votre application WSGI. Le fichier que vous venez de télécharger vous permettra de faire la passerelle:
PythonHandler wsgi_handler PythonOption WSGI.Application toto::application
La syntaxe utilisée dans WSGI peut être un peu rébarbative, définir la réponse, le content-type à chaque fois, etc. Heureusement, nous allons faire appel aux décorations de python pour ça!
def f(fu):
def r(s):
return fu(s+"toto")
return r
@f
def g(s):
return s
print g("titi")
Comment ça peut nous servir à nous?
class header_http:
def __init__(self,f):
self.f=f
def __call__(self,environ, start_response):
start_response('200 OK',[('Content-type','text/html')])
return self.f(environ,start_response);
class html_page:
def __init__(self,f):
self.f=f
def __call__(self,environ, start_response):
return ['<html>']+self.f(environ,start_response)+['</html>']
class body_content:
def __init__(self, f):
self.f = f
def __call__(self,environ,start_response):
return ['<body>']+self.f(environ,start_response)+['</body>']
@header_http
@html_page
@body_content
def application(environ,start_response):
return ['Hello World!']
C'est un peu plus long mais il vous suffit de mettre ces fonctions dans un autre fichier python et votre application se résumera à:
@header_http
@html_page
@body_content
def application(environ,start_response):
return ['Hello World!']
C'est un peu de l'"overkill" de faire comme ça, mais essayons d'aller plus loin.
Désormais vous avez compris que WSGI vous permet d'écrire des applications web que l'on peut déployer facilement de moultes façons différentes. Il ne vous reste plus qu'une chose à faire...
Vous pourrez vous inspirer du code suivant:
from paste.request import parse_formvars
from paste.request import get_cookie_dict
class auth_required:
params={}
def __init__(self,f):
self.f = f
def mysr(self,status, headers):
headers.append(('Set-Cookie','login='+self.params['login']))
headers.append(('Set-Cookie','pass='+self.params['pass']))
return self.sr(status,headers)
def check_auth(self):
if('login' in self.params and 'pass' in self.params):
verification login/password
return 1 si ok
return 0
def __call__(self,environ,start_response):
auth_ok = 0
self.sr = start_response
self.params = get_cookie_dict(environ)
auth_ok = self.check_auth()
if(auth_ok == 0):
self.params = parse_formvars(environ)
auth_ok = self.check_auth()
if(auth_ok):
return self.f(environ,self.mysr)
else:
start_response('403 Forbidden',[('Content-type','text/html')])
return """
code html du formulaire
"""
En disséquant un peu le code, on a la fonction get_cookie_dict qui nous donne un dictionnaire contenant les cookies, la fonction parse_fromvars les variables POST et GET. Ainsi en décorant n'importe quelle fonction par ce décorateur, on est sûr que l'utilisateur est authentifié lorsqu'il accède au contenu, sinon il est renvoyé sur le formulaire d'authentification.
@auth_required
@header_http
@html_page
@body_content
def application(environ,start_response):
return ['Hello World!']
C'est fou, maintenant cette page nécessite d'être authentifié à notre base de données... En plus, ça pourra constituer le début de votre projet!
SimpleTAL est une implémentation python d'un standard permettant de faire des templates de pages Web. Parce que, vous l'avez compris, écrire une page HTML complète dans du code python et la renvoyer dans une fonction n'est pas forcément une manière très élégante de faire. Grâce à ce système de templates, vous pouvez écrire du code "presque" HTML dans un fichier séparé tout en gardant l'aspect dynamique. Voici un exemple simpliste d'utilisation de SimpleTAL:
from simpletal import simpleTAL, simpleTALES
import StringIO
@auth_required
@header_http
@html_page
@body_content
def application(environ, start_response):
s=StringIO.StringIO()
context = simpleTALES.Context()
context.addGlobal ("title", "Hello World")
templateFile = open ("foo.tpl", 'r')
template = simpleTAL.compileHTMLTemplate (templateFile)
templateFile.close()
template.expand (context, s)
return [s.getvalue()]
Le fichier "foo.tpl" contenant:
<h1 tal:content="title">The title goes here</h1>
OpenLDAP fournit une implémentation libre et complète du standard LDAP. La distribution OpenLDAP inclut le serveur LDAP slapd, le serveur de réplication slurpd, que nous n'utiliserons pas, et divers utilitaires pour interagir avec les serveurs en question. Voir la documentation complète du serveur slapd.
Pour fonctionner, le serveur slapd a besoin de l'existence (dans votre civet) des répertoires suivants:
var/run/ var/db/openldap
De plus, nous allons créer notre base de donnée LDAP en l'enracinant ici:dc=example,dc=com. Créons donc un répertoire pour stocker la base de donnée LDAP:
var/db/openldap/example-com
Enfin, créons dans etc/ le fichier slapd.conf de configuration du serveur slapd:
include /home/monnomamoi/outils/etc/schema/core.schema include /home/monnomamoi/outils/etc/schema/cosine.schema include /home/monnomamoi/outils/etc/schema/inetorgperson.schema include /home/monnomamoi/outils/etc/schema/nis.schema pidfile /home/monnomamoi/outils/var/run/slapd.pid password-hash {MD5} loglevel -1 moduleload /usr/lib/ldap/back_bdb.so database bdb suffix "dc=example, dc=com" rootdn "cn=jimbob, dc=example, dc=com" rootpw dirtysecret directory /home/monnomamoi/outils/var/db/openldap/example-com index uid eq index cn,gn,mail eq,sub index sn eq,sub index ou eq index default eq,sub index telephonenumber cachesize 10000 checkpoint 128 15