diff --git a/controllers/admin.py b/controllers/admin.py new file mode 100644 index 0000000..5de5268 --- /dev/null +++ b/controllers/admin.py @@ -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 [ + "

GOSH BATMAN! HOW DID SHE DO THE IMPOSSIBLE?

", + "index" + ] diff --git a/controllers/demo/due.py b/controllers/demo/due.py index 7628023..82cda5b 100644 --- a/controllers/demo/due.py +++ b/controllers/demo/due.py @@ -18,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 ), '
' ] # TODO: pformat..... ---> trasformarlo in un decoratore + return [ html, '



', '
', pformat( environ, width=132 ), '
' ] diff --git a/controllers/forbidden.py b/controllers/forbidden.py new file mode 100644 index 0000000..b00bbd4 --- /dev/null +++ b/controllers/forbidden.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() + +def application( environ, start_response ): + start_response( '200 OK', [('content-type', 'text/html; charset=utf-8')] ) + + return [ + "
", + "

FORBIDDEN

", + "", + "


", + "Maybe you should go back to index", + "
" + ] diff --git a/controllers/login.py b/controllers/login.py new file mode 100644 index 0000000..ff84c46 --- /dev/null +++ b/controllers/login.py @@ -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 Secret page.", + ] + + else: + storage['timeout'] = -1 + html = [ + "
" + "Password: ", + "", + "
", + ] + + start_response( '200 OK', [('content-type', 'text/html; charset=utf-8')] ) + + return html + + diff --git a/controllers/logout.py b/controllers/logout.py new file mode 100644 index 0000000..f9f1c1d --- /dev/null +++ b/controllers/logout.py @@ -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 [ + "
Now you are logged out
", + "index" + ] + diff --git a/controllers/secret.py b/controllers/secret.py index d558f78..0b338f3 100644 --- a/controllers/secret.py +++ b/controllers/secret.py @@ -4,7 +4,7 @@ from decorators import WSGISimpleAuth # decoratore ( singleton ) auth = WSGISimpleAuth() -@auth.require() +@auth.require( 'auth' ) def application( environ, start_response ): storage = environ['auth.storage'] @@ -14,4 +14,5 @@ def application( environ, start_response ): "

The secret code is:

", "
''keep calm and carry on''
" "
uuid:%s
" % environ['auth.uuid'], + "index" ] diff --git a/decorators.py b/decorators.py index d7cccce..be9c8cd 100644 --- a/decorators.py +++ b/decorators.py @@ -158,7 +158,7 @@ class WSGISimpleAuth( object ): """Generate a unique session ID""" return hashlib.sha256( str(os.getpid()) - + str(time()) + + str(time.time()) + str(random.random()) ).hexdigest() @@ -169,7 +169,7 @@ class WSGISimpleAuth( object ): 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 wrapper( environ, start_response ): #-------------------------------------------------------------- @@ -185,6 +185,8 @@ class WSGISimpleAuth( object ): # # salva le informazioni legate al cookie # + timeout = storage.get( 'timeout', self.__timeout ) + storage['epoch_timeout'] = time.time() + timeout storage['epoch_write'] = time.time() self.acquire_lock() ## LOCK @@ -203,6 +205,7 @@ class WSGISimpleAuth( object ): start_response( status, response_headers ); #-------------------------------------------------------------- + # # recupera UUID dal cookie # @@ -218,24 +221,56 @@ class WSGISimpleAuth( object ): self.acquire_lock() ## LOCK - f = open( path, 'r' ) - try: + f = open( path, 'r' ) 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: # UUID assente, crea una nuova struttura dati storage = { 'epoch_created': time.time(), 'permissions': [], 'groups': [], + 'timeout': self.__timeout } - f.close() + logged_in = False self.release_lock() ## RELEASE 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 login 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 # @@ -258,7 +293,7 @@ class WSGISimpleAuth( object ): """ { uuid: jkfghkjgdhfgkjlsk, - permission=[], + permissions=[], groups=[], timeout=3600 } @@ -267,7 +302,7 @@ class WSGISimpleAuth( object ): { uuid: jkfghkjgdhfgkjlsk, - permission=[], + permissions=[], groups=[], timeout=3600 } diff --git a/dispatch_wsgi.py b/dispatch_wsgi.py index 0367835..ec6716e 100755 --- a/dispatch_wsgi.py +++ b/dispatch_wsgi.py @@ -52,7 +52,7 @@ from router import WSGIRouter, WSGISmartRouter # def index( environ, start_response ): start_response( '200 OK', [('content-type', 'text/html')] ) - return [ 'Index was here' ] + return [ 'Index was here' ] # # hello: esempio minimo di applicazione WSGI diff --git a/router.py b/router.py index 4263180..503ea9a 100644 --- a/router.py +++ b/router.py @@ -78,8 +78,7 @@ class WSGISmartRouter(object): 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 - # 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' ) args = tokens[idx:] @@ -88,8 +87,6 @@ class WSGISmartRouter(object): environ['router.args'] = args 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 ) elif environ['REQUEST_METHOD'] == 'POST' and hasattr( handler, 'post' ): diff --git a/static/attack_dog.jpg b/static/attack_dog.jpg new file mode 100644 index 0000000..0fdd63a Binary files /dev/null and b/static/attack_dog.jpg differ diff --git a/static/index.html b/static/index.html index 84e1ecd..0362da4 100644 --- a/static/index.html +++ b/static/index.html @@ -12,7 +12,10 @@
  • test autorouting
  • test autorouting (longest path possible)
  • test sql access
  • -
  • test authenticated
  • +
  • login
  • +
  • secret page
  • +
  • admin page (you have no permissions)
  • +
  • logout
  • Creative Commons License