Code


Version 22 (modified by clong, 8 years ago) (diff)

--

TOC(inline)?

Introduction

What are Row Level Permissions?

An example of row level permissions would be: "User A has read-access to article 234" or "User D has read, write access to article 234".

Why do we need this?

An example of where this would be useful is a forum or message board. With the current permission system, a user is capable of editing all the posts or unable to edit any posts. After implementing a row level permission, it can be modified so a user is capable of editing only their own personal posts.

Current Status

At this current point of time, you can create row level permissions and modify them using the API. Checking of row level permissions are currently not implemented.

Todo

  • More unit tests
  • Tidy up the admin interface and finish the AJAX interface
  • Caching the row level permissions when checking

Using Row Level Permissions

Basic Idea

There are a few things you need to know about row level permissions before working with them:

  • Row level permissions use the permissions table to determine an objects possible permissions, you need to create permissions in the permissions table before using them in row level permissions.
  • Row level permissions can be negative, this is determined by an attribute called "negative".
  • The order of checking permissions will work in the following order: User Row Level Permission -> Group Row Level Permission -> User Model Level Permission -> Group Model Level Permission. The checking will stop either at the first positive or negative, and if no permission is found will return a negative.

Enabling Row Level Permissions

Enabling row level permissions is done by using the Meta class, you enable row level permissions by setting the "row_level_permissions" attribute to true. By default, row level permissions are assumed to be disabled.

Example: To enable row level permissions for the mineral model, the model would look like:

class Mineral(models.Model):
    name = models.CharField(maxlength=150)
    hardness = models.PositiveSmallIntegerField()
    
    class Admin:
        pass
    
    class Meta:
        unique_together = (('name', 'hardness'),)
        row_level_permissions = True
  
    def __str__(self):
        return self.name

Accessing Row Level Permissions from a Model

The relation name for row level permissions from a model is "row_level_permissions", this will return all row level permissions related to the instance of the object. For example, this will return all row level permissions related to the object quartz:

...
rlp_list = quartz.row_level_permissions.all()
...

Accessing the Owner and Model of a Row Level Permission

To return the owner of a row level permission use the attribute "owner". For example:

...
user = row_level_permission.owner
...

To return the instance of a row level permission use the attribte "model". For example:

...
object = row_level_permission.model
...

Developer's note: This will most likely change as I'm not too sure "model" accurately describes what it represents. I will update this page when I make the change.

Creating a Row Level Permission

There are two helper methods to create row level permissions. These can be accessed by using the Row Level Permissions manager (e.g. RowLevelPermission.objects)

The first is create_row_level_permission:

def create_row_level_permission(self, object_instance, owner_instance, permission, negative=False):
   ...

The permission param can either be the codename of the permission or a permission instance. The negative param is optional and will default to false. You must pass an instance of the object and owner to this method. Developer's Note: I will probably add in another option to allow the permission to be an id as well.

The second is create_default_row_permissions:

def create_default_row_level_permissions(self, object_instance, owner_instance, change=True, delete=True, negChange=False, negDel=False):
   ...

This will set up a row level permission with the default permissions set up for an object. The default permissions are: add, change and delete. An example of it's use is:

...
RowLevelPermissions.objects.create_default_row_level_permissions(quartz, user, delete=False)
...

Checking Permissions

In the next week, GenericAuthorization and row level permissions will be merged. Therefore, I have shown two different methods of checking for permissions, one using the generic authorization and the currently implemented technique.

The current method uses the has_perm method in the User model. Note: The object parameter is optional, this is to allow backwards compatibility.

Example:

...
user.has_perm("can_mine", object=mineral)
...

This will return either True or False depending on if the user has the correct permission. It will return false if the user has a negative row level permission on the object.

This will also check group row level permissions. If the user is in two groups, the first having a positive row level permission and the second having a negative row level permission, it will take the positive row level permission over the negative.

Second method using GenericAuthorization will be written after the merger

Implementation Notes

Please see RowLevelPermissionsDeveloper for more information on how row level permissions are implemented.

Proposal for Integrating into Administration Interface

Inline Editing

Using the admin interface a user can edit Row Level Permissions (RLP) within the edit screen of a specific instance of a model. It will only be shown if RLPs are enabled for the model.

The RLPs will be organized in a tabular form and using AJAX the user can add, delete or change RLPs related to the instance of the model. The page will also be set up to be backwards compatible incase the user is not able to use JavaScript.

I am undecided as to whether the RLP form should be shown on the add form or not. The first priority is integrating it into the change form and then determine if it is needed in the add form.

Editing

There will probably be no capability for editing RLPs like a normal object with an add, change, and list view in the administration interface. There are a few reasons:

  • The number of RLPs could be quite high as there could possibly be one for every instance of a model. Editing this through the classic admin interface would be cumbersome.
  • Adding/Editing RLPs requires selecting the model, the instance of the model, the owner model and the instance of the owner model and the permissions related to the model. The form needed to do this would be quite elaborate and not very useful.

Download

Row Level Permissions are currently hosted in a branch on Django SVN. Please use: svn co http://code.djangoproject.com/svn/django/branches/per-object-permissions to download the current code.