Search code examples
django-oscardjango-userena

Overriding the check_permissions management command in django-userena to fix permissions for django-oscar


I am trying to use django-userena with django-oscar. So far, it is working well, except for when I try to link a non-staff user to a fulfillment partner (linking to a staff user works fine). Here is the error I am getting:

Traceback:
File "/Users/shafiquejamal/allfiles/htdocs/venvs/av4env/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  112.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/shafiquejamal/allfiles/htdocs/venvs/av4env/lib/python2.7/site-packages/django/db/transaction.py" in inner
  371.                 return func(*args, **kwargs)
File "/Users/shafiquejamal/allfiles/htdocs/venvs/av4env/lib/python2.7/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
  22.                 return view_func(request, *args, **kwargs)
File "/Users/shafiquejamal/allfiles/htdocs/venvs/av4env/lib/python2.7/site-packages/django/contrib/auth/decorators.py" in _wrapped_view
  22.                 return view_func(request, *args, **kwargs)
File "/Users/shafiquejamal/allfiles/htdocs/venvs/av4env/lib/python2.7/site-packages/django/views/generic/base.py" in view
  69.             return self.dispatch(request, *args, **kwargs)
File "/Users/shafiquejamal/allfiles/htdocs/venvs/av4env/lib/python2.7/site-packages/django/views/generic/base.py" in dispatch
  87.         return handler(request, *args, **kwargs)
File "/Users/shafiquejamal/allfiles/htdocs/venvs/av4env/lib/python2.7/site-packages/oscar/apps/dashboard/partners/views.py" in post
  219.         if self.link_user(user, partner):
File "/Users/shafiquejamal/allfiles/htdocs/venvs/av4env/lib/python2.7/site-packages/oscar/apps/dashboard/partners/views.py" in link_user
  206.                 content_type__app_label='partner')
File "/Users/shafiquejamal/allfiles/htdocs/venvs/av4env/lib/python2.7/site-packages/django/db/models/manager.py" in get
  151.         return self.get_queryset().get(*args, **kwargs)
File "/Users/shafiquejamal/allfiles/htdocs/venvs/av4env/lib/python2.7/site-packages/django/db/models/query.py" in get
  310.                 self.model._meta.object_name)

Exception Type: DoesNotExist at /en/store/dashboard/partners/1/users/380/link/
Exception Value: Permission matching query does not exist.

It seems that the problem is in oscar.apps.dashboards.partners.views:

class PartnerUserLinkView(generic.View):

    def link_user(self, user, partner):
        """
        Links a user to a partner, and adds the dashboard permission if needed.

        Returns False if the user was linked already; True otherwise.
        """
        if partner.users.filter(pk=user.pk).exists():
            return False
        partner.users.add(user)
        if not user.is_staff:
            dashboard_access_perm = Permission.objects.get(
                codename='dashboard_access',
                content_type__app_label='partner')
            user.user_permissions.add(dashboard_access_perm)
        return True

Permission.objects.get does not return an object. This is because the check_permissions command in django-userena checks the following permissions:

ASSIGNED_PERMISSIONS = {
    'profile':
        (('view_profile', 'Can view profile'),
         ('change_profile', 'Can change profile'),
         ('delete_profile', 'Can delete profile')),
    'user':
        (('change_user', 'Can change user'),
         ('delete_user', 'Can delete user'))
}

which, does not include dashboard_access. I tried to add another profile permission ('dashboard_access', _('Can access dashboard')),), to ASSIGNED_PERMISSIONS.profile, but that didn't work - I still got the same error above. I think that the problem is that even if dashboard_access is one of the permissions, the link_user method specifies content_type__app_label='partner', but content_type__app_label is never partner (partner is one of the oscar apps). So I guess I should override the check_permissions management command to check permissions in away that accommodates django-oscar. My questions are:

  1. How do I override or extend the check_permissions management command without touching the code in my virtual environment? I consulted https://docs.djangoproject.com/en/1.6/howto/custom-management-commands/, and started by copying the managers.py code from userena to a management/commands folder in my project, but got a command not implemented error.

  2. How do I modify check permissions to refer to an app label of 'partner'?

  3. Am I even going about this correctly? Maybe I misunderstand how or whether oscar and userena can work together.


Solution

  • I think I was overthinking the problem. It seems that the unlink_user method deletes the permission object that the link_user method looks for, which is one reason that the latter method cannot find the permission object (the other reason being that the object might not have been created in the first place). I hacked together a (not really?) solution by overriding the link_user method as follows:

    # yourproject/dashboard/partners/views.py
    
    from django.contrib.contenttypes.models import ContentType
    from django.contrib.auth.models import Permission
    
    from oscar.apps.dashboard.partners.views import PartnerUserLinkView as CorePartnerUserLinkView
    
    class PartnerUserLinkView(CorePartnerUserLinkView):
    
        def link_user(self, user, partner):
            """
            Links a user to a partner, and adds the dashboard permission if needed.
    
            Returns False if the user was linked already; True otherwise.
            """
            if partner.users.filter(pk=user.pk).exists():
                return False
            partner.users.add(user)
            if not user.is_staff:
                try:
                    dashboard_access_perm = Permission.objects.get(
                        codename='dashboard_access',
                        content_type__app_label='partner')
                except:
                    try:
                        my_content_type = ContentType.objects.get(name='partner',
                            app_label='partner',model='partner')
                    except:
                        my_content_type = ContentType.objects.create(name='partner',
                            app_label='partner',model='partner')
                        my_content_type.save()
                    my_permission = Permission.objects.create(name='partner',
                        content_type=my_content_type,codename='dashboard_access')
                    my_permission.save()
                    dashboard_access_perm = Permission.objects.get(
                        codename='dashboard_access',
                        content_type__app_label='partner')
                user.user_permissions.add(dashboard_access_perm)
            return True
    

    This works for me - with this hack, I can now link non-staff users to fulfillment partners.