field_permissions.py 3.98 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
# -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
# se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics.
#
# Copyright © 2017  Gabriel Détraz
# Copyright © 2017  Goulven Kermarec
# Copyright © 2017  Augustin Lemesle
# Copyright © 2018  Maël Kervella
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""re2o.field_permissions
A model mixin and a field mixin used to remove some unauthorized fields
from the form automatically generated from the model. The model must
subclass `FieldPermissionModelMixin` and the form must subclass
`FieldPermissionFieldMixin` so when a Django form is generated from the
fields of the models, some fields will be removed if the user don't have
the rights to change them (can_change_{name})
"""
32 33

class FieldPermissionModelMixin:
34
    """ The model mixin. Defines the `has_field_perm` function """
35 36 37 38 39 40
    field_permissions = {}  # {'field_name': callable}
    FIELD_PERM_CODENAME = 'can_change_{model}_{name}'
    FIELD_PERMISSION_GETTER = 'can_change_{name}'
    FIELD_PERMISSION_MISSING_DEFAULT = True

    def has_field_perm(self, user, field):
41 42
        """ Checks if a `user` has the right to edit the `field`
        of this model """
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
        if field in self.field_permissions:
            checks = self.field_permissions[field]
            if not isinstance(checks, (list, tuple)):
                checks = [checks]

        else:
            checks = []

            # Consult the optional field-specific hook.
            getter_name = self.FIELD_PERMISSION_GETTER.format(name=field)
            if hasattr(self, getter_name):
                checks.append(getattr(self, getter_name))

            # Try to find a static permission for the field
            else:
                perm_label = self.FIELD_PERM_CODENAME.format(**{
                    'model': self._meta.model_name,
                    'name': field,
                })
                if perm_label in dict(self._meta.permissions):
                    checks.append(perm_label)

        # No requirements means no restrictions.
        if not len(checks):
            return self.FIELD_PERMISSION_MISSING_DEFAULT

        # Try to find a user setting that qualifies them for permission.
        for perm in checks:
            if callable(perm):
72
                result, _reason = perm(user_request=user)
73 74 75
                if result is not None:
                    return result
            else:
Maël Kervella's avatar
Maël Kervella committed
76 77
                # Don't supply 'obj', or else infinite recursion.
                result = user.has_perm(perm)
78 79 80 81 82 83
                if result:
                    return True

        # If no requirement can be met, then permission is denied.
        return False

Maël Kervella's avatar
Maël Kervella committed
84

85 86
class FieldPermissionFormMixin:
    """
87
    Construit le formulaire et retire les champs interdits
88 89 90 91 92
    """
    def __init__(self, *args, **kwargs):
        user = kwargs.pop('user')

        super(FieldPermissionFormMixin, self).__init__(*args, **kwargs)
93
        to_be_deleted = []
94 95
        for name in self.fields:
            if not self.instance.has_field_perm(user, field=name):
96 97 98
                to_be_deleted.append(name)
        for name in to_be_deleted:
            self.remove_unauthorized_field(name)
99 100

    def remove_unauthorized_field(self, name):
101
        """ Remove one field from the fields of the form """
102
        del self.fields[name]