Michael Hall avatar Michael Hall committed dd9170a

Refactoring to compine auth and fieldperms

Comments (0)

Files changed (11)

+syntax: glob
+*.pyc
+
+from extauth.auth.models import Role
+from django.contrib import admin
+
+class RoleAdmin(admin.ModelAdmin):
+    search_fields = ('model','role')
+    ordering = ('model','role')
+    filter_horizontal = ('permissions',)
+
+admin.site.register(Role, RoleAdmin)

extauth/backend.py

+'''
+Copyright 2009 H. Lee Moffitt Cancer Center and Research Institute, Inc. 
+All rights reserved.
+
+@author: Michael Hall <mhall119@gmail.com>
+'''
+

extauth/management/__init__.py

+'''
+Copyright 2009 H. Lee Moffitt Cancer Center and Research Institute, Inc. 
+All rights reserved.
+
+@author: Michael Hall <mhall119@gmail.com>
+'''
+
+from django.db.models import get_models, signals
+from django.contrib.auth import models as auth_app
+from django.db.models import get_apps, get_models, signals
+
+def create_roles(app, created_models, verbosity, **kwargs):
+    from django.contrib.contenttypes.models import ContentType
+    from django.contrib.auth.models import Permission
+    from extauth.auth.models import Role
+    from extauth.auth import roles
+    roles.autodiscover()
+    
+    if not roles.registered_roles:
+        return
+    for klass in roles.registered_roles.keys():
+        ctype = ContentType.objects.get_for_model(klass)
+        rm = roles.get_rolemanager(klass)
+        
+        for rName in rm._roles:
+            r, created = Role.objects.get_or_create(role=rName, model__pk=ctype.id,
+                defaults={'model': ctype})
+            if created and verbosity >= 2:
+                print "Adding role '%s'" % r
+
+signals.post_syncdb.connect(create_roles,
+    dispatch_uid = "extauth.auth.management.create_roles")
+
+def create_model_permissions(app, created_models, verbosity, **kargs):
+    from django.contrib.contenttypes.models import ContentType
+    from django.contrib.auth.models import Permission
+    app_models = get_models(app)
+    if not app_models:
+        return
+    for klass in app_models:
+        ctype = ContentType.objects.get_for_model(klass)
+        for codename, name in _get_all_permissions(klass):
+            p, created = Permission.objects.get_or_create(codename=codename, content_type__pk=ctype.id,
+                defaults={'name': name, 'content_type': ctype})
+            if created and verbosity >= 2:
+                print "Adding permission '%s'" % p
+        
+def _get_all_permissions(klass):
+    "Returns (codename, name) for all permissions in the given opts."
+    from o9 import permissions
+    perms = [('read_%s' % klass._meta.object_name.lower(), u'Can read %s' % klass._meta.verbose_name_raw),]
+    for field in klass._meta.fields + klass._meta.many_to_many:
+        if field.name != 'id':
+            perms.append(('modify_%s__%s' % (klass._meta.object_name.lower(), field.name), u'Can modify %s %s' % (klass._meta.verbose_name_raw, field.name)))
+            perms.append(('view_%s__%s' % (klass._meta.object_name.lower(), field.name), u'Can view %s %s' % (klass._meta.verbose_name_raw, field.name)))
+    return perms
+
+signals.post_syncdb.connect(create_model_permissions,
+    dispatch_uid = "o9.management.create_model_permissions")
+
+def create_page_permissions(app, created_models, verbosity, **kwargs):
+    from django.contrib.contenttypes.models import ContentType
+    from django.contrib.auth.models import Permission
+    from django.contrib.sites.models import Site
+    from o9 import sites
+    
+    ctype = ContentType.objects.get_for_model(Site)
+    
+    for s in sites.managed_sites:
+        if hasattr(s.Meta, 'permissions') and isinstance(s.Meta.permissions, (list, tuple)):
+            for (codename, name) in s.Meta.permissions:
+                p, created = Permission.objects.get_or_create(codename=codename, content_type__pk=ctype.id,
+                    defaults={'name': name, 'content_type': ctype})
+                if created and verbosity >= 2:
+                    print "Adding permission '%s'" % p            
+
+signals.post_syncdb.connect(create_page_permissions,
+    dispatch_uid = "o9.management.create_page_permissions")
+
Add a comment to this file

extauth/management/commands/__init__.py

Empty file added.

extauth/management/commands/updateperms.py

+'''
+Copyright 2009 H. Lee Moffitt Cancer Center and Research Institute, Inc. 
+All rights reserved.
+
+@author: Michael Hall <mhall119@gmail.com>
+'''
+from django.core.management.base import NoArgsCommand, CommandError
+from o9.management import create_model_permissions, create_page_permissions
+from django.contrib.auth.management import create_permissions
+from django.db.models import get_app, get_apps
+from django.conf import settings
+from django.db import transaction
+
+class Command(NoArgsCommand):
+    
+    def handle_noargs(self, **options):
+        verbosity = int(options.pop('verbosity', 1))
+        
+        for app in get_apps():
+            if verbosity >= 1:
+                print "Updating Permissions for %s" % app.__name__
+                            
+            if verbosity >= 1:
+                print "Creating standard permissions"
+            create_permissions(app, [], verbosity, **options)
+            
+            if verbosity >= 1:
+                print "Creating O9 model permissions"
+            create_model_permissions(app, [], verbosity, **options)
+            
+            if verbosity >= 1:
+                print "Creating O9 page permissions"
+            create_page_permissions(app, [], verbosity, **options)
+
+            

extauth/management/commands/updateroles.py

+'''
+Copyright 2009 H. Lee Moffitt Cancer Center and Research Institute, Inc. 
+All rights reserved.
+
+Created on Mar 9, 2010
+
+@author: Michael Hall <mhall119@gmail.com>
+'''
+from django.core.management.base import NoArgsCommand
+from extauth.auth.management import create_roles
+from django.db.models import get_apps
+
+class Command(NoArgsCommand):
+    
+    def handle_noargs(self, **options):
+        verbosity = int(options.pop('verbosity', 1))
+        
+        for app in get_apps():
+            if verbosity >= 1:
+                print "Creating Roles for %s" % app.__name__
+            create_roles(app, [], verbosity, **options)
+
+            

extauth/models.py

+'''
+Copyright 2009 H. Lee Moffitt Cancer Center and Research Institute, Inc. 
+All rights reserved.
+
+@author: Michael Hall <mhall119@gmail.com>
+'''
+from django.contrib import auth
+from django.db import models
+from django.contrib.contenttypes.models import ContentType
+
+class Role(models.Model):
+    
+    class Meta:
+        verbose_name = "Role"
+        unique_together = (('model', 'role'),)
+        
+    model = models.ForeignKey(ContentType, verbose_name='Model Type')
+    role = models.CharField(max_length=255, verbose_name='Role Name')
+    permissions = models.ManyToManyField(auth.models.Permission, blank=True, verbose_name='Role Permissions')
+    
+    def __unicode__(self):
+        return "%s.%s: %s" % (self.model.app_label, self.model, self.role)
+

extauth/permissions.py

+'''
+Copyright 2009 H. Lee Moffitt Cancer Center and Research Institute, Inc. 
+All rights reserved.
+
+@author: Michael Hall <mhall119@gmail.com>
+'''
+from extauth import session
+
+ALL_USERS = '*'
+
+def has_perm(perm, user=None):
+    if perm == ALL_USERS:
+        return True
+    
+    if user is None:
+        user = session.get_current_user()
+
+    if user is None:
+        return False
+    else:
+        return user.has_perm(perm)
+
+def require_perm(perm, user=None):
+    if user is None:
+        user = session.get_current_user()
+
+    if has_perm(perm, user):
+        return True
+    else:
+        if user is None or not user.is_authenticated():
+            raise LoginRequiredException(user, perm, None)
+        else:
+            raise InsufficientPermsException(user, perm, None)
+        
+def get_add_permission(model_class):
+    return '%s.add_%s' % (model_class._meta.app_label, model_class._meta.object_name.lower())
+
+def get_read_permission(model_class):
+    return '%s.read_%s' % (model_class._meta.app_label, model_class._meta.object_name.lower())
+
+def get_change_permission(model_class):
+    return '%s.change_%s' % (model_class._meta.app_label, model_class._meta.object_name.lower())
+
+def get_delete_permission(model_class):
+    return '%s.delete_%s' % (model_class._meta.app_label, model_class._meta.object_name.lower())
+
+def get_modify_permission(model_class, field_name):
+    return '%s.modify_%s__%s' % (model_class._meta.app_label, model_class._meta.object_name.lower(), field_name)
+
+def get_view_permission(model_class, field_name):
+    return '%s.view_%s__%s' % (model_class._meta.app_label, model_class._meta.object_name.lower(), field_name)
+
+class PermissionException(Exception):
+    def __init__(self, user, node, perm, msg):
+        self.user = user
+        self.node = node
+        self.msg = msg
+        
+    def __str__(self):
+        return repr(self.msg)
+
+class InsufficientPermsException(PermissionException):
+    
+    def __init__(self, user, perm, node):
+        self.user = user
+        self.perm = perm
+        self.node = node
+        self.msg = "User %s does not have permission to access this page: %s" % (user, perm)
+
+class LoginRequiredException(PermissionException):
+    def __init__(self, user, perm, node):
+        self.user = user
+        self.perm = perm
+        self.node = node
+        self.msg = "User must be logged in to access this page"
+
+'''
+Copyright 2009 H. Lee Moffitt Cancer Center and Research Institute, Inc. 
+All rights reserved.
+
+@author: Michael Hall <mhall119@gmail.com>
+'''
+
+from extauth import utils, session
+from extauth.auth.models import Role
+from extauth.fieldperms import permissions
+
+from django.contrib.auth.models import User, Group
+from django.contrib.contenttypes.models import ContentType
+from django.utils.functional import curry
+import copy
+
+registered_roles = {}
+def register(model_class, role_class=None):
+    if registered_roles.has_key(model_class):
+        raise RoleException("Model %s has already been registered with a RoleManager" % model_class)
+    else:
+        registered_roles[model_class] = role_class or EmptyRoles
+        
+def get_rolemanager(model_class):
+    if not registered_roles.has_key(model_class):
+        return EmptyRoles
+        #raise RoleException(None, model_class, "Model %s has not been registered with a RoleManager" % model_class)
+    else:
+        return registered_roles[model_class]
+
+def get_roles(model_inst, user=None):
+    if user is None:
+        user = session.get_current_user()
+    role_class = get_rolemanager(model_inst.__class__)
+    role_inst = role_class(model_inst, user)
+    return role_inst
+
+class RoleException(Exception):
+    def __init__(self, user, model, msg):
+        super(RoleException, self).__init__(msg)
+        self.user = user
+        self.model = model
+        
+class RoleManagerBase(type):
+    """
+    Metaclass for all Role Managers.
+    """
+    def __new__(cls, name, bases, attrs):
+        super_new = super(RoleManagerBase, cls).__new__
+        parents = [b for b in bases if isinstance(b, RoleManagerBase)]
+        if not parents:
+            # If this isn't a subclass of RoleManager, don't do anything special.
+            return super_new(cls, name, bases, attrs)
+
+        # Create the class.
+        module = attrs.pop('__module__')
+        new_class = super_new(cls, name, bases, {'__module__': module})
+        
+        roles = dict()
+        for aName, a in attrs.items():
+            if isinstance(a, RoleBase):
+                roles[aName] = a
+            else:
+                setattr(new_class, aName, a)
+        setattr(new_class, '_roles', roles)
+        return new_class
+    
+class RoleManager(object):
+    'Role Managers define the available roles for a given model'
+    
+    __metaclass__ = RoleManagerBase
+    
+    def __init__(self, model, user=None):
+        self.model = model
+        if user is None:
+            self.user = session.get_current_user()
+        else:
+            self.user = user
+        
+        roles = {}
+        _roles = self._roles
+        for (rName, r) in self._roles.items():
+            role = copy.deepcopy(r)
+            role.user = self.user
+            role.model = self.model
+            roles[rName] = role
+        setattr(self, 'roles', roles)
+            
+    def __getattr__(self, aName):
+        if aName in self.roles:
+            return self.roles[aName].hasRole()
+        raise AttributeError
+    
+    def getUserRoles(self):
+        uRoles = []
+        for rName, role in self.roles.items():
+            if role.hasRole():
+                uRoles.append(rName)
+        return uRoles
+    
+    def has_perm(self, pName):
+        model_type = ContentType.objects.get_for_model(self.model.__class__)
+        (app, sep, perm) = pName.partition('.')
+        if not perm or perm == '':
+            perm = app
+            app = self.model._meta.app_label
+            
+        perms = Role.objects.filter(model=model_type, 
+                                        role__in=self.getUserRoles(),
+                                        permissions__content_type__app_label=app,
+                                        permissions__codename=perm)
+        if perms.count() > 0:
+            return True
+        else:
+            return permissions.has_perm(pName, self.user)
+                
+    def has_any_perm(self, *perms):
+        for p in perms:
+            if self.has_perm(p):
+                return True
+        return False
+    
+    def has_all_perms(self, *perms):
+        for p in perms:
+            if not self.has_perm(p):
+                return False
+        return True
+
+    def require_perm(self, perm):
+        if self.has_perm(perm):
+            return True
+        else:
+            raise PermissionDenied("User does not have required permissions: %s" % perm, params={'user': self.user, 'perm': perm})
+    
+class RoleBase(object):
+    'Base class for all Roles'
+    
+    def __init__(self):
+        self._user = None
+        self._model = None
+        pass
+    
+    def setUser(self, user):
+        if isinstance(user, User):
+            self._user = user
+        else:
+            raise TypeError("Argument must be django.contrib.auth.models.User, not %s" % type(user))
+        
+    def getUser(self):
+        return self._user
+    user = property(getUser, setUser)
+    
+    def setModel(self, model):
+        self._model = model
+        
+    def getModel(self):
+        return self._model
+    model = property(getModel, setModel)
+    
+    def hasRole(self):
+        return False
+    
+    def __call__(self):
+        return self.hasRole()    
+    
+class UserIsAuthenticated(RoleBase):
+    'Any Authenticated user will have this role'
+    def __init__(self):
+        super(UserIsAuthenticated, self).__init__()
+    
+    def hasRole(self):
+        if self.user is None:
+            return False
+        
+        return self.user.is_authenticated()
+    
+class IsSelf(RoleBase):
+    """
+    Users will have this role on their own django.contrib.auth.models.User 
+    object (or any child-class of it)
+    """
+    def __init__(self):
+        super(IsSelf, self).__init__()
+    
+    def hasRole(self):
+        if isinstance(self.model, type(self.user)) and self.user.id == self.model.id:
+            return True
+        return False
+    
+class UserIs(RoleBase):
+    """
+    Users will have this role of the given model field is either a ForeignKey
+    to their django.contrib.auth.models.User object (or any child-class of it)
+    or a field who's string value matches their username
+    """    
+    def __init__(self, model_field):
+        super(UserIs, self).__init__()
+        self._model_field = model_field
+        
+    def setModelField(self, model_field):
+        self._model_field = model_field
+    
+    def getModelField(self):
+        return self._model_field
+    model_field = property(getModelField, setModelField)
+    
+    def hasRole(self):
+        if self.model is None or self.user is None:
+            return False
+        
+        v = utils.get_model_value(self.model, self.model_field)
+        if v is None:
+            return False
+        
+        if isinstance(v, User):
+            return v.username == self.user.username
+        else:
+            return v == self.user.username
+    
+class UserIn(RoleBase):
+    """
+    Users will have this role of the given model field is either a ForeignKey
+    to a django.contrib.auth.models.Group object (or any child-class of it)
+    of which they are a member, or the field's string value is that of a group
+    of which they are a member.
+    """    
+    
+    def __init__(self, model_field):
+        super(UserIn, self).__init__()
+        self._model_field = model_field        
+        
+    def setModelField(self, model_field):
+        self._model_field = model_field
+    
+    def getModelField(self):
+        return self._model_field
+    model_field = property(getModelField, setModelField)
+    
+    def hasRole(self):
+        if self.model is None or self.user is None:
+            return False
+        
+        v = utils.get_model_value(self.model, self.model_field)
+        if v is None:
+            return False
+        
+        if isinstance(v, Group):
+            return v in self.user.groups.all()
+        else:
+            return self.user.groups.filter(name=v).count() > 0
+    
+class Not(RoleBase):
+    """
+    Wraps another Role instance, and returns the inverse.  If the user has the
+    given role, this will say that they don't.  If the user doesn't have the
+    role, this will say that they do
+    """
+    def __init__(self, role):
+        self.role = role
+        
+    def setUser(self, user):
+        super(Not, self).setUser(user)
+        self.role.setUser(user)
+        
+    def setModel(self, model):
+        super(Not, self).setModel(model)
+        self.role.setModel(model)
+    
+    def hasRole(self):
+        return not self.role.hasRole()
+        
+class Any(RoleBase):
+    """
+    Wraps multiple Role instances.  A user will have this role if they have any
+    one of the contained roles.
+    """
+    def __init__(self, *roles):
+        self.roles = []
+        for r in roles:
+            if isinstance(r, RoleBase):
+                self.roles.append(r)
+                
+    def setUser(self, user):
+        super(Not, self).setUser(user)
+        for r in self.roles:
+            r.setUser(user)
+        
+    def setModel(self, model):
+        super(Not, self).setModel(model)
+        for r in self.roles:
+            r.setModel(model)
+    
+    def hasRole(self):
+        for r in self.roles:
+            if r.hasRole():
+                return True
+        return False
+        
+class All(Any):
+    """
+    Wraps multiple Role instances.  A user will have this role if they have all
+    of the contained roles.
+    """
+
+    def hasRole(self):
+        for r in self.roles:
+            if not r.hasRole():
+                return False
+        return True
+        
+class EmptyRoles(RoleManager):
+    """
+    Provides no Roles, this is used when requesting Roles from an object type
+    that does not have a RoleManager defined
+    """
+    pass
+    
+def autodiscover():
+    """
+    Auto-discover INSTALLED_APPS admin.py modules and fail silently when 
+    not present. This forces an import on them to register any role bits they
+    may want.
+    """
+    import imp
+    from django.conf import settings
+
+    for app in settings.INSTALLED_APPS:
+        # For each app, we need to look for an admin.py inside that app's
+        # package. We can't use os.path here -- recall that modules may be
+        # imported different ways (think zip files) -- so we need to get
+        # the app's __path__ and look for admin.py on that path.
+
+        # Step 1: find out the app's __path__ Import errors here will (and
+        # should) bubble up, but a missing __path__ (which is legal, but weird)
+        # fails silently -- apps that do weird things with __path__ might
+        # need to roll their own admin registration.
+        try:
+            app_path = __import__(app, {}, {}, [app.split('.')[-1]]).__path__
+        except AttributeError:
+            continue
+
+        # Step 2: use imp.find_module to find the app's admin.py. For some
+        # reason imp.find_module raises ImportError if the app can't be found
+        # but doesn't actually try to import the module. So skip this app if
+        # its admin.py doesn't exist
+        try:
+            imp.find_module('roles', app_path)
+        except ImportError:
+            continue
+
+        # Step 3: import the app's admin file. If this has errors we want them
+        # to bubble up.
+        __import__("%s.roles" % app)

extauth/session.py

     _thread_locals.user = user
 
 class CaptureRequestUser(object):
-    "Middleware that captures the current HTTP request user for o9 permissions"
+    "Middleware that captures the current HTTP request user for ExtAuth permissions"
     
     def process_request(self, request):
         #if hasattr(request, 'user'):
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.