From 751147f254e07c54d9a9e32da001d1ecb74f61fa Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Fri, 10 Apr 2020 00:02:22 +0200 Subject: [PATCH] Don't display a note that we can't see, fix CI, fix distinct fields on PostgresSQL DB --- apps/activity/models.py | 4 ++-- apps/activity/views.py | 19 +++++++++++++------ apps/note/api/serializers.py | 18 ++++++++++++------ apps/note/api/urls.py | 2 +- apps/note/api/views.py | 3 ++- apps/permission/backends.py | 20 ++++++++++++++------ apps/treasury/views.py | 6 +++--- static/js/base.js | 2 +- 8 files changed, 48 insertions(+), 26 deletions(-) diff --git a/apps/activity/models.py b/apps/activity/models.py index 29f04b39..cab229c4 100644 --- a/apps/activity/models.py +++ b/apps/activity/models.py @@ -139,7 +139,7 @@ class Entry(models.Model): verbose_name = _("entry") verbose_name_plural = _("entries") - def save(self, *args,**kwargs): + def save(self, *args, **kwargs): qs = Entry.objects.filter(~Q(pk=self.pk), activity=self.activity, note=self.note, guest=self.guest) if qs.exists(): @@ -153,7 +153,7 @@ class Entry(models.Model): if self.note.balance < 0: raise ValidationError(_("The balance is negative.")) - ret = super().save(*args,**kwargs) + ret = super().save(*args, **kwargs) if insert and self.guest: GuestTransaction.objects.create( diff --git a/apps/activity/views.py b/apps/activity/views.py index 14746929..c0ae6a4e 100644 --- a/apps/activity/views.py +++ b/apps/activity/views.py @@ -3,6 +3,7 @@ from datetime import datetime, timezone +from django.conf import settings from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.contenttypes.models import ContentType from django.db.models import F, Q @@ -45,8 +46,8 @@ class ActivityListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView context['title'] = _("Activities") upcoming_activities = Activity.objects.filter(date_end__gt=datetime.now()) - context['upcoming'] = ActivityTable(data=upcoming_activities - .filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))) + context['upcoming'] = ActivityTable(data=upcoming_activities.filter( + PermissionBackend.filter_queryset(self.request.user, Activity, "view"))) return context @@ -138,8 +139,14 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView): | Q(note__noteuser__user__last_name__regex=pattern) | Q(name__regex=pattern) | Q(normalized_name__regex=Alias.normalize(pattern)))) \ - .filter(PermissionBackend.filter_queryset(self.request.user, Alias, "view"))\ - .distinct()[:20] + .filter(PermissionBackend.filter_queryset(self.request.user, Alias, "view")) + if settings.DATABASES[note_qs.db]["ENGINE"] == 'django.db.backends.postgresql_psycopg2': + note_qs = note_qs.distinct('note__pk')[:20] + else: + # SQLite doesn't support distinct fields. For compatibility reason (in dev mode), the note list will only + # have distinct aliases rather than distinct notes with a SQLite DB, but it can fill the result page. + # In production mode, please use PostgreSQL. + note_qs = note_qs.distinct()[:20] for note in note_qs: note.type = "Adhérent" note.activity = activity @@ -153,9 +160,9 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView): context["title"] = _('Entry for activity "{}"').format(activity.name) context["noteuser_ctype"] = ContentType.objects.get_for_model(NoteUser).pk context["notespecial_ctype"] = ContentType.objects.get_for_model(NoteSpecial).pk - + context["activities_open"] = Activity.objects.filter(open=True).filter( PermissionBackend.filter_queryset(self.request.user, Activity, "view")).filter( PermissionBackend.filter_queryset(self.request.user, Activity, "change")).all() - return context \ No newline at end of file + return context diff --git a/apps/note/api/serializers.py b/apps/note/api/serializers.py index 7005ce16..ce2ed97e 100644 --- a/apps/note/api/serializers.py +++ b/apps/note/api/serializers.py @@ -3,6 +3,8 @@ from rest_framework import serializers from rest_polymorphic.serializers import PolymorphicSerializer +from note_kfet.middlewares import get_current_authenticated_user +from permission.backends import PermissionBackend from ..models.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction, TemplateCategory, \ @@ -96,20 +98,24 @@ class NotePolymorphicSerializer(PolymorphicSerializer): class Meta: model = Note + class ConsumerSerializer(serializers.ModelSerializer): """ REST API Nested Serializer for Consumers. - return Alias, and the note Associated to it in + return Alias, and the note Associated to it in """ - note = NotePolymorphicSerializer() + note = serializers.SerializerMethodField() + class Meta: model = Alias fields = '__all__' - @staticmethod - def setup_eager_loading(queryset): - queryset = queryset.select_related('note') - + def get_note(self, obj): + if PermissionBackend.check_perm(get_current_authenticated_user(), "note.view_note", obj.note): + print(obj.pk) + return NotePolymorphicSerializer().to_representation(obj.note) + return dict(id=obj.id) + class TemplateCategorySerializer(serializers.ModelSerializer): """ diff --git a/apps/note/api/urls.py b/apps/note/api/urls.py index 57909080..2f3fd1a9 100644 --- a/apps/note/api/urls.py +++ b/apps/note/api/urls.py @@ -12,7 +12,7 @@ def register_note_urls(router, path): router.register(path + '/note', NotePolymorphicViewSet) router.register(path + '/alias', AliasViewSet) router.register(path + '/consumer', ConsumerViewSet) - + router.register(path + '/transaction/category', TemplateCategoryViewSet) router.register(path + '/transaction/transaction', TransactionViewSet) router.register(path + '/transaction/template', TransactionTemplateViewSet) diff --git a/apps/note/api/views.py b/apps/note/api/views.py index 61ef19eb..d5ad8129 100644 --- a/apps/note/api/views.py +++ b/apps/note/api/views.py @@ -89,6 +89,7 @@ class AliasViewSet(ReadProtectedModelViewSet): return queryset + class ConsumerViewSet(ReadOnlyProtectedModelViewSet): queryset = Alias.objects.all() serializer_class = ConsumerSerializer @@ -111,7 +112,7 @@ class ConsumerViewSet(ReadOnlyProtectedModelViewSet): | Q(normalized_name__regex="^" + alias.lower())) return queryset - + class TemplateCategoryViewSet(ReadProtectedModelViewSet): """ diff --git a/apps/permission/backends.py b/apps/permission/backends.py index 04d93528..2407ea1b 100644 --- a/apps/permission/backends.py +++ b/apps/permission/backends.py @@ -8,6 +8,7 @@ from django.contrib.auth.models import User, AnonymousUser from django.contrib.contenttypes.models import ContentType from django.db.models import Q, F from note.models import Note, NoteUser, NoteClub, NoteSpecial +from note_kfet import settings from note_kfet.middlewares import get_current_session from member.models import Membership, Club @@ -36,14 +37,21 @@ class PermissionBackend(ModelBackend): # Unauthenticated users have no permissions return Permission.objects.none() - return Permission.objects.annotate(club=F("rolepermissions__role__membership__club")) \ + perms = Permission.objects.annotate(club=F("rolepermissions__role__membership__club")) \ .filter( rolepermissions__role__membership__user=user, rolepermissions__role__membership__date_start__lte=datetime.date.today(), rolepermissions__role__membership__date_end__gte=datetime.date.today(), type=t, - mask__rank__lte=get_current_session().get("permission_mask", 0), - ).distinct() + mask__rank__lte=get_current_session().get("permission_mask", 42), + ) + if settings.DATABASES[perms.db]["ENGINE"] == 'django.db.backends.postgresql_psycopg2': + # We want one permission per club, and per permission type. + # SQLite does not support this kind of filter, that's why we don't filter the permissions with this + # kind of DB. This only increases performances (we can check multiple times the same permission) + # but don't have any semantic influence. + perms = perms.distinct('club', 'pk') + return perms @staticmethod def permissions(user, model, type): @@ -95,7 +103,7 @@ class PermissionBackend(ModelBackend): # Anonymous users can't do anything return Q(pk=-1) - if user.is_superuser and get_current_session().get("permission_mask", 0) >= 42: + if user.is_superuser and get_current_session().get("permission_mask", 42) >= 42: # Superusers have all rights return Q() @@ -129,9 +137,9 @@ class PermissionBackend(ModelBackend): sess = get_current_session() if sess is not None and sess.session_key is None: - return Permission.objects.none() + return False - if user_obj.is_superuser and get_current_session().get("permission_mask", 0) >= 42: + if user_obj.is_superuser and get_current_session().get("permission_mask", 42) >= 42: return True if obj is None: diff --git a/apps/treasury/views.py b/apps/treasury/views.py index 7361d1d2..8d744443 100644 --- a/apps/treasury/views.py +++ b/apps/treasury/views.py @@ -203,9 +203,9 @@ class RemittanceCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["table"] = RemittanceTable(data=Remittance.objects - .filter(PermissionBackend.filter_queryset(self.request.user, Remittance, "view")) - .all()) + context["table"] = RemittanceTable( + data=Remittance.objects.filter( + PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all()) context["special_transactions"] = SpecialTransactionTable(data=SpecialTransaction.objects.none()) return context diff --git a/static/js/base.js b/static/js/base.js index 179bdd3b..fc933794 100644 --- a/static/js/base.js +++ b/static/js/base.js @@ -119,7 +119,7 @@ function displayNote(note, alias, user_note_field=null, profile_pic_field=null) note.display_image = '/media/pic/default.png'; } let img = note.display_image; - if (alias !== note.name) + if (alias !== note.name && note.name) alias += " (aka. " + note.name + ")"; if (user_note_field !== null)