Changes between Version 38 and Version 39 of RowLevelPermissions


Ignore:
Timestamp:
Jun 19, 2009, 6:45:38 PM (15 years ago)
Author:
James Bennett
Comment:

The branch is dead and people need to use the admin's features.

Legend:

Unmodified
Added
Removed
Modified
  • RowLevelPermissions

    v38 v39  
    1 = Row-level permissions =
     1A couple years ago, a branch in Django's repository was created to explore implementing finer-grained permissions (at the level of individual objects, rather than at the level of model classes). That branch is long since dead; it has not seen a commit in over two years, and likely will never be updated or integrated into Django in any meaningful way.
    22
    3 == Introduction ==
     3If you need finer-grained permissions in your own applications, it should be noted that Django's administrative application supports this, via the following methods which can be overridden on subclasses of `ModelAdmin`. Note that all of these methods receive the current `HttpRequest` object as an argument, allowing for customization based on the specific authenticated user:
    44
    5 === What are row-level permissions? ===
     5 * `queryset(self, request)`: Should return a `QuerySet` for use in the admin's list of objects for a model. Objects not present in this `QuerySet` will not be shown.
     6 * `has_add_permission(self, request)`: Should return `True` if adding an object is permitted, `False` otherwise.
     7 * `has_change_permission(self, request, obj=None`): Should return `True` if editing `obj` is permitted, `False` otherwise. If `obj` is `None`, should return `True` or `False` to indicate whether editing of objects of this type is permitted in general (e.g., if `False` will be interpreted as meaning that the current user is not permitted to edit any object of this type).
     8 * `has_delete_permission(self, request, obj=None`): Should return `True` if deleting `obj` is permitted, `False` otherwise. If `obj` is `None`, should return `True` or `False` to indicate whether deleting objects of this type is permitted in general (e.g., if `False` will be interpreted as meaning that the current user is not permitted to delete any object of this type).
    69
    7 Some examples of row-level permissions: "User A has read-access to article 234" or "User D has read, write access to article 234."
    8 
    9 === Why do we need this? ===
    10 
    11 With the current permission system, a user of Django's admin interface is capable either of editing *all* objects of a certain type or editing *none* of the objects of a certain type. After implementing a row level permission, the user can be capable of editing only certain objects of that type -- e.g., just the objects he created himself.
    12 
    13 === Status in Django ===
    14 
    15 Row-level permissions are being implemented in a branch -- the [http://code.djangoproject.com/browser/django/branches/per-object-permissions per-object-permissions branch]. This branch needs your help in testing. To get the code, use this Subversion command:
    16 
    17 {{{
    18 svn co http://code.djangoproject.com/svn/django/branches/per-object-permissions
    19 }}}
    20 
    21 == Using row-level permissions ==
    22 
    23 === The basics ===
    24 
    25 There are a few things you need to know about row-level permissions before working with them:
    26 
    27  * Row-level permissions use the permissions table to determine an object's possible permissions. You need to create permissions in the permissions table before using them in row-level permissions.
    28  * Row-level permissions can be negative. This is determined by an attribute called "negative."
    29  * Django checks permissions in the following order:
    30 
    31    * User row-level permission
    32    * Group row-level permission
    33    * User model-level permission
    34    * Group model-level permission
    35 
    36 Permission checking stops either at the first positive or negative. If no permission is found, it will return a negative (or false).
    37 
    38 === Enabling row-level permissions ===
    39 
    40 To enable row-level permissions for a model, add a {{{row_level_permissions = True}}} to the model's {{{class Meta}}}. By default, row-level permissions are disabled.
    41 
    42 For example:
    43 
    44 {{{
    45 #!python
    46 class Mineral(models.Model):
    47     name = models.CharField(maxlength=150)
    48     hardness = models.PositiveSmallIntegerField()
    49    
    50     class Admin:
    51         pass
    52 
    53     class Meta:
    54         row_level_permissions = True
    55 
    56     def __str__(self):
    57         return self.name
    58 }}}
    59 
    60 The {{{class Admin}}} is not required, but its presence does create the default add/change/delete model-level permissions, which are useful to have.
    61 
    62 === Creating a row-level permission ===
    63 
    64 Django has two helper methods for use in creating row-level permissions. They both live on the {{{RowLevelPermission}}} model manager (i.e., {{{RowLevelPermission.objects}}}).
    65 
    66 The first helper function is {{{create_row_level_permission}}}:
    67 
    68 {{{
    69 #!python
    70 def create_row_level_permission(self, object_instance, owner_instance, permission, negative=False):
    71    # ...
    72 }}}
    73 
    74 The permission parameter can either be the codename of the permission or a permission instance. The negative parameter is optional and defaults to {{{False}}}. You must pass an instance of the object and owner to this method.
    75 
    76 The second helper function is {{{create_default_row_permissions}}}:
    77 
    78 {{{
    79 #!python
    80 def create_default_row_level_permissions(self, object_instance, owner_instance, change=True, delete=True, negChange=False, negDel=False):
    81    # ...
    82 }}}
    83 
    84 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.
    85 
    86 For example, this creates a change row-level permission on the {{{quartz}}} object:
    87 
    88 {{{
    89 #!python
    90 RowLevelPermission.objects.create_default_row_level_permissions(quartz, user, delete=False)
    91 }}}
    92 
    93 === Checking permissions ===
    94 
    95 To check whether a user has permission to edit an object, use the {{{has_perm()}}} method on the {{{User}}} object:
    96 
    97 {{{
    98 #!python
    99 user.has_perm("mine.can_mine", object=mineral)
    100 }}}
    101 
    102 The {{{object}}} parameter is optional for backwards-compatibility. If you don't want to check row-level permissions, exclude the {{{object}}} parameter.
    103 
    104 {{{has_perm()}}} returns either {{{True}}} or {{{False}}}. It returns {{{False}}} if the user has a negative row-level permission on the object. It also checks 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 permission over the negative.
    105 
    106 Note that the GenericAuthorization branch (yet another of Django's many current branches) implements a different way of checking permissions. See the GenericAuthorization page for how row-level permissions fit in with that scheme.
    107 
    108 ==== Has row-level permission ====
    109 
    110 The {{{User}}} method {{{contains_permission()}}} checks whether a user has the given permission on a model -- not an *instance* of a model but the model class itself. It returns {{{True}}} if *any* row-level permission exists for the given user for the given model. (This is used in the admin interface to determine if the change list should be displayed for a user.)
    111 
    112 For example, the following would return {{{True}}} if {{{some_user}}} had change permission on article 234:
    113 {{{
    114 #!python
    115 some_user.contains_permission("mine.change_mineral", Mineral)
    116 }}}
    117 It will return True.
    118 
    119 The name {{{contains_permission}}} is still up for debate, as it's not perfect. If you have any ideas, please leave them here by editing this page, or leave a message on the django-developers mailing list.
    120 
    121 === Checking permissions in a template ===
    122 
    123 Use the template tag {{{if_has_perm}}} to check permission in a template. The tag has the following syntax:
    124 
    125 {{{
    126 {% load auth %}
    127 {% if_has_perm [not] (permission codename) [object] %}
    128 ...
    129 {% else %}
    130 ...
    131 {% end_if_has_perm %}
    132 }}}
    133 
    134 The parameters in square brackets are optional and the normal brackets are required. The else statement is optional. The permission codename should be in the format: app_label.codename.
    135 
    136 For example:
    137 
    138 {{{
    139 {% load auth %}
    140 {% if_has_perm mine.change_mineral obj %}
    141 ...
    142 {% else %}
    143 ...
    144 {% end_if_has_perm %}
    145 }}}
    146 
    147 === Administration ===
    148 
    149 You can configure row-level permissions to be created automatically by the admin interface when a user creates an object by using the {{{class Admin}}} options {{{grant_change_row_level_perm}}} and {{{grant_delete_row_level_perm}}}. These are {{{False}}} by default.
    150 
    151 For example:
    152 
    153 {{{
    154 #!python
    155 class Mineral(models.Model):
    156     # ...
    157     class Admin:
    158         grant_change_row_level_perm = True
    159         grant_delete_row_level_perm = True
    160    
    161     class Meta:
    162         row_level_permissions = True
    163 }}}
    164 
    165 Row-level permissions can be edited in the admin interface. On the change form for each object with row-level permissions enabled is a link beside History to edit row-level permissions. To edit row-level permissions, you must have the "change RLP" permission ''and'' change permission on the object. To add row-level permissions, you must have the "add RLP" permisison ''and'' change permission on the object.
    166 
    167 By default, all instances of a model are shown to a user when they access the change list for a model. To turn off this behavior, set the {{{show_all_rows}}} {{{class Admin}}} option to {{{False}}}. Doing this will increase the number of database queries made to the server, which is why the default option is that all rows are shown. Example:
    168 
    169 {{{
    170 #!python
    171 class Mineral(models.Model):
    172     # ...
    173     class Admin:
    174         show_all_rows = False
    175    
    176     class Meta:
    177         row_level_permissions = True
    178 }}}
    179 
    180 === Accessing row-level permissions from a model ===
    181 
    182 Use the {{{row_level_permissions}}} attribute on a model object to retrieve 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}}}:
    183 
    184 {{{
    185 #!python
    186 rlp_list = quartz.row_level_permissions.all()
    187 }}}
    188 
    189 === Accessing the owner and model of a row-level permission ===
    190 
    191 To return the owner of a row-level permission, use the {{{owner}}} attribute. For example:
    192 
    193 {{{
    194 #!python
    195 user = row_level_permission.owner
    196 }}}
    197 
    198 To return the instance of a row-level permission, use the {{{model}}} attribute. For example:
    199 
    200 {{{
    201 #!python
    202 object = row_level_permission.model
    203 }}}
    204 
    205 == Implementation notes ==
    206 
    207 Please see RowLevelPermissionsDeveloper for more information on how row-level permissions are implemented.
    208 
    209 == Known bugs ==
    210 
    211  * Connecting more then one "owner" to the RLP model causes a many-to-many conflict. This is a bug with the generic relation code. The workaround for now is to rename the {{{related_name}}} for each different owner. (See ticket #2573 for the bug with generic relations.)
    212  * Row-level permission objects cannot have row-level permissions enabled.
    213  * The error message in the admin interface is displayed with a checkmark, not an error icon.
    214 
    215 == Contact ==
    216 
    217 If there are any problems, please contact Chris, the branch maintainer, at indirecthit [at] gmail.com.
     10For public-facing (i.e., non-admin) views, you are of course free to implement whatever form of permission-checking logic your application requires.
Back to Top