| @@ -0,0 +1,16 @@ | |||||
| #!/usr/bin/python | |||||
| # -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; -*- | |||||
| from decorators import WSGISimpleAuth # decoratore ( singleton ) | |||||
| auth = WSGISimpleAuth() | |||||
| @auth.require( 'admin superadmin hyperadmin' ) | |||||
| def application( environ, start_response ): | |||||
| storage = environ['auth.storage'] | |||||
| start_response( '200 OK', [('content-type', 'text/html; charset=utf-8')] ) | |||||
| return [ | |||||
| "<h1>GOSH BATMAN! HOW DID SHE DO THE IMPOSSIBLE?</h1>", | |||||
| "<a href='/index.html'>index</a>" | |||||
| ] | |||||
| @@ -18,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, '<br><br><hr><br>', '<pre>', pformat( environ, width=132 ), '</pre>' ] # TODO: pformat..... ---> trasformarlo in un decoratore | |||||
| return [ html, '<br><br><hr><br>', '<pre>', pformat( environ, width=132 ), '</pre>' ] | |||||
| @@ -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() | |||||
| def application( environ, start_response ): | |||||
| start_response( '200 OK', [('content-type', 'text/html; charset=utf-8')] ) | |||||
| return [ | |||||
| "<center>", | |||||
| "<h1>FORBIDDEN</h1>", | |||||
| "<img src='/attack_dog.jpg'>", | |||||
| "<br><br><br>", | |||||
| "Maybe you should go back to <a href='/index.html'>index</a>", | |||||
| "</center>" | |||||
| ] | |||||
| @@ -0,0 +1,40 @@ | |||||
| #!/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'] | |||||
| import cgi | |||||
| post_environ = environ.copy() | |||||
| post_environ['QUERY_STRING'] = '' | |||||
| post = cgi.FieldStorage( | |||||
| fp=environ['wsgi.input'], | |||||
| environ=post_environ, | |||||
| keep_blank_values=True | |||||
| ) | |||||
| if 'password' in post: | |||||
| password = post['password'].value | |||||
| storage['permissions'] = [ 'auth' ] | |||||
| html = [ | |||||
| "You are logged in, now you can see the <a href='/secret'>Secret</a> page.", | |||||
| ] | |||||
| else: | |||||
| storage['timeout'] = -1 | |||||
| html = [ | |||||
| "<form method='POST'>" | |||||
| "Password: <input type='password' value='' name='password' />", | |||||
| "<input type='submit'>", | |||||
| "</form>", | |||||
| ] | |||||
| start_response( '200 OK', [('content-type', 'text/html; charset=utf-8')] ) | |||||
| return html | |||||
| @@ -0,0 +1,19 @@ | |||||
| #!/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'] | |||||
| storage['timeout'] = -1 | |||||
| start_response( '200 OK', [('content-type', 'text/html; charset=utf-8')] ) | |||||
| return [ | |||||
| "<h5>Now you are logged out</h5>", | |||||
| "<a href='/index.html'>index</a>" | |||||
| ] | |||||
| @@ -4,7 +4,7 @@ | |||||
| from decorators import WSGISimpleAuth # decoratore ( singleton ) | from decorators import WSGISimpleAuth # decoratore ( singleton ) | ||||
| auth = WSGISimpleAuth() | auth = WSGISimpleAuth() | ||||
| @auth.require() | |||||
| @auth.require( 'auth' ) | |||||
| def application( environ, start_response ): | def application( environ, start_response ): | ||||
| storage = environ['auth.storage'] | storage = environ['auth.storage'] | ||||
| @@ -14,4 +14,5 @@ def application( environ, start_response ): | |||||
| "<h1>The secret code is:</h1>", | "<h1>The secret code is:</h1>", | ||||
| "<h6>''keep calm and carry on''</h6>" | "<h6>''keep calm and carry on''</h6>" | ||||
| "<h5>uuid:%s</h5>" % environ['auth.uuid'], | "<h5>uuid:%s</h5>" % environ['auth.uuid'], | ||||
| "<a href='/index.html'>index</a>" | |||||
| ] | ] | ||||
| @@ -158,7 +158,7 @@ class WSGISimpleAuth( object ): | |||||
| """Generate a unique session ID""" | """Generate a unique session ID""" | ||||
| return hashlib.sha256( | return hashlib.sha256( | ||||
| str(os.getpid()) | str(os.getpid()) | ||||
| + str(time()) | |||||
| + str(time.time()) | |||||
| + str(random.random()) | + str(random.random()) | ||||
| ).hexdigest() | ).hexdigest() | ||||
| @@ -169,7 +169,7 @@ class WSGISimpleAuth( object ): | |||||
| self._lock.release() | self._lock.release() | ||||
| def require( self, permission='', group='', p_g_mode='AND', p_mode='OR', g_mode='OR' ): | |||||
| def require( self, permissions=[], groups=[], 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 ): | ||||
| #-------------------------------------------------------------- | #-------------------------------------------------------------- | ||||
| @@ -185,6 +185,8 @@ class WSGISimpleAuth( object ): | |||||
| # | # | ||||
| # salva le informazioni legate al cookie | # salva le informazioni legate al cookie | ||||
| # | # | ||||
| timeout = storage.get( 'timeout', self.__timeout ) | |||||
| storage['epoch_timeout'] = time.time() + timeout | |||||
| storage['epoch_write'] = time.time() | storage['epoch_write'] = time.time() | ||||
| self.acquire_lock() ## LOCK | self.acquire_lock() ## LOCK | ||||
| @@ -203,6 +205,7 @@ class WSGISimpleAuth( object ): | |||||
| start_response( status, response_headers ); | start_response( status, response_headers ); | ||||
| #-------------------------------------------------------------- | #-------------------------------------------------------------- | ||||
| # | # | ||||
| # recupera UUID dal cookie | # recupera UUID dal cookie | ||||
| # | # | ||||
| @@ -218,24 +221,56 @@ class WSGISimpleAuth( object ): | |||||
| self.acquire_lock() ## LOCK | self.acquire_lock() ## LOCK | ||||
| f = open( path, 'r' ) | |||||
| try: | try: | ||||
| f = open( path, 'r' ) | |||||
| storage = cPickle.load( f ) | storage = cPickle.load( f ) | ||||
| f.close() | |||||
| if storage['epoch_timeout'] < time.time(): | |||||
| # dati scaduti rimuove il file dei permessi.. | |||||
| os.unlink( path ) | |||||
| # ... e finge di non everlo mai trovato | |||||
| raise Exception | |||||
| logged_in = True | |||||
| except: | except: | ||||
| # UUID assente, crea una nuova struttura dati | # UUID assente, crea una nuova struttura dati | ||||
| storage = { | storage = { | ||||
| 'epoch_created': time.time(), | 'epoch_created': time.time(), | ||||
| 'permissions': [], | 'permissions': [], | ||||
| 'groups': [], | 'groups': [], | ||||
| 'timeout': self.__timeout | |||||
| } | } | ||||
| f.close() | |||||
| logged_in = False | |||||
| self.release_lock() ## RELEASE | self.release_lock() ## RELEASE | ||||
| storage['epoch_read'] = time.time() | storage['epoch_read'] = time.time() | ||||
| if permissions: | |||||
| if isinstance( permissions, str ): | |||||
| p = permissions.split() | |||||
| else: | |||||
| p = permissions | |||||
| if not logged_in: | |||||
| # non autenticato -> redirect to 'login_url' | |||||
| start_response( '302 Found', [('Location', '/login'),] ) | |||||
| yield 'Please <a href="%s">login</a> first.' % '/login' | |||||
| return | |||||
| for perm in p: | |||||
| if perm in storage['permissions']: | |||||
| break | |||||
| else: | |||||
| # permessi insufficienti -> redirect to 'forbidden_url' | |||||
| start_response( '302 Found', [('Location', '/forbidden'),] ) | |||||
| yield 'This page is not for your eyes.' | |||||
| return | |||||
| # | # | ||||
| # popola environ | # popola environ | ||||
| # | # | ||||
| @@ -258,7 +293,7 @@ class WSGISimpleAuth( object ): | |||||
| """ | """ | ||||
| { | { | ||||
| uuid: jkfghkjgdhfgkjlsk, | uuid: jkfghkjgdhfgkjlsk, | ||||
| permission=[], | |||||
| permissions=[], | |||||
| groups=[], | groups=[], | ||||
| timeout=3600 | timeout=3600 | ||||
| } | } | ||||
| @@ -267,7 +302,7 @@ class WSGISimpleAuth( object ): | |||||
| { | { | ||||
| uuid: jkfghkjgdhfgkjlsk, | uuid: jkfghkjgdhfgkjlsk, | ||||
| permission=[], | |||||
| permissions=[], | |||||
| groups=[], | groups=[], | ||||
| timeout=3600 | timeout=3600 | ||||
| } | } | ||||
| @@ -52,7 +52,7 @@ from router import WSGIRouter, WSGISmartRouter | |||||
| # | # | ||||
| def index( environ, start_response ): | def index( environ, start_response ): | ||||
| start_response( '200 OK', [('content-type', 'text/html')] ) | start_response( '200 OK', [('content-type', 'text/html')] ) | ||||
| return [ 'Index was here' ] | |||||
| return [ 'Index was <a href="/index.html">here</a>' ] | |||||
| # | # | ||||
| # hello: esempio minimo di applicazione WSGI | # hello: esempio minimo di applicazione WSGI | ||||
| @@ -78,8 +78,7 @@ class WSGISmartRouter(object): | |||||
| tokens = [ token for token in environ['SCRIPT_URL'].split('/') if token ] | tokens = [ token for token in environ['SCRIPT_URL'].split('/') if token ] | ||||
| #for idx in range( 1, len( tokens ) + 1 ): # from shortest path to longest one | #for idx in range( 1, len( tokens ) + 1 ): # from shortest path to longest one | ||||
| # from longest path to shortest one | |||||
| for idx in range( len( tokens ) , 0, -1 ): | |||||
| for idx in range( len( tokens ) , 0, -1 ): # from longest path to shortest one | |||||
| module = os.path.normpath( self.cont_dir + '/'.join( tokens[:idx] ) + '.py' ) | module = os.path.normpath( self.cont_dir + '/'.join( tokens[:idx] ) + '.py' ) | ||||
| args = tokens[idx:] | args = tokens[idx:] | ||||
| @@ -88,8 +87,6 @@ class WSGISmartRouter(object): | |||||
| environ['router.args'] = args | environ['router.args'] = args | ||||
| if environ['REQUEST_METHOD'] == 'GET' and hasattr( handler, 'get' ): | if environ['REQUEST_METHOD'] == 'GET' and hasattr( handler, 'get' ): | ||||
| # FIXME: forse è meglio eseguire QUI la start_response | |||||
| # e compilare il template (solo per GET e POST) | |||||
| return handler.get( environ, start_response ) | return handler.get( environ, start_response ) | ||||
| elif environ['REQUEST_METHOD'] == 'POST' and hasattr( handler, 'post' ): | elif environ['REQUEST_METHOD'] == 'POST' and hasattr( handler, 'post' ): | ||||
| @@ -12,7 +12,10 @@ | |||||
| <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> | |||||
| <li><a href="/login">login</a></li> | |||||
| <li><a href="/secret">secret page</a></li> | |||||
| <li><a href="/admin">admin page (you have no permissions)</a></li> | |||||
| <li><a href="/logout">logout</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> | ||||