Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find the required permissions of Django URLs without calling them?

My Django app currently has URLs which are protected by 'permission_required()' functions.

This function is called in three different ways.

  1. As a decorator in views.py, with hardcoded parameters.
  2. As a plain function, with autogenerated parameter, in custom Class Based Generic Views.
  3. As a function invoking views in urls.py, with hardcoded parameters.

I'm now adding a menu system to the app, and I need to make menu entries reflect whether the user has permission to request the URL of each menu entry. (Either by greying-out or hiding said entries.)

Is there a way of query the permissions required to a URL without requesting the URL?

The only solution I've thought of so far is to replace the decorator with a parameterless 'menu_permssion_required()' decorator and hardcode all of the permissions into a Python structure. This seems like a step backwards, as my custom Class Based Generic Views already autogenerate their required permissions.

Any suggestions on how to make a menu system which reflects URL permissions for the current user?

like image 318
fadedbee Avatar asked Jun 10 '12 05:06

fadedbee


1 Answers

Here is an example of how to solve your problem:

First, Create a decorator wrapper to use instead of permission_required:

from django.contrib.auth.decorators import login_required, permission_required, user_passes_test
from django.core.exceptions import PermissionDenied
from functools import wraps
from  django.utils.decorators import available_attrs

def require_perms(*perms):
    def decorator(view_func):
        view_func.permissions = perms
        @wraps(view_func, assigned=available_attrs(view_func))
        def _wrapped_view(request, *args, **kwargs):
            for perm in perms:
                return view_func(request, *args, **kwargs)
            raise PermissionDenied()
        return _wrapped_view
    return decorator

Then, use it to decorate your views:

@require_perms('my_perm',)
def home(request):
.....

Then, add a tag to use for your menu items:

from django.core.urlresolvers import resolve

def check_menu_permissions(menu_path, user):
    view = resolve(menu_path)
    if hasattr(view.func, "permissions"):
        permissions = view.func.permissions
        for perm in permissions:
            if user.has_perm(perm):
                return True # Yep, the user can access this url
            else:
                return False # Nope, the user cannot access this url
    return True #  or False - depending on what is the default behavior

And finally, in your templates, when building the menu tree:

<button href="{{ some_path }} {% if not check_menu_permissions some_path request.user %}disabled="disabled"{% endif %} />

N.B. I've not tested the last part with the tag, but I hope you got the idea. The magic thing here is to add the permissions to the view_func in the decorator, and then you can access this using resolve(path). I'm not sure how this will behave in terms of performance, but after all that's just an idea.

EDIT: Just fixed a bug in the example..

like image 187
Tisho Avatar answered Oct 05 '22 23:10

Tisho