Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I access URL parameters from within a BasePermission?

I'm trying to write a custom rest_framework Permission to prevent users from querying information that's not of the same company as them. Unfortunately, I can't seem to access any of the URL's parameters from within has_permission() or has_object_permissions().

Here's the beginning of my router:

# Create a basic router
router = routers.SimpleRouter()
# Establish some variables to assist with nested routes
root_elem = 'companies'
root_elem_id = '/(?P<company_id>[0-9]+)'
loca_elem = '/locations'
loca_elem_id = '/(?P<location_id>[0-9]+)'
# Companies will be the root from which all other relations branch
router.register(r'' + root_elem, views.CompanyViewSet)
router.register(r'' + root_elem + root_elem_id + loca_elem,
                views.LocationViewSet)

Here's my custom permission:

# Only permit actions originating from location managers or company admins
class IsLocationManagerOrHigher(BasePermission):
    # Checked when displaying lists of records
    def has_permission(self, request, *args, **kwargs):
        is_correct_level = False
        # Admins can see every location if their location_id
        # matches a location that's a child of the company
        # specified in the URL
        if request.employee.is_admin:
            is_correct_level = True

        return request.user and is_correct_level

    # Checked when viewing specific records
    def has_object_permission(self, request, view, obj):
        is_correct_level = False
        # Admins can see location details if their location's company_id
        # matches a Location's company_id
        if request.employee.is_admin:
            is_correct_level = True
        # Managers can see location details if it's their location
        elif obj.id == request.employee.location_id and request.employee.is_manager:
            is_correct_level = True

        return request.user and is_correct_level

Right now checking request.employee.is_admin is only half of what I need - I also need to access the company_id from the URL and make sure it matches the admin's location's company_id:

# Pseudocode
try:
    user_location = Location.objects.get(id=request.employee.location_id)
    return user_location.company_id == kwargs['company_id']
except ObjectDoesNotExist:
    pass

I've yet to figure out how to pass these parameters into the Permission so that it can perform this extra step. Or perhaps there's a better way of accomplishing what I'm trying to do?

like image 718
IAmKale Avatar asked Apr 15 '15 20:04

IAmKale


2 Answers

If you can't pass them in directly (which would be preferable), they are available on the request object:

company_id = request.resolver_match.kwargs.get('company_id')

request.resolver_match.args and request.resolver_match.kwargs contain the positional/keyword arguments captured in your url.

like image 87
knbk Avatar answered Oct 13 '22 20:10

knbk


As an alternative to the correct response posted by knbk, you can also get the URL parameters using the view object passed to has_permission method. Like this:

company_id = view.kwargs.get('company_id')

like image 34
Rocckk Avatar answered Oct 13 '22 19:10

Rocckk