From 3e5d83ba62a90b67a82c27a012cd7b64d35992df Mon Sep 17 00:00:00 2001 From: Andrea Papotti Date: Fri, 7 Dec 2012 17:22:26 +0100 Subject: [PATCH] persistenza su file del dizionario associato al cookie --- .gitignore | 2 + controllers/demo/due.py | 6 +- controllers/secret.py | 17 ++++++ decorators.py | 127 ++++++++++++++++++++++++++++++++++++---- dispatch_wsgi.py | 7 ++- static/index.html | 1 + views/template1.tmpl | 7 +-- 7 files changed, 147 insertions(+), 20 deletions(-) create mode 100644 controllers/secret.py diff --git a/.gitignore b/.gitignore index 3374e77..0544496 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ nanowsgi.wpu +auth_files/* + diff --git a/controllers/demo/due.py b/controllers/demo/due.py index 993fc44..7628023 100644 --- a/controllers/demo/due.py +++ b/controllers/demo/due.py @@ -1,13 +1,15 @@ #!/usr/bin/python # -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*- -from decorators import WSGITemplate # decoratore ( singleton ) +from decorators import WSGISimpleAuth, WSGITemplate # decoratore ( singleton ) +auth = WSGISimpleAuth() wsgitmpl = WSGITemplate() # # esempio minimo di controller WSGI # +@auth.require() @wsgitmpl.template( 'template1.tmpl' ) def application( environ, start_response ): from pprint import pformat @@ -16,4 +18,4 @@ def application( environ, start_response ): start_response( '200 OK', [('content-type', 'text/html; charset=utf-8')] ) - return [ html, pformat( environ, width=132 ).replace('\n','
\n') ] # TODO: pformat..... ---> trasformarlo in un decoratore + return [ html, '



', '
', pformat( environ, width=132 ), '
' ] # TODO: pformat..... ---> trasformarlo in un decoratore diff --git a/controllers/secret.py b/controllers/secret.py new file mode 100644 index 0000000..d558f78 --- /dev/null +++ b/controllers/secret.py @@ -0,0 +1,17 @@ +#!/usr/bin/python +# -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*- + +from decorators import WSGISimpleAuth # decoratore ( singleton ) +auth = WSGISimpleAuth() + +@auth.require() +def application( environ, start_response ): + storage = environ['auth.storage'] + + start_response( '200 OK', [('content-type', 'text/html; charset=utf-8')] ) + + return [ + "

The secret code is:

", + "
''keep calm and carry on''
" + "
uuid:%s
" % environ['auth.uuid'], + ] diff --git a/decorators.py b/decorators.py index 13576a5..d7cccce 100644 --- a/decorators.py +++ b/decorators.py @@ -9,9 +9,17 @@ from template import render from functools import partial ## WSGIAuth +import threading +import re import Cookie import hashlib import hmac +import os +import random +import time +import cPickle + + md5 = lambda x : hashlib.md5( x ).hexdigest() sha1 = lambda key,value: hmac.new( key, value, hashlib.sha1 ).hexdigest() @@ -87,7 +95,6 @@ class WSGIMySQL( object ): self.__dict_cursor = MySQLdb.cursors.DictCursor - def __newconn( self, alias ): import MySQLdb @@ -141,25 +148,103 @@ class WSGIMySQL( object ): class WSGISimpleAuth( object ): __metaclass__ = Singleton - def __init__( self, secret_key, login_url=None, forbidden_url=None ): - self.__secret_key = secret_key + def __init__( self, timeout=900, auth_dir = 'auth_files', login_url=None, forbidden_url=None ): + import os + self.__authdir = os.path.normpath( os.path.join( os.path.split(__file__)[0], auth_dir ) ) + self.__timeout = timeout + self._lock = threading.RLock() + + def uuid( self ): + """Generate a unique session ID""" + return hashlib.sha256( + str(os.getpid()) + + str(time()) + + str(random.random()) + ).hexdigest() - def auth( self, permission='', group='', p_g_mode='AND', p_mode='OR', g_mode='OR' ): + def acquire_lock(self): + self._lock.acquire() + + def release_lock(self): + self._lock.release() + + + def require( self, permission='', group='', p_g_mode='AND', p_mode='OR', g_mode='OR' ): def real_decorator( wsgi_application ): def wrapper( environ, start_response ): - try: - uuid = Cookie.SimpleCookie(environ["HTTP_COOKIE"])["uuid"].value - except: - uuid = None - - environ['auth.uuid'] = uuid + #-------------------------------------------------------------- def my_start_response( status, response_headers ): + # + # aggiunge il cookie all'header + # cookie = Cookie.SimpleCookie() cookie["uuid"] = uuid - response_headers.append( ('Set-Cookie',cookie.OutputString()) ) + response_headers.append( ('Set-Cookie',cookie.output()) ) + + # + # salva le informazioni legate al cookie + # + storage['epoch_write'] = time.time() + + self.acquire_lock() ## LOCK + + f = open( path, 'w' ) + try: + cPickle.dump(storage, f) + finally: + f.close() + + self.release_lock() ## RELEASE + + # + # start response originale + # start_response( status, response_headers ); + #-------------------------------------------------------------- + # + # recupera UUID dal cookie + # + try: + uuid = Cookie.SimpleCookie(environ["HTTP_COOKIE"])["uuid"].value + except: + uuid = self.uuid() + + # + # utilizza lo UUID per recuperare le informazioni (locali) ad esso legate + # + path = os.path.join( self.__authdir, uuid ) + + self.acquire_lock() ## LOCK + + f = open( path, 'r' ) + + try: + storage = cPickle.load( f ) + except: + # UUID assente, crea una nuova struttura dati + storage = { + 'epoch_created': time.time(), + 'permissions': [], + 'groups': [], + } + + f.close() + + self.release_lock() ## RELEASE + + storage['epoch_read'] = time.time() + + # + # popola environ + # + environ['auth.uuid'] = uuid + environ['auth.storage'] = storage + + # + # output dei contenuti generati + # for item in wsgi_application( environ, my_start_response ): yield item @@ -168,4 +253,22 @@ class WSGISimpleAuth( object ): return real_decorator -# EOF \ No newline at end of file +# EOF + +""" +{ + uuid: jkfghkjgdhfgkjlsk, + permission=[], + groups=[], + timeout=3600 +} + + + +{ + uuid: jkfghkjgdhfgkjlsk, + permission=[], + groups=[], + timeout=3600 +} +""" \ No newline at end of file diff --git a/dispatch_wsgi.py b/dispatch_wsgi.py index 0681f63..0367835 100755 --- a/dispatch_wsgi.py +++ b/dispatch_wsgi.py @@ -36,6 +36,11 @@ WSGIMySQL( ), ) +# +# inizializzazione sistema di autenticazione +# +from decorators import WSGIMySQL +WSGIMySQL( authdir='authfiles' ) # # importazione handler definiti esternamente @@ -71,12 +76,10 @@ def fallback( environ, start_response ): # # definiamo gli handler per le url che vogliamo servire # -################################################################################ from test_mysql import simple_mysql handlers = ( ( r'/', index ), ( r'/hello', hello ), - ############################################################################ ( r'/mysql', simple_mysql ), ) diff --git a/static/index.html b/static/index.html index f384eff..84e1ecd 100644 --- a/static/index.html +++ b/static/index.html @@ -12,6 +12,7 @@
  • test autorouting
  • test autorouting (longest path possible)
  • test sql access
  • +
  • test authenticated
  • Creative Commons License diff --git a/views/template1.tmpl b/views/template1.tmpl index ea35cdc..7e08cf7 100644 --- a/views/template1.tmpl +++ b/views/template1.tmpl @@ -3,11 +3,10 @@ il valore di v1 è {{= v1 }}
    mentre il valore di v2 è {{= v2 }}
    -questo è il risultato di un loop che genera 10 valori casuali:
    +questo è il risultato di un loop che genera 10 valori interi casuali:
    {{ import random }} {{ for x in range( 10 ): }} -
    - {{= random.randint( 10, 100 ) }} +
    + {{= random.randint( 10, 10000 ) }}
    {{ pass }} -