diff options
Diffstat (limited to 'ishtar_common')
-rw-r--r-- | ishtar_common/admin.py | 3 | ||||
-rw-r--r-- | ishtar_common/models.py | 154 | ||||
-rw-r--r-- | ishtar_common/models_common.py | 7 | ||||
-rw-r--r-- | ishtar_common/utils.py | 14 | ||||
-rw-r--r-- | ishtar_common/views_item.py | 1 | ||||
-rw-r--r-- | ishtar_common/wizards.py | 36 |
6 files changed, 168 insertions, 47 deletions
diff --git a/ishtar_common/admin.py b/ishtar_common/admin.py index f67a99e01..743d643a3 100644 --- a/ishtar_common/admin.py +++ b/ishtar_common/admin.py @@ -1655,6 +1655,9 @@ class ProfileTypeAdmin(GeneralTypeAdmin): return Http404() permissions_needed = set() permissions_not_needed = set() + for model in ("basefind", "import", "biographicalnote"): + for perm_type in ("add", "change", "delete", "view"): + permissions_not_needed.add((perm_type, model)) for group in obj.groups.all(): for perm in group.permissions.all(): sp = perm.codename.split("_") diff --git a/ishtar_common/models.py b/ishtar_common/models.py index 8fcb9edbb..0c92f2f10 100644 --- a/ishtar_common/models.py +++ b/ishtar_common/models.py @@ -27,6 +27,7 @@ from bs4 import BeautifulSoup import bleach import copy import datetime +from guardian.models import UserObjectPermission import inspect from importlib import import_module from jinja2 import TemplateSyntaxError, UndefinedError @@ -297,7 +298,7 @@ class ValueGetter: def _get_values_update_sub_filter(self, filtr, prefix): if not filtr: return - return [k[len(prefix) :] for k in filtr if k.startswith(prefix)] + return [k[len(prefix):] for k in filtr if k.startswith(prefix)] def get_extra_values(self, prefix="", no_values=False, filtr=None, **kwargs): return {} @@ -3540,6 +3541,81 @@ class UserProfile(models.Model): new_item.external_sources.add(src) return new_item + def _generate_permission(self, ishtar_user, content_type, permission_request): + item_ids = [] + model_class = content_type.model_class() + if permission_request.include_associated_items: + item_ids += model_class.filter( + ishtar_users__pk=ishtar_user.pk + ).values_list("pk", flat=True) + if permission_request.include_upstream_items: + # TODO.... + item_ids += model_class.get_ids_from_upper_permissions(ishtar_user.user_ptr.pk) + if permission_request.request or permission_request.limit_to_attached_areas: + # TODO + pass + query = model_class.objects + return item_ids + + def generate_permission(self, content_type): + ishtar_user = self.person.ishtaruser + + # add base permissions + for group in self.profile_type.groups.all(): + for perm in group.permissions.all(): + ishtar_user.user_ptr.user_permissions.add(perm) + q_has_perm = self.profile_type.groups.filter( + permissions__content_type=content_type, + permissions__codename__contains="_own_" + ) + if not q_has_perm.count(): # no permission to generate + return + + permissions = [] + for group in q_has_perm.all(): + permissions += list(group.permissions.values_list("pk", flat=True)) + q_req = self.profile_type.permission_requests.filter( + model=content_type, active=True + ) + item_ids = [] + if not q_req.count(): + # TODO v5: delete old behaviour + """ + print(f"WARNING: no permission request for content {content_type.name} and profile {self}") + print("Using old behaviour") + """ + model_class = content_type.model_class() + query = model_class.get_owns(user=ishtar_user, query=True, no_auth_check=True) + if query: + item_ids = list( + model_class.objects.filter(query).values_list("pk", flat=True) + ) + else: + for perm_request in q_req.all(): + item_ids += self._generate_permission( + ishtar_user, content_type, perm_request + ) + user_id = ishtar_user.user_ptr.pk + object_permissions = [] + item_ids = list(set(item_ids)) + permissions = list(set(permissions)) + for permission_id in permissions: + exclude = list(UserObjectPermission.objects.filter( + content_type_id=content_type.pk, permission_id=permission_id, + user_id=user_id + ).values_list("object_pk", flat=True)) + object_permissions += [ + UserObjectPermission( + object_pk=str(item_id), + content_type_id=content_type.pk, + permission_id=permission_id, + user_id=user_id + ) + for item_id in item_ids if str(item_id) not in exclude + ] + if object_permissions: + UserObjectPermission.objects.bulk_create(object_permissions) + def save( self, force_insert=False, @@ -3692,8 +3768,8 @@ class IshtarUser(FullSearch): _("Advanced shortcut menu"), default=False ) # latest news read by the user - latest_news_version = models.CharField(_("Latest news version"), default="", blank=True, - max_length=20) + latest_news_version = models.CharField(_("Latest news version"), default="", + blank=True, max_length=20) display_news = models.BooleanField(_("Display news"), default=True) display_forum_entries = models.BooleanField(_("Display forum entries"), default=True) @@ -3787,32 +3863,43 @@ class IshtarUser(FullSearch): if obj: return self.user_ptr.has_perm(permission, obj) return self.user_ptr.has_perm(permission) - """ - res = ( - bool( - self.profiles.filter( - current=True, profile_type__txt_idx=permission - ).count() - ) - or bool( - self.profiles.filter( - current=True, - profile_type__groups__permissions__codename=permission, - ).count() - ) - or bool( - self.ishtaruser.user_ptr.groups.filter( - permissions__codename__in=[permission] - ).count() - ) - or bool( - self.ishtaruser.user_ptr.user_permissions.filter( - codename__in=[permission] - ).count() - ) - ) - return res - """ + + def generate_permission(self): + # models to treat first in this order to manage cascade permissions + model_names = [ + ("archaeological_operations", "operation"), + ("archaeological_context_records", "contextrecord"), + ("archaeological_warehouse", "warehouse"), + ("archaeological_finds", "treatment"), + ("archaeological_warehouse", "container"), + ("archaeological_finds", "find"), + ] + # cascade permission to treat at the end + last_model_names = [ + ("ishtar_common", "document"), + ("ishtar_common", "geovectordata"), + ("ishtar_common", "import"), + ] + for ct in ContentType.objects.all(): + name = (ct.app_label, ct.model) + klass = ct.model_class() + if not klass or not getattr(klass, "SHOW_URL", None) \ + or name in model_names \ + or name in last_model_names: + continue + model_names.append(name) + model_names += last_model_names + content_types = [ + ContentType.objects.get(app_label=app, model=model_name) + for app, model_name in model_names + ] + # clean all permission first + UserObjectPermission.objects.filter(user_id=self.pk).delete() + self.user_ptr.user_permissions.clear() + + for ct in content_types: + for profile in self.person.profiles.all(): + profile.generate_permission(ct) def full_label(self): return self.person.full_label() @@ -5014,7 +5101,14 @@ class Document( def get_query_owns(cls, ishtaruser): query_own_list = [] for rel_model in cls.RELATED_MODELS: - klass = getattr(cls, rel_model).remote_field.related_model + r = getattr(cls, rel_model) + if hasattr(r, "remote_field"): + r = getattr(cls, rel_model).remote_field + else: + r = getattr(cls, rel_model).rel + klass = r.related_model + if not hasattr(klass, "_get_query_owns_dicts"): + continue q_own_dct = klass._get_query_owns_dicts(ishtaruser) if q_own_dct: query_own_list.append((rel_model + "__", q_own_dct)) diff --git a/ishtar_common/models_common.py b/ishtar_common/models_common.py index e92d7d55d..28f5aba00 100644 --- a/ishtar_common/models_common.py +++ b/ishtar_common/models_common.py @@ -1629,13 +1629,14 @@ class BaseHistorizedItem( ) } - class Meta: - abstract = True - @classmethod def get_verbose_name(cls): return cls._meta.verbose_name + @classmethod + def get_ids_from_upper_permissions(cls, user_id): + return [] + def is_locked(self, user=None): if not user: return self.locked diff --git a/ishtar_common/utils.py b/ishtar_common/utils.py index 8de745874..09c470823 100644 --- a/ishtar_common/utils.py +++ b/ishtar_common/utils.py @@ -395,7 +395,8 @@ class OwnPerms: """ Check if the current object is owned by the user """ - print("ishtar_common/utils.py - 370 - DELETE") + print("ishtar_common/utils.py - 377 - DELETE") + raise IshtarUser = apps.get_model("ishtar_common", "IshtarUser") if isinstance(user, IshtarUser): ishtaruser = user @@ -467,13 +468,18 @@ class OwnPerms: values=None, get_short_menu_class=False, menu_filtr=None, + no_auth_check=False, + query=False ): """ Get Own items """ + return_query = query + query = None if not replace_query: replace_query = {} - if hasattr(user, "is_authenticated") and not user.is_authenticated: + if hasattr(user, "is_authenticated") and not user.is_authenticated \ + and not no_auth_check: returned = cls.objects.filter(pk__isnull=True) if values: returned = [] @@ -502,6 +508,10 @@ class OwnPerms: if values: returned = [] return returned + if return_query: + if query: + return query + return replace_query if query: q = cls.objects.filter(query) else: # replace_query diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py index a88cb48b3..64dee8872 100644 --- a/ishtar_common/views_item.py +++ b/ishtar_common/views_item.py @@ -12,7 +12,6 @@ import requests # nosec: no user input used import subprocess # nosec from tempfile import NamedTemporaryFile -import unidecode from django.conf import settings from django.contrib.auth.models import Permission diff --git a/ishtar_common/wizards.py b/ishtar_common/wizards.py index e41c4c811..1da9b9f4d 100644 --- a/ishtar_common/wizards.py +++ b/ishtar_common/wizards.py @@ -35,7 +35,7 @@ from formtools.wizard.views import ( from django.contrib.sites.models import Site from django.core.exceptions import ObjectDoesNotExist from django.core.files.images import ImageFile -from django.core.files.storage import default_storage, FileSystemStorage +from django.core.files.storage import FileSystemStorage from django.core.mail import send_mail from django.db.models.fields.files import FileField, ImageFieldFile from django.db.models.fields.related import ManyToManyField @@ -50,7 +50,7 @@ from django.utils.safestring import mark_safe from ishtar_common import models, models_rest from ishtar_common.forms import CustomForm, reverse_lazy -from ishtar_common.utils import get_all_field_names, get_person_gdpr_log, MultiValueDict, \ +from ishtar_common.utils import get_all_field_names, get_person_gdpr_log, MultiValueDict,\ put_session_message logger = logging.getLogger(__name__) @@ -154,6 +154,7 @@ class Wizard(IshtarWizard): label = "" translated_keys = [] modification = None # True when the wizard modify an item + deletion = True # True on deletion storage_name = "formtools.wizard.storage.session.SessionStorage" wizard_done_template = "ishtar/wizard/wizard_done.html" wizard_done_window = "" @@ -211,16 +212,9 @@ class Wizard(IshtarWizard): self.steps = StepsHelper(self) current_object = self.get_current_object() - ishtaruser = ( - request.user.ishtaruser if hasattr(request.user, "ishtaruser") else None - ) - # not the first step and current object is not owned if self.steps and self.steps.first != step and current_object: - is_own = current_object.is_own( - ishtaruser, alt_query_own=self.alt_is_own_method - ) - if not is_own: + if not self.verify_permission(request, current_object): messages.add_message( request, messages.WARNING, @@ -230,6 +224,23 @@ class Wizard(IshtarWizard): return return True + def verify_permission(self, request, current_object=None): + meta = self.model._meta + perm = f"{meta.app_label}." + if self.modification: + perm += "change" + elif self.deletion: + perm += "delete" + else: + perm += "add" + base_perm = f"{perm}_{meta.model_name}" + if request.user.has_perm(base_perm): + return True + if not current_object: + return False + own_perm = f"{perm}_own_{meta.model_name}" + return request.user.has_perm(own_perm, current_object) + def dispatch(self, request, *args, **kwargs): self.current_right = kwargs.get("current_right", None) step = kwargs.get("step", None) @@ -241,7 +252,6 @@ class Wizard(IshtarWizard): self.filter_owns_items = True else: self.filter_owns_items = False - return super(Wizard, self).dispatch(request, *args, **kwargs) def get_prefix(self, request, *args, **kwargs): @@ -1714,6 +1724,8 @@ class DocumentSearch(SearchWizard): class DeletionWizard(Wizard): + deletion = True + def __init__(self, *args, **kwargs): if (not hasattr(self, "fields") or not self.fields) and ( hasattr(self, "model") and hasattr(self.model, "TABLE_COLS") @@ -1790,6 +1802,8 @@ class MultipleItemWizard(Wizard): class MultipleDeletionWizard(MultipleItemWizard): + deletion = True + def __init__(self, *args, **kwargs): if (not hasattr(self, "fields") or not self.fields) and ( hasattr(self, "model") and hasattr(self.model, "TABLE_COLS") |