#!/usr/bin/python # -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*- ## all from singleton import Singleton ## WSGITemplate 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() #------------------------------------------------------------------------------ class WSGITemplate( object ): __metaclass__ = Singleton def __init__( self, basedir='' ): import os self.__basedir = os.path.normpath( os.path.join( os.path.split(__file__)[0], basedir ) ) + '/' self.__fallback_template = \ "
{{ = pformat( { k:v for k,v in locals().iteritems() if k not in ('NOESCAPE','__builtins__','pformat','response') }, width=132 ) }}\n\n"
def template( self, filename=None ):
def real_decorator( wsgi_application ):
def wrapper( environ, start_response ):
if filename:
environ[ 'template' ] = partial( render, filename=self.__basedir + filename )
else:
environ[ 'template' ] = partial( render, content=self.__fallback_template )
return wsgi_application( environ, start_response )
return wrapper
return real_decorator
class WSGIMySQL( object ):
__metaclass__ = Singleton
def __init__( self, dsn, *args ):
""" inizializza le connessioni a 1 o più database.
In caso di connessioni a databese multipli,
le connessioni sono identificate tramite ALIAS
o in mancanza di questo tramite DB
ogni singolo dsn deve essere un dizionario con le chiavi:
- DB : nome del database
- HOST : host a cui connettersi
- USER : username da utilizzare per connettersi
- PASSWORD : password da utilizzare per connettersi
- ALIAS : (opzionale) identificativo della connessione
"""
import MySQLdb
#
# aggiungiamo il primo dsn in cima alla lista
#
args = list( args )
args.insert( 0, dsn )
#
# creiamo il nostro dizionario di dizionari
#
self.__dsn = { dsndict.get( 'ALIAS', dsndict['DB'] ):dsndict for dsndict in args }
#
# verifichiamo che non ci siano alias duplicati
#
if len( self.__dsn.keys() ) != len( args ):
raise Exception( "WSGIMySQL :: conflicting alias in dsn list" )
#
# tentiamo di creare la prima connessione verso TUTTI i dsn passati
#
for alias, dsndict in self.__dsn.iteritems():
dsndict['pool'] = [ self.__newconn( alias ) ]
self.__dict_cursor = MySQLdb.cursors.DictCursor
def __newconn( self, alias ):
import MySQLdb
return MySQLdb.connect(
host = self.__dsn[ alias ]["HOST"],
user = self.__dsn[ alias ]["USER"],
passwd = self.__dsn[ alias ]["PASSWORD"],
db = self.__dsn[ alias ]["DB"]
)
def db( self, *args ):
def real_decorator( wsgi_application ):
def wrapper( environ, start_response ):
connections = []
for arg in args:
try:
conn = self.__dsn[ arg ]['pool'].pop( 0 )
except IndexError:
conn = self.__newconn( arg )
connections.append( conn )
cur = conn.cursor( self.__dict_cursor )
environ['mysql.' + arg + '.cur'] = cur
try:
for item in wsgi_application( environ, start_response ):
yield item
finally:
for arg in args:
conn = connections.pop(0)
conn.commit()
self.__dsn[ arg ]['pool'].append( conn )
return wrapper
return real_decorator
def __del__( self ):
#
# chiudiamo tutte le connessioni attualmente aperte
#
for dsndict in self.__dsn.items():
for conn in dsndict['pool']:
conn.close()
class WSGISimpleAuth( object ):
__metaclass__ = Singleton
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 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 ):
#--------------------------------------------------------------
def my_start_response( status, response_headers ):
#
# aggiunge il cookie all'header
#
cookie = Cookie.SimpleCookie()
cookie["uuid"] = uuid
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
return wrapper
return real_decorator
# EOF
"""
{
uuid: jkfghkjgdhfgkjlsk,
permission=[],
groups=[],
timeout=3600
}
{
uuid: jkfghkjgdhfgkjlsk,
permission=[],
groups=[],
timeout=3600
}
"""