changeset 239:b18f89d6d17f

Adde draft for permissions systems, made all needed decorators, and checks. For future usage in the system.
author Marcin Kuzminski <marcin@python-works.com>
date Sun, 30 May 2010 19:49:40 +0200
parents a55c17874486
children 7c4fa2a66195
files pylons_app/config/environment.py pylons_app/controllers/users.py pylons_app/lib/app_globals.py pylons_app/lib/auth.py pylons_app/lib/db_manage.py pylons_app/model/db.py pylons_app/templates/admin/admin.html
diffstat 7 files changed, 126 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/pylons_app/config/environment.py	Sun May 30 17:55:56 2010 +0200
+++ b/pylons_app/config/environment.py	Sun May 30 19:49:40 2010 +0200
@@ -1,16 +1,17 @@
 """Pylons environment configuration"""
-import logging
-import os
-
 from mako.lookup import TemplateLookup
 from pylons.configuration import PylonsConfig
 from pylons.error import handle_mako_error
+from pylons_app.config.routing import make_map
+from pylons_app.lib.auth import set_available_permissions
+from pylons_app.model import init_model
 from sqlalchemy import engine_from_config
-
+import logging
+import os
 import pylons_app.lib.app_globals as app_globals
 import pylons_app.lib.helpers
-from pylons_app.config.routing import make_map
-from pylons_app.model import init_model
+
+
 
 log = logging.getLogger(__name__)
 
@@ -62,6 +63,7 @@
 
     init_model(sa_engine_db1)
 
+    set_available_permissions(config)
     # CONFIGURATION OPTIONS HERE (note: all config options will override
     # any Pylons config options)
     
--- a/pylons_app/controllers/users.py	Sun May 30 17:55:56 2010 +0200
+++ b/pylons_app/controllers/users.py	Sun May 30 19:49:40 2010 +0200
@@ -4,7 +4,7 @@
 from pylons.i18n.translation import _
 from pylons_app.lib import helpers as h    
 from pylons.controllers.util import abort, redirect
-from pylons_app.lib.auth import LoginRequired
+from pylons_app.lib.auth import LoginRequired, CheckPermissionAll
 from pylons_app.lib.base import BaseController, render
 from pylons_app.model.db import User, UserLog
 from pylons_app.model.forms import UserForm
@@ -26,7 +26,8 @@
         c.admin_user = session.get('admin_user')
         c.admin_username = session.get('admin_username')
         super(UsersController, self).__before__()
-        
+    
+
     def index(self, format='html'):
         """GET /users: All items in the collection"""
         # url('users')
--- a/pylons_app/lib/app_globals.py	Sun May 30 17:55:56 2010 +0200
+++ b/pylons_app/lib/app_globals.py	Sun May 30 19:49:40 2010 +0200
@@ -22,3 +22,4 @@
         self.paths = self.baseui.configitems('paths')
         self.base_path = self.paths[0][1].replace('*', '')
         self.changeset_annotation_colors = {}
+        self.available_permissions = None # propagated after init_model
--- a/pylons_app/lib/auth.py	Sun May 30 17:55:56 2010 +0200
+++ b/pylons_app/lib/auth.py	Sun May 30 19:49:40 2010 +0200
@@ -1,5 +1,5 @@
 from functools import wraps
-from pylons import session, url
+from pylons import session, url, app_globals as g
 from pylons.controllers.util import abort, redirect
 from pylons_app.model import meta
 from pylons_app.model.db import User
@@ -47,7 +47,26 @@
     
     def __init__(self):
         pass
-    
+
+
+
+def set_available_permissions(config):
+    """
+    This function will propagate pylons globals with all available defined
+    permission given in db. We don't wannt to check each time from db for new 
+    permissions since adding a new permission also requires application restart
+    ie. to decorate new views with the newly created permission
+    @param config:
+    """
+    from pylons_app.model.meta import Session
+    from pylons_app.model.db import Permission
+    logging.info('getting information about all available permissions')
+    sa = Session()
+    all_perms = sa.query(Permission).all()
+    config['pylons.app_globals'].available_permissions = [x.permission_name for x in all_perms]
+
+
+        
 #===============================================================================
 # DECORATORS
 #===============================================================================
@@ -73,3 +92,62 @@
                 return redirect(url('login_home'))
 
         return _wrapper
+
+class PermsDecorator(object):
+    
+    def __init__(self, *perms):
+        available_perms = g.available_permissions
+        for perm in perms:
+            if perm not in available_perms:
+                raise Exception("'%s' permission in not defined" % perm)
+        self.required_perms = set(perms)
+        self.user_perms = set([])#propagate this list from somewhere.
+        
+    def __call__(self, func):        
+        @wraps(func)
+        def _wrapper(*args, **kwargs):
+            logging.info('checking %s permissions %s for %s',
+               self.__class__.__name__[-3:], self.required_perms, func.__name__)            
+            
+            if self.check_permissions():
+                logging.info('Permission granted for %s', func.__name__)
+                return func(*args, **kwargs)
+            
+            else:
+                logging.warning('Permission denied for %s', func.__name__)
+                #redirect with forbidden ret code
+                return redirect(url('access_denied'), 403) 
+        return _wrapper
+        
+        
+    def check_permissions(self):
+        """
+        Dummy function for overiding
+        """
+        raise Exception('You have to write this function in child class')
+
+class CheckPermissionAll(PermsDecorator):
+    """
+    Checks for access permission for all given predicates. All of them have to
+    be meet in order to fulfill the request
+    """
+        
+    def check_permissions(self):
+        if self.required_perms.issubset(self.user_perms):
+            return True
+        return False
+            
+
+class CheckPermissionAny(PermsDecorator):
+    """
+    Checks for access permission for any of given predicates. In order to 
+    fulfill the request any of predicates must be meet
+    """
+    
+    def check_permissions(self):
+        if self.required_perms.intersection(self.user_perms):
+            return True
+        return False
+
+
+
--- a/pylons_app/lib/db_manage.py	Sun May 30 17:55:56 2010 +0200
+++ b/pylons_app/lib/db_manage.py	Sun May 30 19:49:40 2010 +0200
@@ -1,17 +1,16 @@
+from os.path import dirname as dn, join as jn
+from pylons_app.lib.auth import get_crypt_password
+from pylons_app.model import init_model
+from pylons_app.model.db import User, Permission
+from pylons_app.model.meta import Session, Base
+from sqlalchemy.engine import create_engine
 import logging
-from os.path import dirname as dn
-from os.path import join as jn
-from sqlalchemy.engine import create_engine
 import os
 import sys
 ROOT = dn(dn(dn(os.path.realpath(__file__))))
 sys.path.append(ROOT)
 
-from pylons_app.model.db import User
-from pylons_app.model.meta import Session, Base
 
-from pylons_app.lib.auth import get_crypt_password
-from pylons_app.model import init_model
 
 log = logging.getLogger('db manage')
 log.setLevel(logging.DEBUG)
@@ -68,9 +67,28 @@
             self.sa.rollback()
             raise
     
+    def create_permissions(self):
+        perms = [('can_view_admin_users', 'Access to admin user view'),
+                 
+                 ]
+        
+        for p in perms:
+            new_perm = Permission()
+            new_perm.permission_name = p[0]
+            new_perm.permission_longname = p[1]
+            try:
+                self.sa.add(new_perm)
+                self.sa.commit()
+            except:
+                self.sa.rollback()
+                raise
+        
+        
+        
 if __name__ == '__main__':
     dbmanage = DbManage(log_sql=True)
     dbmanage.create_tables(override=True)
-    dbmanage.admin_prompt()  
+    dbmanage.admin_prompt()
+    dbmanage.create_permissions()  
 
 
--- a/pylons_app/model/db.py	Sun May 30 17:55:56 2010 +0200
+++ b/pylons_app/model/db.py	Sun May 30 19:49:40 2010 +0200
@@ -40,6 +40,7 @@
     __table_args__ = {'useexisting':True}
     permission_id = Column("id", INTEGER(), nullable=False, unique=True, default=None, primary_key=1)
     permission_name = Column("permission_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
-
+    permission_longname = Column("permission_longname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
+    
     def __repr__(self):
         return "<Permission('%s:%s')>" % (self.permission_id, self.permission_name)
--- a/pylons_app/templates/admin/admin.html	Sun May 30 17:55:56 2010 +0200
+++ b/pylons_app/templates/admin/admin.html	Sun May 30 19:49:40 2010 +0200
@@ -12,15 +12,10 @@
 	${self.submenu('')}
 </%def>
 <%def name="main()">
-    %if c.admin_user:
-	    <div>
-	        <h2>Welcome ${c.admin_username}</h2>
-			    <div id="user_log">
-					${c.log_data}
-				</div>
-	    </div>
-    %else:
-		<h2>${_('Sorry only admin users can acces this area')}</h2>
-    %endif
-    
+    <div>
+        <h2>Welcome ${c.admin_username}</h2>
+		    <div id="user_log">
+				${c.log_data}
+			</div>
+    </div>
 </%def>
\ No newline at end of file