mirror of https://gitlab.crans.org/bde/nk20
Optimize permissions, full support add perms, more fixtures
This commit is contained in:
parent
c653e0986e
commit
f80cb635d3
|
@ -7,7 +7,8 @@ from django.db.models import Q, F
|
|||
|
||||
from note.models import Note, NoteUser, NoteClub, NoteSpecial
|
||||
from note_kfet.middlewares import get_current_session
|
||||
from .models import Membership, RolePermissions, Club
|
||||
from permission.models import Permission
|
||||
from .models import Membership, Club
|
||||
from django.contrib.auth.backends import ModelBackend
|
||||
|
||||
|
||||
|
@ -17,13 +18,16 @@ class PermissionBackend(ModelBackend):
|
|||
supports_inactive_user = False
|
||||
|
||||
@staticmethod
|
||||
def permissions(user):
|
||||
def permissions(user, model, type):
|
||||
for membership in Membership.objects.filter(user=user).all():
|
||||
if not membership.valid() or membership.roles is None:
|
||||
continue
|
||||
|
||||
for role_permissions in RolePermissions.objects.filter(role=membership.roles).all():
|
||||
for permission in role_permissions.permissions.all():
|
||||
for permission in Permission.objects.filter(
|
||||
rolepermissions__role=membership.roles,
|
||||
model__app_label=model.app_label, # For polymorphic models, we don't filter on model type
|
||||
type=type
|
||||
).all():
|
||||
permission = permission.about(
|
||||
user=user,
|
||||
club=membership.club,
|
||||
|
@ -60,10 +64,10 @@ class PermissionBackend(ModelBackend):
|
|||
|
||||
# Never satisfied
|
||||
query = Q(pk=-1)
|
||||
for perm in PermissionBackend.permissions(user):
|
||||
for perm in PermissionBackend.permissions(user, model, t):
|
||||
if perm.field and field != perm.field:
|
||||
continue
|
||||
if perm.model != model or perm.type != t:
|
||||
if perm.type != t or perm.model != model:
|
||||
continue
|
||||
query = query | perm.query
|
||||
return query
|
||||
|
@ -78,7 +82,9 @@ class PermissionBackend(ModelBackend):
|
|||
perm = perm.split('.')[-1].split('_', 2)
|
||||
perm_type = perm[0]
|
||||
perm_field = perm[2] if len(perm) == 3 else None
|
||||
if any(permission.applies(obj, perm_type, perm_field) for permission in self.permissions(user_obj)):
|
||||
ct = ContentType.objects.get_for_model(obj)
|
||||
if any(permission.applies(obj, perm_type, perm_field)
|
||||
for permission in self.permissions(user_obj, ct, perm_type)):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -86,4 +92,5 @@ class PermissionBackend(ModelBackend):
|
|||
return False
|
||||
|
||||
def get_all_permissions(self, user_obj, obj=None):
|
||||
return list(self.permissions(user_obj))
|
||||
ct = ContentType.objects.get_for_model(obj)
|
||||
return list(self.permissions(user_obj, ct, "view"))
|
||||
|
|
|
@ -239,6 +239,186 @@
|
|||
"description": "Update a note balance with a transaction"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 19,
|
||||
"fields": {
|
||||
"model": 35,
|
||||
"query": "[\"OR\", {\"pk\": [\"club\", \"note\", \"pk\"]}, {\"pk__in\": [\"NoteUser\", \"objects\", [\"filter\", {\"user__membership__club\": [\"club\"]}], [\"all\"]]}]",
|
||||
"type": "view",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"description": "View notes of club members"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 20,
|
||||
"fields": {
|
||||
"model": 37,
|
||||
"query": "[\"AND\", [\"OR\", {\"source\": [\"club\", \"note\"]}, {\"destination\": [\"club\", \"note\"]}], {\"amount__lte\": {\"F\": [\"ADD\", [\"F\", \"source__balance\"], 5000]}}]",
|
||||
"type": "add",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"description": "Create transactions with a club"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 21,
|
||||
"fields": {
|
||||
"model": 44,
|
||||
"query": "[\"AND\", {\"destination\": [\"club\", \"note\"]}, {\"amount__lte\": {\"F\": [\"ADD\", [\"F\", \"source__balance\"], 50]}}]",
|
||||
"type": "add",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"description": "Create transactions from buttons with a club"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 22,
|
||||
"fields": {
|
||||
"model": 29,
|
||||
"query": "{\"pk\": [\"club\", \"pk\"]}",
|
||||
"type": "view",
|
||||
"mask": 1,
|
||||
"field": "",
|
||||
"description": "View club infos"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 23,
|
||||
"fields": {
|
||||
"model": 37,
|
||||
"query": "{}",
|
||||
"type": "change",
|
||||
"mask": 1,
|
||||
"field": "valid",
|
||||
"description": "Update validation status of a transaction"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 24,
|
||||
"fields": {
|
||||
"model": 37,
|
||||
"query": "{}",
|
||||
"type": "view",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"description": "View all transactions"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 25,
|
||||
"fields": {
|
||||
"model": 43,
|
||||
"query": "{}",
|
||||
"type": "view",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"description": "Display credit/debit interface"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 26,
|
||||
"fields": {
|
||||
"model": 43,
|
||||
"query": "{}",
|
||||
"type": "add",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"description": "Create credit/debit transaction"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 27,
|
||||
"fields": {
|
||||
"model": 36,
|
||||
"query": "{}",
|
||||
"type": "view",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"description": "View button categories"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 28,
|
||||
"fields": {
|
||||
"model": 36,
|
||||
"query": "{}",
|
||||
"type": "change",
|
||||
"mask": 3,
|
||||
"field": "",
|
||||
"description": "Change button category"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 29,
|
||||
"fields": {
|
||||
"model": 36,
|
||||
"query": "{}",
|
||||
"type": "add",
|
||||
"mask": 3,
|
||||
"field": "",
|
||||
"description": "Add button category"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 30,
|
||||
"fields": {
|
||||
"model": 38,
|
||||
"query": "{}",
|
||||
"type": "view",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"description": "View buttons"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 31,
|
||||
"fields": {
|
||||
"model": 38,
|
||||
"query": "{}",
|
||||
"type": "add",
|
||||
"mask": 3,
|
||||
"field": "",
|
||||
"description": "Add buttons"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 32,
|
||||
"fields": {
|
||||
"model": 38,
|
||||
"query": "{}",
|
||||
"type": "change",
|
||||
"mask": 3,
|
||||
"field": "",
|
||||
"description": "Update buttons"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 33,
|
||||
"fields": {
|
||||
"model": 37,
|
||||
"query": "{}",
|
||||
"type": "add",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"description": "Create any transaction"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "member.role",
|
||||
"pk": 1,
|
||||
|
@ -253,6 +433,48 @@
|
|||
"name": "Adh\u00e9rent Kfet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "member.role",
|
||||
"pk": 3,
|
||||
"fields": {
|
||||
"name": "Pr\u00e9sident\u00b7e BDE"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "member.role",
|
||||
"pk": 4,
|
||||
"fields": {
|
||||
"name": "Tr\u00e9sorier\u00b7\u00e8re BDE"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "member.role",
|
||||
"pk": 5,
|
||||
"fields": {
|
||||
"name": "Respo info"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "member.role",
|
||||
"pk": 6,
|
||||
"fields": {
|
||||
"name": "GC Kfet"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "member.role",
|
||||
"pk": 7,
|
||||
"fields": {
|
||||
"name": "Pr\u00e9sident\u00b7e de club"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "member.role",
|
||||
"pk": 8,
|
||||
"fields": {
|
||||
"name": "Tr\u00e9sorier\u00b7\u00e8re de club"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "member.rolepermissions",
|
||||
"pk": 1,
|
||||
|
@ -295,5 +517,38 @@
|
|||
18
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "member.rolepermissions",
|
||||
"pk": 3,
|
||||
"fields": {
|
||||
"role": 8,
|
||||
"permissions": [
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
22
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "member.rolepermissions",
|
||||
"pk": 4,
|
||||
"fields": {
|
||||
"role": 4,
|
||||
"permissions": [
|
||||
23,
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
27,
|
||||
28,
|
||||
29,
|
||||
30,
|
||||
31,
|
||||
32,
|
||||
33
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
|
@ -8,7 +8,7 @@ import operator
|
|||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from django.db.models import F, Q
|
||||
from django.db.models import F, Q, Model
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
|
@ -33,7 +33,14 @@ class InstancedPermission:
|
|||
|
||||
if self.type == 'add':
|
||||
if permission_type == self.type:
|
||||
return self.query(obj)
|
||||
# Don't increase indexes
|
||||
obj.pk = 0
|
||||
# Force insertion, no data verification, no trigger
|
||||
Model.save(obj, force_insert=True)
|
||||
ret = obj in self.model.model_class().objects.filter(self.query).all()
|
||||
# Delete testing object
|
||||
Model.delete(obj)
|
||||
return ret
|
||||
|
||||
if permission_type == self.type:
|
||||
if self.field and field_name != self.field:
|
||||
|
@ -151,6 +158,10 @@ class Permission(models.Model):
|
|||
field = kwargs[value[0]]
|
||||
for i in range(1, len(value)):
|
||||
if isinstance(value[i], list):
|
||||
if value[i][0] in kwargs:
|
||||
field = Permission.compute_param(value[i], **kwargs)
|
||||
continue
|
||||
|
||||
field = getattr(field, value[i][0])
|
||||
params = []
|
||||
call_kwargs = {}
|
||||
|
@ -168,9 +179,6 @@ class Permission(models.Model):
|
|||
return field
|
||||
|
||||
def _about(self, query, **kwargs):
|
||||
if self.type == 'add':
|
||||
# Handle add permission differently
|
||||
return self._about_add(query, **kwargs)
|
||||
if len(query) == 0:
|
||||
# The query is either [] or {} and
|
||||
# applies to all objects of the model
|
||||
|
@ -200,48 +208,6 @@ class Permission(models.Model):
|
|||
# TODO: find a better way to crash here
|
||||
raise Exception("query {} is wrong".format(self.query))
|
||||
|
||||
def _about_add(self, _query, **kwargs):
|
||||
query = _query
|
||||
if len(query) == 0:
|
||||
return lambda _: True
|
||||
if isinstance(query, list):
|
||||
if query[0] == 'AND':
|
||||
return lambda obj: functools.reduce(operator.and_, [self._about_add(q, **kwargs)(obj) for q in query[1:]])
|
||||
elif query[0] == 'OR':
|
||||
return lambda obj: functools.reduce(operator.or_, [self._about_add(q, **kwargs)(obj) for q in query[1:]])
|
||||
elif query[0] == 'NOT':
|
||||
return lambda obj: not self._about_add(query[1], **kwargs)(obj)
|
||||
elif isinstance(query, dict):
|
||||
q_kwargs = {}
|
||||
for key in query:
|
||||
value = query[key]
|
||||
if isinstance(value, list):
|
||||
# It is a parameter we query its primary key
|
||||
q_kwargs[key] = Permission.compute_param(value, **kwargs)
|
||||
elif isinstance(value, dict):
|
||||
# It is an F object
|
||||
q_kwargs[key] = Permission.compute_f(query['F'], **kwargs)
|
||||
else:
|
||||
q_kwargs[key] = value
|
||||
def func(obj):
|
||||
nonlocal q_kwargs
|
||||
for arg in q_kwargs:
|
||||
spl = arg.split('__')
|
||||
value = obj
|
||||
last = None
|
||||
for s in spl:
|
||||
if not hasattr(obj, s):
|
||||
last = s
|
||||
break
|
||||
value = getattr(obj, s)
|
||||
if last == "lte": # TODO Add more filters
|
||||
if value > q_kwargs[arg]:
|
||||
return False
|
||||
elif value != q_kwargs[arg]:
|
||||
return False
|
||||
return True
|
||||
return func
|
||||
|
||||
def about(self, **kwargs):
|
||||
"""
|
||||
Return an InstancedPermission with the parameters
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.db.models.signals import pre_save, pre_delete, post_save, post_delete
|
||||
|
||||
from logs import signals as logs_signals
|
||||
from member.backends import PermissionBackend
|
||||
from note_kfet.middlewares import get_current_authenticated_user
|
||||
|
||||
|
@ -39,20 +41,46 @@ def pre_save_object(sender, instance, **kwargs):
|
|||
model_name = model_name_full[1]
|
||||
|
||||
if qs.exists():
|
||||
# We check if the user can change the model
|
||||
|
||||
# If the user has all right on a model, then OK
|
||||
if PermissionBackend().has_perm(user, app_label + ".change_" + model_name, instance):
|
||||
return
|
||||
|
||||
# In the other case, we check if he/she has the right to change one field
|
||||
previous = qs.get()
|
||||
for field in instance._meta.fields:
|
||||
field_name = field.name
|
||||
old_value = getattr(previous, field.name)
|
||||
new_value = getattr(instance, field.name)
|
||||
# If the field wasn't modified, no need to check the permissions
|
||||
if old_value == new_value:
|
||||
continue
|
||||
if not PermissionBackend().has_perm(user, app_label + ".change_" + model_name + "_" + field_name, instance):
|
||||
raise PermissionDenied
|
||||
else:
|
||||
if not PermissionBackend().has_perm(user, app_label + ".add_" + model_name, instance):
|
||||
# We check if the user can add the model
|
||||
|
||||
# While checking permissions, the object will be inserted in the DB, then removed.
|
||||
# We disable temporary the connectors
|
||||
pre_save.disconnect(pre_save_object)
|
||||
pre_delete.disconnect(pre_delete_object)
|
||||
# We disable also logs connectors
|
||||
pre_save.disconnect(logs_signals.pre_save_object)
|
||||
post_save.disconnect(logs_signals.save_object)
|
||||
post_delete.disconnect(logs_signals.delete_object)
|
||||
|
||||
# We check if the user has right to add the object
|
||||
has_perm = PermissionBackend().has_perm(user, app_label + ".add_" + model_name, instance)
|
||||
|
||||
# Then we reconnect all
|
||||
pre_save.connect(pre_save_object)
|
||||
pre_delete.connect(pre_delete_object)
|
||||
pre_save.connect(logs_signals.pre_save_object)
|
||||
post_save.connect(logs_signals.save_object)
|
||||
post_delete.connect(logs_signals.delete_object)
|
||||
|
||||
if not has_perm:
|
||||
raise PermissionDenied
|
||||
|
||||
|
||||
|
@ -73,5 +101,6 @@ def pre_delete_object(sender, instance, **kwargs):
|
|||
app_label = model_name_full[0]
|
||||
model_name = model_name_full[1]
|
||||
|
||||
# We check if the user has rights to delete the object
|
||||
if not PermissionBackend().has_perm(user, app_label + ".delete_" + model_name, instance):
|
||||
raise PermissionDenied
|
||||
|
|
Loading…
Reference in New Issue