restrict access instead of allowing it #26

Closed
vIiRuS opened this Issue Jul 22, 2014 · 9 comments

Projects

None yet

2 participants

@vIiRuS
vIiRuS commented Jul 22, 2014

Hi

I want to use django-permission for a project I am working on, but I'm not sure how to achieve what I want.
I used the normal django permissions stuff until now, but now I need to restrict access to some objects. I still want to use the django permissions for defining which user is generally allowed to read/add/change/delete objects. But I also want to add a check, that checks if a object is marked as private and if yes, the user has to be in a specific group.

Now the problem is, that as soon as a user has the read permission for all objects of that model, my custom permission code is not called. But I want my custom permission code to be executed explicitly when a user has the global permission for that model to ensure that the user also has the permission for this specific object
Is there any way to do this?

@lambdalisue
Owner

Well it sounds like you are mis-understanding the usage of django-permission.

as soon as a user has the read permission for all objects of that model, my custom permission code is not called

If you want to call custom permission, you have to (I'm assuming you have Entry model of blogs app)

  1. Remove 'blogs.read_entry' permission from all user to handle that permission with django-permission
  2. Write your custom permission logic something like
# perms.py
from permission.logics.base import PermissionLogic


class BlogEntryPermissionLogic(PermissionLogic):
    def has_perm(self, user_obj, perm, obj=None):
        read_permission = self.get_full_permission_string('read')
        if perm != read_permission:
            # if the permission is not 'read' permission, ignore it
            return False
        if obj is None:
            # All user have a potential to read the Entry model (model permission)
            return True
        if obj.private:
            # The object is private thus only 'staff' users have a permission to read
            return user_obj.is_staff
        # The object is not private thus all users have a permission to read
        return True

# models.py

# ...

class Entry(models.Model):
    # ...

from permission import add_permission_logic
from perms import BlogEntryPermissionLogic
add_permission_logic(Entry, BlogEntryPermissionLogic)

django's permission system (include django-permission or any other permission plugins) prior True.
This mean that if one of the permission handler return True for particular permission, all other permission handler will be ignored. That's why first you have to remove 'read' permission from all users if you want to treat that permission with different permission handler (like django-permission).

@vIiRuS
vIiRuS commented Jul 23, 2014

The problem is, that a permission logic like that wouldn't be sufficient, as not all users are even allowed to read entries. Would the best way to do this, to create something like a read_entries_permission property for the User model and then check for that? (or some other way to replicate the original permissions for this)

@lambdalisue
Owner

Hum? I don't really get your situation.
Could you tell me the detail of your real sample? I'll think the way.

Would the best way to do this, to create something like a read_entries_permission property for the User model and then check for that?

I don't really think so. For example, if you want to allow users to delete them OWN entry then the entry should have author property and the property should be checked in has_perm method of a permission logic.
A logic should be dependent on the target model but user model to keep the model clean.

@lambdalisue
Owner

Well I think you try to use django-permission in Lagerregal right? Could you point the files which cause the problems?

Btw, this is our real usages, in case these kind of example helps you.
We basically use builtin permission logics (Author, Collaborators, Staff, Group) but some models require more specified logics.
Most of codes are commented in Japanese but I'm sure you can understand what we tried to do from the source code.

https://github.com/kawazrepos/Kawaz3rd/blob/develop/src/kawaz/apps/events/perms.py
https://github.com/kawazrepos/Kawaz3rd/blob/develop/src/kawaz/apps/products/perms.py
https://github.com/kawazrepos/Kawaz3rd/blob/develop/src/kawaz/apps/projects/perms.py
https://github.com/kawazrepos/Kawaz3rd/blob/develop/src/kawaz/core/personas/perms.py

@vIiRuS
vIiRuS commented Jul 29, 2014

The permission file is here: https://github.com/vIiRuS/Lagerregal/blob/feature/departments/devices/perms.py
Currently it's mostly just the example.

there are basically a few cases I need to handle:

  • User does NOT have devuces.read_device permission -> is not allowed to read, no matter if the device is private or not
  • User has devices.read_device permision & device is not private -> User is allowed to read
  • User has devices.read_device permission & device is private & User is in the correct department-> User is allowed to read
  • User has devices.read_device permission & device is private & User is in NOT the correct department-> User is allowed to read

The Problem is, that currently, when a user has the devices.read_device permission, they are allowed to read the device. I have no chance to check if the device is private. Which is what I want to do.
Hope this makes my problem a bit clearer?

@lambdalisue
Owner

Hum, now I got the situation.

Well,,, first of all, you don't need some codes so modify your has_perm as

    def has_perm(self, user_obj, perm, obj=None):
        if not user_obj.is_authenticated():
            return False

        if obj is None:
            return False
        elif user_obj.is_active and user_obj.has_perm(perm):
            if obj.is_private:
                if user_obj.department == obj.department:
                    return True
            else:
                return True

        return False

But this change should not solve the problem. It is a bit strange because django builtin permission backend return False for object permission thus the code should be called correctly.
Are you sure that the obj is passed to has_perm method? How do you check the permissions? Can I see the code which actually check the permission (like permission_required decorator or request.user.has_perm or whatever).

@lambdalisue
Owner

Looks like you are using permission.decorators.permission_required decorator (https://github.com/vIiRuS/Lagerregal/blob/feature/departments/devices/views.py#L103). It is correct.

Well what happen with the code below if you access the detail page of a device.

    def has_perm(self, user_obj, perm, obj=None):
        if not user_obj.is_authenticated():
            return False

        if obj is None:
            return False
        elif user_obj.is_active and user_obj.has_perm(perm):
            raise Exception("Object Permission check is called")
        return False
@vIiRuS
vIiRuS commented Aug 4, 2014

Ok. This is strange. when using the last bit of code you posted, it seems to work just as expected. I'm not really sure what the error was before, but now everything is fine. Thanks for your help!

@vIiRuS vIiRuS closed this Aug 4, 2014
@lambdalisue
Owner

That's good to hear. You are welcome :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment