Code

Changes between Version 38 and Version 39 of RowLevelPermissions


Ignore:
Timestamp:
06/19/09 16:45:38 (5 years ago)
Author:
ubernostrum
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.