开发者

How do I set up add, delete, update and view permissions for a particular model instance?

I am a Python and Django newbie working on a Django app and I have a requirement in which I have to manage permissions for model instance Foo in my Django. There are other model instances available but the permissions only apply for foo.

I have overridden has_add_permission, has_delete_permission in my FooAdmin class but nothing seems to be wo开发者_开发问答rking.

Requirement:

  1. Add Foo object: The user must be a super admin
  2. Delete Foo object: The user must be a super admin
  3. Edit existing Foo object: The user must belong to ldap group 'foo-edit'. If the user belongs to this group, then he/she can only edit one field (building) (Not Yet Implemented)
  4. View Foo: If the user belongs to ldap group 'foo-access', then the user has only view rights. (To Be Investigated)
  5. If any other model instance is to be added, updated, deleted or modified, then rules 1 to 4 don't apply.

My implementation:

class FooAdmin(ModelAdmin):
  ...
  ...
  ...
  def has_add_permission(self, request):
    permissions = self.opts.app_label + '.' + self.opts.get_add_permission()
    # Other objects are fine.
    user = request.user
    if (user.has_perm('survey.add_foo') and user.is_authenticated
        and user.is_superuser):
      return True
    return False

  def has_delete_permission(self, request, obj=None):
    permissions = self.opts.app_label + '.' + self.opts.get_delete_permission()
    user = request.user
    if (user.has_perm('survey.delete_foo') and user.is_authenticated
        and user.is_superuser):
      return True
    return False

Issue:

  1. I can create another instance of type Bar but can not delete what I have just created. This is not a problem with instances of type FooBar. BarAdmin implementation does not have any code that manages permissions as is the case of FooBarAdmin implementation.

    Is there a way to resolve this issue and make Bar and FooBar deletable again?

  2. How do I implement required # 3 and 4? I can override has_change_permission method to determine if the person has change permissions. How do I give the user editing rights for only one field.

    Django does not seem to support view only permissions. I can create has_view_permission(self, request) and get_model_perms(self, request) and other places? But I am not sure how to implement this though. I have been looking through the Django source code but can't think of anything.


After a bit of Googling and random hacks, I have able to come up with a solution. I don't know what the protocol is to answer to your question.

  1. Create a Middle ware implementation (threadlocals.py) that stores the user information in a thread local object.

    try:
      from threading import local
    except ImportError:
      from django.utils._threading_local import local
    
    _thread_locals = local()
    def get_current_user():
      return getattr(_thread_locals, 'user', None)
    
    class ThreadLocals(object):
      """Middleware that gets various objects from the request object and
      saves them in thread local storage."""
    
      def process_request(self, request):
        _thread_locals.user = getattr(request, 'user', None)
    
  2. Create an authorization utility module that will check for permissions.

    import ldap  
    from django.contrib.auth.models import User    
    from myapp.middleware import threadlocals
    
    def can_edit():
      user_obj = threadlocals.get_current_user()
      if user_obj and (is_superadmin(user_obj.username) or \
          is_part_of_group(user_obj.username, 'foo-edit')):
        return True
      return False
    
    def can_edit_selectively():
      user = threadlocals.get_current_user()
      if (user and not user.is_superuser and
          is_part_of_group(user.username, 'foo-edit')):
        return True
      return False
    
    def can_view_only():
      user_obj = threadlocals.get_current_user()
      if (user_obj and not user_obj.is_superuser and
          is_part_of_group(user_obj.username, 'foo-access') and
          not is_part_of_group(user_obj.username, 'foo-edit')):
        return True
      return False
    
    def has_add_foo_permission():
      user = threadlocals.get_current_user()
      if (user and user.has_perm('survey.add_foo') and user.is_authenticated
          and user.is_superuser):
        return True
      return False
    
    def has_delete_foo_permission():
      user = threadlocals.get_current_user()
      if (user and user.has_perm('survey.delete_foo') and user.is_authenticated
          and user.is_superuser):
        return True
      return False
    
    def is_superadmin(logged_in_user):
       admin = False
       try:
         user = User.objects.get(username=logged_in_user)
         if user.is_superuser:
           admin = True
         except User.DoesNotExist:
           pass
      return admin
    
    def is_part_of_group(user, group):
      try:
        user_obj = User.objects.get(username=user)
        if user_obj.groups.filter(name=group):
          return True
      except User.DoesNotExist:
        pass
      conn = ldap.open("ldap.corp.mycompany.com")
      conn.simple_bind_s('', '')
      base = "ou=Groups,dc=mycompany,dc=com"
      scope = ldap.SCOPE_SUBTREE
      filterstr = "cn=%s" % group
      retrieve_attributes = None
      try:
        ldap_result_id = conn.search_s(base, scope, filterstr, retrieve_attributes)
        if ldap_result_id:
          results = ldap_result_id[0][1]
          return user in results['memberUid']
      except ldap.LDAPError:
        pass
      return False
    
  3. Set up Admin. In the actions, the permissions are evaluated. Also if the user does not have delete permissions, then the action 'delete Foo' is not available.

    class FooAdmin(ModelAdmin):
    
      ...
    
      def get_actions(self, request):
        actions = super(FooAdmin, self).get_actions(request)
        delete_permission = self.has_delete_permission(request)
        if actions and not delete_permission:
          del actions['delete_selected']
        return actions
    
      def has_add_permission(self, request):
        return auth_utils.has_add_foo_permission()
    
      def has_delete_permission(self, request, obj=None):
        return auth_utils.has_delete_foo_permission()
    
      ....
    
  4. Make changes to init function in the implementation of ModelForm

     class FooForm(ModelForm):
    
       def __init__(self, *args, **kwargs):
         self.can_view_only = (auth_utils.can_view_only() and
                               not auth_utils.can_edit_selectively())
         self.can_edit_selectively = (auth_utils.can_edit_selectively() or
                                      auth_utils.can_view_only())
         if self.can_edit_selectively:
           ... Set the widget attributes to read only.
    
         # If the user has view only rights, then disable edit rights for building id.
         if self.can_view_only:
           self.fields['buildingid'].widget = forms.widgets.TextInput()
           self.fields['buildingid'].widget.attrs['readonly'] = True
         elif (auth_utils.can_edit() or auth_utils.can_edit_selectively()):
           self.fields['buildingid'].widget=forms.Select(choices=
                                                    utils.get_table_values('building'))
    
  5. I also had to disable the actions that modified the submit line.

    def submit_edit_selected_row(context):
      opts = context['opts']
      change = context['change']
      is_popup = context['is_popup']
      save_as = context['save_as']
      if opts.object_name.lower() != 'foo':
        has_delete = (not is_popup and context['has_delete_permission']
                      and (change or context['show_delete']))
        has_save_as_new = not is_popup and change and save_as
        has_save_and_add_another = (context['has_add_permission'] and
                                    not is_popup and (not save_as or context['add']))
        has_save_and_continue = (context['has_add_permission'] and
                                 not is_popup and (not save_as or context['add']))
        has_save = True
      else:
        has_delete = auth_utils.has_delete_pop_permission()
        has_save_as_new = auth_utils.has_add_pop_permission()
        has_save_and_add_another = auth_utils.has_add_pop_permission()
        has_save_and_continue = (auth_utils.can_edit() or
                                 auth_utils.can_edit_selectively())
        has_save = auth_utils.can_edit() or auth_utils.can_edit_selectively()
      return {
        'onclick_attrib': (opts.get_ordered_objects() and change
                              and 'onclick="submitOrderForm();"' or ''),
        'show_delete_link': has_delete,
        'show_save_as_as_new': has_save_as_new,
        'show_save_and_add_another': has_save_and_add_another,
        'show_save_and_continue': has_save_and_continue,
        'is_popup': is_popup,
        'show_save': has_save
       }
    register.inclusion_tag('admin/submit_line.html', takes_context=True) (submit_edit_selected_row)
    
  6. I had to modify extend the admin template. The rest remains the same.

    {% if save_on_top %} {% submit_edit_selected_row %} {% endif}
    
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜