Don't display a note that we can't see, fix CI, fix distinct fields on PostgresSQL DB

This commit is contained in:
Yohann D'ANELLO 2020-04-10 00:02:22 +02:00
parent bac81cd13e
commit 751147f254
8 changed files with 48 additions and 26 deletions

View File

@ -139,7 +139,7 @@ class Entry(models.Model):
verbose_name = _("entry") verbose_name = _("entry")
verbose_name_plural = _("entries") 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) qs = Entry.objects.filter(~Q(pk=self.pk), activity=self.activity, note=self.note, guest=self.guest)
if qs.exists(): if qs.exists():
@ -153,7 +153,7 @@ class Entry(models.Model):
if self.note.balance < 0: if self.note.balance < 0:
raise ValidationError(_("The balance is negative.")) raise ValidationError(_("The balance is negative."))
ret = super().save(*args,**kwargs) ret = super().save(*args, **kwargs)
if insert and self.guest: if insert and self.guest:
GuestTransaction.objects.create( GuestTransaction.objects.create(

View File

@ -3,6 +3,7 @@
from datetime import datetime, timezone from datetime import datetime, timezone
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db.models import F, Q from django.db.models import F, Q
@ -45,8 +46,8 @@ class ActivityListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView
context['title'] = _("Activities") context['title'] = _("Activities")
upcoming_activities = Activity.objects.filter(date_end__gt=datetime.now()) upcoming_activities = Activity.objects.filter(date_end__gt=datetime.now())
context['upcoming'] = ActivityTable(data=upcoming_activities context['upcoming'] = ActivityTable(data=upcoming_activities.filter(
.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))) PermissionBackend.filter_queryset(self.request.user, Activity, "view")))
return context return context
@ -138,8 +139,14 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView):
| Q(note__noteuser__user__last_name__regex=pattern) | Q(note__noteuser__user__last_name__regex=pattern)
| Q(name__regex=pattern) | Q(name__regex=pattern)
| Q(normalized_name__regex=Alias.normalize(pattern)))) \ | Q(normalized_name__regex=Alias.normalize(pattern)))) \
.filter(PermissionBackend.filter_queryset(self.request.user, Alias, "view"))\ .filter(PermissionBackend.filter_queryset(self.request.user, Alias, "view"))
.distinct()[:20] 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: for note in note_qs:
note.type = "Adhérent" note.type = "Adhérent"
note.activity = activity note.activity = activity
@ -153,9 +160,9 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView):
context["title"] = _('Entry for activity "{}"').format(activity.name) context["title"] = _('Entry for activity "{}"').format(activity.name)
context["noteuser_ctype"] = ContentType.objects.get_for_model(NoteUser).pk context["noteuser_ctype"] = ContentType.objects.get_for_model(NoteUser).pk
context["notespecial_ctype"] = ContentType.objects.get_for_model(NoteSpecial).pk context["notespecial_ctype"] = ContentType.objects.get_for_model(NoteSpecial).pk
context["activities_open"] = Activity.objects.filter(open=True).filter( 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, "view")).filter(
PermissionBackend.filter_queryset(self.request.user, Activity, "change")).all() PermissionBackend.filter_queryset(self.request.user, Activity, "change")).all()
return context return context

View File

@ -3,6 +3,8 @@
from rest_framework import serializers from rest_framework import serializers
from rest_polymorphic.serializers import PolymorphicSerializer 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.notes import Note, NoteClub, NoteSpecial, NoteUser, Alias
from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction, TemplateCategory, \ from ..models.transactions import TransactionTemplate, Transaction, MembershipTransaction, TemplateCategory, \
@ -96,20 +98,24 @@ class NotePolymorphicSerializer(PolymorphicSerializer):
class Meta: class Meta:
model = Note model = Note
class ConsumerSerializer(serializers.ModelSerializer): class ConsumerSerializer(serializers.ModelSerializer):
""" """
REST API Nested Serializer for Consumers. 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: class Meta:
model = Alias model = Alias
fields = '__all__' fields = '__all__'
@staticmethod def get_note(self, obj):
def setup_eager_loading(queryset): if PermissionBackend.check_perm(get_current_authenticated_user(), "note.view_note", obj.note):
queryset = queryset.select_related('note') print(obj.pk)
return NotePolymorphicSerializer().to_representation(obj.note)
return dict(id=obj.id)
class TemplateCategorySerializer(serializers.ModelSerializer): class TemplateCategorySerializer(serializers.ModelSerializer):
""" """

View File

@ -12,7 +12,7 @@ def register_note_urls(router, path):
router.register(path + '/note', NotePolymorphicViewSet) router.register(path + '/note', NotePolymorphicViewSet)
router.register(path + '/alias', AliasViewSet) router.register(path + '/alias', AliasViewSet)
router.register(path + '/consumer', ConsumerViewSet) router.register(path + '/consumer', ConsumerViewSet)
router.register(path + '/transaction/category', TemplateCategoryViewSet) router.register(path + '/transaction/category', TemplateCategoryViewSet)
router.register(path + '/transaction/transaction', TransactionViewSet) router.register(path + '/transaction/transaction', TransactionViewSet)
router.register(path + '/transaction/template', TransactionTemplateViewSet) router.register(path + '/transaction/template', TransactionTemplateViewSet)

View File

@ -89,6 +89,7 @@ class AliasViewSet(ReadProtectedModelViewSet):
return queryset return queryset
class ConsumerViewSet(ReadOnlyProtectedModelViewSet): class ConsumerViewSet(ReadOnlyProtectedModelViewSet):
queryset = Alias.objects.all() queryset = Alias.objects.all()
serializer_class = ConsumerSerializer serializer_class = ConsumerSerializer
@ -111,7 +112,7 @@ class ConsumerViewSet(ReadOnlyProtectedModelViewSet):
| Q(normalized_name__regex="^" + alias.lower())) | Q(normalized_name__regex="^" + alias.lower()))
return queryset return queryset
class TemplateCategoryViewSet(ReadProtectedModelViewSet): class TemplateCategoryViewSet(ReadProtectedModelViewSet):
""" """

View File

@ -8,6 +8,7 @@ from django.contrib.auth.models import User, AnonymousUser
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.db.models import Q, F from django.db.models import Q, F
from note.models import Note, NoteUser, NoteClub, NoteSpecial from note.models import Note, NoteUser, NoteClub, NoteSpecial
from note_kfet import settings
from note_kfet.middlewares import get_current_session from note_kfet.middlewares import get_current_session
from member.models import Membership, Club from member.models import Membership, Club
@ -36,14 +37,21 @@ class PermissionBackend(ModelBackend):
# Unauthenticated users have no permissions # Unauthenticated users have no permissions
return Permission.objects.none() 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( .filter(
rolepermissions__role__membership__user=user, rolepermissions__role__membership__user=user,
rolepermissions__role__membership__date_start__lte=datetime.date.today(), rolepermissions__role__membership__date_start__lte=datetime.date.today(),
rolepermissions__role__membership__date_end__gte=datetime.date.today(), rolepermissions__role__membership__date_end__gte=datetime.date.today(),
type=t, type=t,
mask__rank__lte=get_current_session().get("permission_mask", 0), mask__rank__lte=get_current_session().get("permission_mask", 42),
).distinct() )
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 @staticmethod
def permissions(user, model, type): def permissions(user, model, type):
@ -95,7 +103,7 @@ class PermissionBackend(ModelBackend):
# Anonymous users can't do anything # Anonymous users can't do anything
return Q(pk=-1) 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 # Superusers have all rights
return Q() return Q()
@ -129,9 +137,9 @@ class PermissionBackend(ModelBackend):
sess = get_current_session() sess = get_current_session()
if sess is not None and sess.session_key is None: 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 return True
if obj is None: if obj is None:

View File

@ -203,9 +203,9 @@ class RemittanceCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context["table"] = RemittanceTable(data=Remittance.objects context["table"] = RemittanceTable(
.filter(PermissionBackend.filter_queryset(self.request.user, Remittance, "view")) data=Remittance.objects.filter(
.all()) PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all())
context["special_transactions"] = SpecialTransactionTable(data=SpecialTransaction.objects.none()) context["special_transactions"] = SpecialTransactionTable(data=SpecialTransaction.objects.none())
return context return context

View File

@ -119,7 +119,7 @@ function displayNote(note, alias, user_note_field=null, profile_pic_field=null)
note.display_image = '/media/pic/default.png'; note.display_image = '/media/pic/default.png';
} }
let img = note.display_image; let img = note.display_image;
if (alias !== note.name) if (alias !== note.name && note.name)
alias += " (aka. " + note.name + ")"; alias += " (aka. " + note.name + ")";
if (user_note_field !== null) if (user_note_field !== null)