25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

274 satır
7.9 KiB

  1. #!/usr/bin/python
  2. # -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*-
  3. ## all
  4. from singleton import Singleton
  5. ## WSGITemplate
  6. from template import render
  7. from functools import partial
  8. ## WSGIAuth
  9. import threading
  10. import re
  11. import Cookie
  12. import hashlib
  13. import hmac
  14. import os
  15. import random
  16. import time
  17. import cPickle
  18. md5 = lambda x : hashlib.md5( x ).hexdigest()
  19. sha1 = lambda key,value: hmac.new( key, value, hashlib.sha1 ).hexdigest()
  20. #------------------------------------------------------------------------------
  21. class WSGITemplate( object ):
  22. __metaclass__ = Singleton
  23. def __init__( self, basedir='' ):
  24. import os
  25. self.__basedir = os.path.normpath( os.path.join( os.path.split(__file__)[0], basedir ) ) + '/'
  26. self.__fallback_template = \
  27. "<h1> NO TEMPLATE DEFINED </h1>\n" \
  28. "{{ from pprint import pformat }}" \
  29. "<pre>{{ = pformat( { k:v for k,v in locals().iteritems() if k not in ('NOESCAPE','__builtins__','pformat','response') }, width=132 ) }}</pre>\n\n"
  30. def template( self, filename=None ):
  31. def real_decorator( wsgi_application ):
  32. def wrapper( environ, start_response ):
  33. if filename:
  34. environ[ 'template' ] = partial( render, filename=self.__basedir + filename )
  35. else:
  36. environ[ 'template' ] = partial( render, content=self.__fallback_template )
  37. return wsgi_application( environ, start_response )
  38. return wrapper
  39. return real_decorator
  40. class WSGIMySQL( object ):
  41. __metaclass__ = Singleton
  42. def __init__( self, dsn, *args ):
  43. """ inizializza le connessioni a 1 o più database.
  44. In caso di connessioni a databese multipli,
  45. le connessioni sono identificate tramite ALIAS
  46. o in mancanza di questo tramite DB
  47. ogni singolo dsn deve essere un dizionario con le chiavi:
  48. - DB : nome del database
  49. - HOST : host a cui connettersi
  50. - USER : username da utilizzare per connettersi
  51. - PASSWORD : password da utilizzare per connettersi
  52. - ALIAS : (opzionale) identificativo della connessione
  53. """
  54. import MySQLdb
  55. #
  56. # aggiungiamo il primo dsn in cima alla lista
  57. #
  58. args = list( args )
  59. args.insert( 0, dsn )
  60. #
  61. # creiamo il nostro dizionario di dizionari
  62. #
  63. self.__dsn = { dsndict.get( 'ALIAS', dsndict['DB'] ):dsndict for dsndict in args }
  64. #
  65. # verifichiamo che non ci siano alias duplicati
  66. #
  67. if len( self.__dsn.keys() ) != len( args ):
  68. raise Exception( "WSGIMySQL :: conflicting alias in dsn list" )
  69. #
  70. # tentiamo di creare la prima connessione verso TUTTI i dsn passati
  71. #
  72. for alias, dsndict in self.__dsn.iteritems():
  73. dsndict['pool'] = [ self.__newconn( alias ) ]
  74. self.__dict_cursor = MySQLdb.cursors.DictCursor
  75. def __newconn( self, alias ):
  76. import MySQLdb
  77. return MySQLdb.connect(
  78. host = self.__dsn[ alias ]["HOST"],
  79. user = self.__dsn[ alias ]["USER"],
  80. passwd = self.__dsn[ alias ]["PASSWORD"],
  81. db = self.__dsn[ alias ]["DB"]
  82. )
  83. def db( self, *args ):
  84. def real_decorator( wsgi_application ):
  85. def wrapper( environ, start_response ):
  86. connections = []
  87. for arg in args:
  88. try:
  89. conn = self.__dsn[ arg ]['pool'].pop( 0 )
  90. except IndexError:
  91. conn = self.__newconn( arg )
  92. connections.append( conn )
  93. cur = conn.cursor( self.__dict_cursor )
  94. environ['mysql.' + arg + '.cur'] = cur
  95. try:
  96. for item in wsgi_application( environ, start_response ):
  97. yield item
  98. finally:
  99. for arg in args:
  100. conn = connections.pop(0)
  101. conn.commit()
  102. self.__dsn[ arg ]['pool'].append( conn )
  103. return wrapper
  104. return real_decorator
  105. def __del__( self ):
  106. #
  107. # chiudiamo tutte le connessioni attualmente aperte
  108. #
  109. for dsndict in self.__dsn.items():
  110. for conn in dsndict['pool']:
  111. conn.close()
  112. class WSGISimpleAuth( object ):
  113. __metaclass__ = Singleton
  114. def __init__( self, timeout=900, auth_dir = 'auth_files', login_url=None, forbidden_url=None ):
  115. import os
  116. self.__authdir = os.path.normpath( os.path.join( os.path.split(__file__)[0], auth_dir ) )
  117. self.__timeout = timeout
  118. self._lock = threading.RLock()
  119. def uuid( self ):
  120. """Generate a unique session ID"""
  121. return hashlib.sha256(
  122. str(os.getpid())
  123. + str(time())
  124. + str(random.random())
  125. ).hexdigest()
  126. def acquire_lock(self):
  127. self._lock.acquire()
  128. def release_lock(self):
  129. self._lock.release()
  130. def require( self, permission='', group='', p_g_mode='AND', p_mode='OR', g_mode='OR' ):
  131. def real_decorator( wsgi_application ):
  132. def wrapper( environ, start_response ):
  133. #--------------------------------------------------------------
  134. def my_start_response( status, response_headers ):
  135. #
  136. # aggiunge il cookie all'header
  137. #
  138. cookie = Cookie.SimpleCookie()
  139. cookie["uuid"] = uuid
  140. response_headers.append( ('Set-Cookie',cookie.output()) )
  141. #
  142. # salva le informazioni legate al cookie
  143. #
  144. storage['epoch_write'] = time.time()
  145. self.acquire_lock() ## LOCK
  146. f = open( path, 'w' )
  147. try:
  148. cPickle.dump(storage, f)
  149. finally:
  150. f.close()
  151. self.release_lock() ## RELEASE
  152. #
  153. # start response originale
  154. #
  155. start_response( status, response_headers );
  156. #--------------------------------------------------------------
  157. #
  158. # recupera UUID dal cookie
  159. #
  160. try:
  161. uuid = Cookie.SimpleCookie(environ["HTTP_COOKIE"])["uuid"].value
  162. except:
  163. uuid = self.uuid()
  164. #
  165. # utilizza lo UUID per recuperare le informazioni (locali) ad esso legate
  166. #
  167. path = os.path.join( self.__authdir, uuid )
  168. self.acquire_lock() ## LOCK
  169. f = open( path, 'r' )
  170. try:
  171. storage = cPickle.load( f )
  172. except:
  173. # UUID assente, crea una nuova struttura dati
  174. storage = {
  175. 'epoch_created': time.time(),
  176. 'permissions': [],
  177. 'groups': [],
  178. }
  179. f.close()
  180. self.release_lock() ## RELEASE
  181. storage['epoch_read'] = time.time()
  182. #
  183. # popola environ
  184. #
  185. environ['auth.uuid'] = uuid
  186. environ['auth.storage'] = storage
  187. #
  188. # output dei contenuti generati
  189. #
  190. for item in wsgi_application( environ, my_start_response ):
  191. yield item
  192. return wrapper
  193. return real_decorator
  194. # EOF
  195. """
  196. {
  197. uuid: jkfghkjgdhfgkjlsk,
  198. permission=[],
  199. groups=[],
  200. timeout=3600
  201. }
  202. {
  203. uuid: jkfghkjgdhfgkjlsk,
  204. permission=[],
  205. groups=[],
  206. timeout=3600
  207. }
  208. """