| @@ -1,2 +1,4 @@ | |||||
| nanowsgi.wpu | nanowsgi.wpu | ||||
| auth_files/* | |||||
| @@ -1,13 +1,15 @@ | |||||
| #!/usr/bin/python | #!/usr/bin/python | ||||
| # -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*- | # -*- 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() | wsgitmpl = WSGITemplate() | ||||
| # | # | ||||
| # esempio minimo di controller WSGI | # esempio minimo di controller WSGI | ||||
| # | # | ||||
| @auth.require() | |||||
| @wsgitmpl.template( 'template1.tmpl' ) | @wsgitmpl.template( 'template1.tmpl' ) | ||||
| def application( environ, start_response ): | def application( environ, start_response ): | ||||
| from pprint import pformat | from pprint import pformat | ||||
| @@ -16,4 +18,4 @@ def application( environ, start_response ): | |||||
| start_response( '200 OK', [('content-type', 'text/html; charset=utf-8')] ) | start_response( '200 OK', [('content-type', 'text/html; charset=utf-8')] ) | ||||
| return [ html, pformat( environ, width=132 ).replace('\n','<br>\n') ] # TODO: pformat..... ---> trasformarlo in un decoratore | |||||
| return [ html, '<br><br><hr><br>', '<pre>', pformat( environ, width=132 ), '</pre>' ] # TODO: pformat..... ---> trasformarlo in un decoratore | |||||
| @@ -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 [ | |||||
| "<h1>The secret code is:</h1>", | |||||
| "<h6>''keep calm and carry on''</h6>" | |||||
| "<h5>uuid:%s</h5>" % environ['auth.uuid'], | |||||
| ] | |||||
| @@ -9,9 +9,17 @@ from template import render | |||||
| from functools import partial | from functools import partial | ||||
| ## WSGIAuth | ## WSGIAuth | ||||
| import threading | |||||
| import re | |||||
| import Cookie | import Cookie | ||||
| import hashlib | import hashlib | ||||
| import hmac | import hmac | ||||
| import os | |||||
| import random | |||||
| import time | |||||
| import cPickle | |||||
| md5 = lambda x : hashlib.md5( x ).hexdigest() | md5 = lambda x : hashlib.md5( x ).hexdigest() | ||||
| sha1 = lambda key,value: hmac.new( key, value, hashlib.sha1 ).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 | self.__dict_cursor = MySQLdb.cursors.DictCursor | ||||
| def __newconn( self, alias ): | def __newconn( self, alias ): | ||||
| import MySQLdb | import MySQLdb | ||||
| @@ -141,25 +148,103 @@ class WSGIMySQL( object ): | |||||
| class WSGISimpleAuth( object ): | class WSGISimpleAuth( object ): | ||||
| __metaclass__ = Singleton | __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 real_decorator( wsgi_application ): | ||||
| def wrapper( environ, start_response ): | 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 ): | def my_start_response( status, response_headers ): | ||||
| # | |||||
| # aggiunge il cookie all'header | |||||
| # | |||||
| cookie = Cookie.SimpleCookie() | cookie = Cookie.SimpleCookie() | ||||
| cookie["uuid"] = uuid | 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 ); | 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 ): | for item in wsgi_application( environ, my_start_response ): | ||||
| yield item | yield item | ||||
| @@ -168,4 +253,22 @@ class WSGISimpleAuth( object ): | |||||
| return real_decorator | return real_decorator | ||||
| # EOF | |||||
| # EOF | |||||
| """ | |||||
| { | |||||
| uuid: jkfghkjgdhfgkjlsk, | |||||
| permission=[], | |||||
| groups=[], | |||||
| timeout=3600 | |||||
| } | |||||
| { | |||||
| uuid: jkfghkjgdhfgkjlsk, | |||||
| permission=[], | |||||
| groups=[], | |||||
| timeout=3600 | |||||
| } | |||||
| """ | |||||
| @@ -36,6 +36,11 @@ WSGIMySQL( | |||||
| ), | ), | ||||
| ) | ) | ||||
| # | |||||
| # inizializzazione sistema di autenticazione | |||||
| # | |||||
| from decorators import WSGIMySQL | |||||
| WSGIMySQL( authdir='authfiles' ) | |||||
| # | # | ||||
| # importazione handler definiti esternamente | # importazione handler definiti esternamente | ||||
| @@ -71,12 +76,10 @@ def fallback( environ, start_response ): | |||||
| # | # | ||||
| # definiamo gli handler per le url che vogliamo servire | # definiamo gli handler per le url che vogliamo servire | ||||
| # | # | ||||
| ################################################################################ from test_mysql import simple_mysql | |||||
| handlers = ( | handlers = ( | ||||
| ( r'/', index ), | ( r'/', index ), | ||||
| ( r'/hello', hello ), | ( r'/hello', hello ), | ||||
| ############################################################################ ( r'/mysql', simple_mysql ), | |||||
| ) | ) | ||||
| @@ -12,6 +12,7 @@ | |||||
| <li><a href="/demo">test autorouting</a></li> | <li><a href="/demo">test autorouting</a></li> | ||||
| <li><a href="/demo/due">test autorouting (longest path possible)</a></li> | <li><a href="/demo/due">test autorouting (longest path possible)</a></li> | ||||
| <li><a href="/demo/sql">test sql access</a></li> | <li><a href="/demo/sql">test sql access</a></li> | ||||
| <li><a href="/secret">test authenticated</a></li> | |||||
| </ul> | </ul> | ||||
| <div style="position:fixed; bottom:0; left:10px;"> | <div style="position:fixed; bottom:0; left:10px;"> | ||||
| <a rel="license" href="http://creativecommons.org/licenses/by/3.0/deed.en_US"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by/3.0/88x31.png" /></a> | <a rel="license" href="http://creativecommons.org/licenses/by/3.0/deed.en_US"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by/3.0/88x31.png" /></a> | ||||
| @@ -3,11 +3,10 @@ il valore di v1 è {{= v1 }} | |||||
| <br> | <br> | ||||
| mentre il valore di v2 è {{= v2 }} | mentre il valore di v2 è {{= v2 }} | ||||
| <br> | <br> | ||||
| questo è il risultato di un loop che genera 10 valori casuali:<br> | |||||
| questo è il risultato di un loop che genera 10 valori interi casuali:<br> | |||||
| {{ import random }} | {{ import random }} | ||||
| {{ for x in range( 10 ): }} | {{ for x in range( 10 ): }} | ||||
| <div style='background-color:{{ = random.choice( ('#fcc','yellow','#ccc','cyan',) ) }};'> | |||||
| {{= random.randint( 10, 100 ) }} | |||||
| <div style='background-color:#{{ = random.choice('89abcdef')*3 }};'> | |||||
| {{= random.randint( 10, 10000 ) }} | |||||
| </div> | </div> | ||||
| {{ pass }} | {{ pass }} | ||||