diff --git a/apps/activity/views.py b/apps/activity/views.py
index feb7591d9f8cdf98ebc0a6a844ef95e8a6c94988..51e2ebf535a8ba762e8da65ee91c749ceeec556b 100644
--- a/apps/activity/views.py
+++ b/apps/activity/views.py
@@ -1,5 +1,6 @@
 # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 # SPDX-License-Identifier: GPL-3.0-or-later
+
 from datetime import datetime, timezone
 
 from django.contrib.auth.mixins import LoginRequiredMixin
@@ -11,13 +12,14 @@ from django.utils.translation import gettext_lazy as _
 from django_tables2.views import SingleTableView
 from note.models import NoteUser, Alias, NoteSpecial
 from permission.backends import PermissionBackend
+from permission.views import ProtectQuerysetMixin
 
 from .forms import ActivityForm, GuestForm
 from .models import Activity, Guest, Entry
 from .tables import ActivityTable, GuestTable, EntryTable
 
 
-class ActivityCreateView(LoginRequiredMixin, CreateView):
+class ActivityCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
     model = Activity
     form_class = ActivityForm
 
@@ -30,13 +32,12 @@ class ActivityCreateView(LoginRequiredMixin, CreateView):
         return reverse_lazy('activity:activity_detail', kwargs={"pk": self.object.pk})
 
 
-class ActivityListView(LoginRequiredMixin, SingleTableView):
+class ActivityListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
     model = Activity
     table_class = ActivityTable
 
     def get_queryset(self):
-        return super().get_queryset()\
-            .filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view")).reverse()
+        return super().get_queryset().reverse()
 
     def get_context_data(self, **kwargs):
         ctx = super().get_context_data(**kwargs)
@@ -50,7 +51,7 @@ class ActivityListView(LoginRequiredMixin, SingleTableView):
         return ctx
 
 
-class ActivityDetailView(LoginRequiredMixin, DetailView):
+class ActivityDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
     model = Activity
     context_object_name = "activity"
 
@@ -66,7 +67,7 @@ class ActivityDetailView(LoginRequiredMixin, DetailView):
         return ctx
 
 
-class ActivityUpdateView(LoginRequiredMixin, UpdateView):
+class ActivityUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
     model = Activity
     form_class = ActivityForm
 
@@ -74,18 +75,20 @@ class ActivityUpdateView(LoginRequiredMixin, UpdateView):
         return reverse_lazy('activity:activity_detail', kwargs={"pk": self.kwargs["pk"]})
 
 
-class ActivityInviteView(LoginRequiredMixin, CreateView):
+class ActivityInviteView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
     model = Guest
     form_class = GuestForm
     template_name = "activity/activity_invite.html"
 
     def get_form(self, form_class=None):
         form = super().get_form(form_class)
-        form.activity = Activity.objects.get(pk=self.kwargs["pk"])
+        form.activity = Activity.objects.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\
+            .get(pk=self.kwargs["pk"])
         return form
 
     def form_valid(self, form):
-        form.instance.activity = Activity.objects.get(pk=self.kwargs["pk"])
+        form.instance.activity = Activity.objects\
+            .filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view")).get(pk=self.kwargs["pk"])
         return super().form_valid(form)
 
     def get_success_url(self, **kwargs):
@@ -98,7 +101,8 @@ class ActivityEntryView(LoginRequiredMixin, TemplateView):
     def get_context_data(self, **kwargs):
         ctx = super().get_context_data(**kwargs)
 
-        activity = Activity.objects.get(pk=self.kwargs["pk"])
+        activity = Activity.objects.filter(PermissionBackend.filter_queryset(self.request.user, Activity, "view"))\
+            .get(pk=self.kwargs["pk"])
         ctx["activity"] = activity
 
         matched = []
diff --git a/apps/member/forms.py b/apps/member/forms.py
index d731c10c0311712fa1b6f42e8cdea5aefb468707..9c25f2c28f694725e727aabbe569ce3f5be04035 100644
--- a/apps/member/forms.py
+++ b/apps/member/forms.py
@@ -49,7 +49,13 @@ class ClubForm(forms.ModelForm):
         model = Club
         fields = '__all__'
         widgets = {
-            "membership_fee": AmountInput()
+            "membership_fee": AmountInput(),
+            "parent_club": Autocomplete(
+                Club,
+                attrs={
+                    'api_url': '/api/members/club/',
+                }
+            ),
         }
 
 
diff --git a/apps/member/views.py b/apps/member/views.py
index e8bde67d7f7f16574c06a1a7cd9ccc4b9e5061eb..932899ff7dccdae7e38ec68abf293d2674c0a806 100644
--- a/apps/member/views.py
+++ b/apps/member/views.py
@@ -22,6 +22,7 @@ from note.models.notes import NoteActivity
 from note.models.transactions import Transaction
 from note.tables import HistoryTable, AliasTable, NoteActivityTable
 from permission.backends import PermissionBackend
+from permission.views import ProtectQuerysetMixin
 
 from .filters import UserFilter, UserFilterFormHelper
 from .forms import SignUpForm, ProfileForm, ClubForm, MembershipForm, MemberFormSet, FormSetHelper, \
@@ -64,7 +65,7 @@ class UserCreateView(CreateView):
         return super().form_valid(form)
 
 
-class UserUpdateView(LoginRequiredMixin, UpdateView):
+class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
     model = User
     fields = ['first_name', 'last_name', 'username', 'email']
     template_name = 'member/profile_update.html'
@@ -98,7 +99,8 @@ class UserUpdateView(LoginRequiredMixin, UpdateView):
         if form.is_valid() and profile_form.is_valid():
             new_username = form.data['username']
             alias = Alias.objects.filter(name=new_username)
-            # Si le nouveau pseudo n'est pas un de nos alias, on supprime éventuellement un alias similaire pour le remplacer
+            # Si le nouveau pseudo n'est pas un de nos alias,
+            # on supprime éventuellement un alias similaire pour le remplacer
             if not alias.exists():
                 similar = Alias.objects.filter(
                     normalized_name=Alias.normalize(new_username))
@@ -120,7 +122,7 @@ class UserUpdateView(LoginRequiredMixin, UpdateView):
             return reverse_lazy('member:user_detail', args=(self.object.id,))
 
 
-class UserDetailView(LoginRequiredMixin, DetailView):
+class UserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
     """
     Affiche les informations sur un utilisateur, sa note, ses clubs...
     """
@@ -128,9 +130,6 @@ class UserDetailView(LoginRequiredMixin, DetailView):
     context_object_name = "user_object"
     template_name = "member/profile_detail.html"
 
-    def get_queryset(self, **kwargs):
-        return super().get_queryset().filter(PermissionBackend.filter_queryset(self.request.user, User, "view"))
-
     def get_context_data(self, **kwargs):
         context = super().get_context_data(**kwargs)
         user = context['user_object']
@@ -138,13 +137,13 @@ class UserDetailView(LoginRequiredMixin, DetailView):
             Transaction.objects.all().filter(Q(source=user.note) | Q(destination=user.note)).order_by("-id")\
             .filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view"))
         context['history_list'] = HistoryTable(history_list)
-        club_list = \
-            Membership.objects.all().filter(user=user).only("club")
+        club_list = Membership.objects.all().filter(user=user)\
+            .filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view")).only("club")
         context['club_list'] = ClubTable(club_list)
         return context
 
 
-class UserListView(LoginRequiredMixin, SingleTableView):
+class UserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
     """
     Affiche la liste des utilisateurs, avec une fonction de recherche statique
     """
@@ -155,7 +154,7 @@ class UserListView(LoginRequiredMixin, SingleTableView):
     formhelper_class = UserFilterFormHelper
 
     def get_queryset(self, **kwargs):
-        qs = super().get_queryset().filter(PermissionBackend.filter_queryset(self.request.user, User, "view"))
+        qs = super().get_queryset()
         self.filter = self.filter_class(self.request.GET, queryset=qs)
         self.filter.form.helper = self.formhelper_class()
         return self.filter.qs
@@ -166,7 +165,7 @@ class UserListView(LoginRequiredMixin, SingleTableView):
         return context
 
 
-class ProfileAliasView(LoginRequiredMixin, DetailView):
+class ProfileAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
     model = User
     template_name = 'member/profile_alias.html'
     context_object_name = 'user_object'
@@ -178,7 +177,7 @@ class ProfileAliasView(LoginRequiredMixin, DetailView):
         return context
 
 
-class PictureUpdateView(LoginRequiredMixin, FormMixin, DetailView):
+class PictureUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, DetailView):
     form_class = ImageForm
 
     def get_context_data(self, **kwargs):
@@ -239,8 +238,7 @@ class ManageAuthTokens(LoginRequiredMixin, TemplateView):
     template_name = "member/manage_auth_tokens.html"
 
     def get(self, request, *args, **kwargs):
-        if 'regenerate' in request.GET and Token.objects.filter(
-                user=request.user).exists():
+        if 'regenerate' in request.GET and Token.objects.filter(user=request.user).exists():
             Token.objects.get(user=self.request.user).delete()
             return redirect(reverse_lazy('member:auth_token') + "?show",
                             permanent=True)
@@ -249,8 +247,7 @@ class ManageAuthTokens(LoginRequiredMixin, TemplateView):
 
     def get_context_data(self, **kwargs):
         context = super().get_context_data(**kwargs)
-        context['token'] = Token.objects.get_or_create(
-            user=self.request.user)[0]
+        context['token'] = Token.objects.get_or_create(user=self.request.user)[0]
         return context
 
 
@@ -259,7 +256,7 @@ class ManageAuthTokens(LoginRequiredMixin, TemplateView):
 # ******************************* #
 
 
-class ClubCreateView(LoginRequiredMixin, CreateView):
+class ClubCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
     """
     Create Club
     """
@@ -271,38 +268,32 @@ class ClubCreateView(LoginRequiredMixin, CreateView):
         return super().form_valid(form)
 
 
-class ClubListView(LoginRequiredMixin, SingleTableView):
+class ClubListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
     """
     List existing Clubs
     """
     model = Club
     table_class = ClubTable
 
-    def get_queryset(self, **kwargs):
-        return super().get_queryset().filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))
-
 
-class ClubDetailView(LoginRequiredMixin, DetailView):
+class ClubDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
     model = Club
     context_object_name = "club"
 
-    def get_queryset(self, **kwargs):
-        return super().get_queryset().filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))
-
     def get_context_data(self, **kwargs):
         context = super().get_context_data(**kwargs)
         club = context["club"]
         club_transactions = Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note))\
             .filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view")).order_by('-id')
         context['history_list'] = HistoryTable(club_transactions)
-        club_member = \
-            Membership.objects.all().filter(club=club)
+        club_member = Membership.objects.filter(club=club)\
+            .filter(PermissionBackend.filter_queryset(self.request.user, Membership, "view")).all()
         # TODO: consider only valid Membership
         context['member_list'] = club_member
         return context
 
 
-class ClubAliasView(LoginRequiredMixin, DetailView):
+class ClubAliasView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
     model = Club
     template_name = 'member/club_alias.html'
     context_object_name = 'club'
@@ -314,7 +305,7 @@ class ClubAliasView(LoginRequiredMixin, DetailView):
         return context
 
 
-class ClubUpdateView(LoginRequiredMixin, UpdateView):
+class ClubUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
     model = Club
     context_object_name = "club"
     form_class = ClubForm
@@ -333,7 +324,7 @@ class ClubPictureUpdateView(PictureUpdateView):
         return reverse_lazy('member:club_detail', kwargs={'pk': self.object.id})
 
 
-class ClubAddMemberView(LoginRequiredMixin, CreateView):
+class ClubAddMemberView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
     model = Membership
     form_class = MembershipForm
     template_name = 'member/add_members.html'
@@ -344,7 +335,8 @@ class ClubAddMemberView(LoginRequiredMixin, CreateView):
                                                                                  "change"))
 
     def get_context_data(self, **kwargs):
-        club = Club.objects.get(pk=self.kwargs["pk"])
+        club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\
+            .get(pk=self.kwargs["pk"])
         context = super().get_context_data(**kwargs)
         context['formset'] = MemberFormSet()
         context['helper'] = FormSetHelper()
@@ -367,36 +359,40 @@ class ClubAddMemberView(LoginRequiredMixin, CreateView):
         return super().form_valid(formset)
 
 
-class ClubLinkedNotesView(LoginRequiredMixin, SingleTableView):
+class ClubLinkedNotesView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
     model = NoteActivity
     table_class = NoteActivityTable
 
     def get_queryset(self):
-        return super().get_queryset().filter(club=self.get_object())\
-            .filter(PermissionBackend.filter_queryset(self.request.user, NoteActivity, "view"))
+        return super().get_queryset().filter(club=self.get_object())
 
     def get_object(self):
         if hasattr(self, 'object'):
             return self.object
-        self.object = Club.objects.get(pk=int(self.kwargs["pk"]))
+        self.object = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\
+            .get(pk=int(self.kwargs["pk"]))
         return self.object
 
     def get_context_data(self, **kwargs):
         ctx = super().get_context_data(**kwargs)
 
-        ctx["object"] = ctx["club"] = self.get_object()
+        club = ctx["object"] = ctx["club"] = self.get_object()
+
+        empty_note = NoteActivity(note_name="", club=club, controller=self.request.user)
+        ctx["can_create"] = PermissionBackend().has_perm(self.request.user, "note.add_noteactivity", empty_note)
 
         return ctx
 
 
-class ClubLinkedNoteCreateView(LoginRequiredMixin, CreateView):
+class ClubLinkedNoteCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
     model = NoteActivity
     form_class = NoteActivityForm
 
     def get_context_data(self, **kwargs):
         ctx = super().get_context_data(**kwargs)
 
-        club = Club.objects.get(pk=self.kwargs["club_pk"])
+        club = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\
+            .get(pk=self.kwargs["club_pk"])
         ctx["object"] = ctx["club"] = club
         ctx["form"].fields["club"].initial = club
 
@@ -408,14 +404,15 @@ class ClubLinkedNoteCreateView(LoginRequiredMixin, CreateView):
                             kwargs={"club_pk": self.object.club.pk, "pk": self.object.pk})
 
 
-class ClubLinkedNoteUpdateView(LoginRequiredMixin, UpdateView):
+class ClubLinkedNoteUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
     model = NoteActivity
     form_class = NoteActivityForm
 
     def get_context_data(self, **kwargs):
         ctx = super().get_context_data(**kwargs)
 
-        ctx["club"] = Club.objects.get(pk=self.kwargs["club_pk"])
+        ctx["club"] = Club.objects.filter(PermissionBackend.filter_queryset(self.request.user, Club, "view"))\
+            .get(pk=self.kwargs["club_pk"])
 
         return ctx
 
@@ -424,15 +421,15 @@ class ClubLinkedNoteUpdateView(LoginRequiredMixin, UpdateView):
                             kwargs={"club_pk": self.object.club.pk, "pk": self.object.pk})
 
 
-class ClubLinkedNoteDetailView(LoginRequiredMixin, DetailView):
+class ClubLinkedNoteDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
     model = NoteActivity
 
     def get_context_data(self, **kwargs):
         ctx = super().get_context_data(**kwargs)
 
-        note = NoteActivity.objects.get(pk=self.kwargs["pk"])
+        note = self.get_queryset().filter(pk=self.kwargs["pk"]).get()
 
-        transactions = Transaction.objects.all().filter(Q(source=note) | Q(destination=note))\
+        transactions = Transaction.objects.filter(Q(source=note) | Q(destination=note))\
             .filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view")).order_by("-id")
         ctx['history_list'] = HistoryTable(transactions)
         ctx["note"] = note
diff --git a/apps/note/models/__init__.py b/apps/note/models/__init__.py
index e9c8a0a9f28d15f586e4398a0f29406bdf1aec01..dd024963974e56c7f278badfdc6e734a5aa1528b 100644
--- a/apps/note/models/__init__.py
+++ b/apps/note/models/__init__.py
@@ -1,13 +1,13 @@
 # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 # SPDX-License-Identifier: GPL-3.0-or-later
 
-from .notes import Alias, Note, NoteClub, NoteSpecial, NoteUser
+from .notes import Alias, Note, NoteClub, NoteSpecial, NoteUser, NoteActivity
 from .transactions import MembershipTransaction, Transaction, \
     TemplateCategory, TransactionTemplate, RecurrentTransaction, SpecialTransaction
 
 __all__ = [
     # Notes
-    'Alias', 'Note', 'NoteClub', 'NoteSpecial', 'NoteUser',
+    'Alias', 'Note', 'NoteClub', 'NoteSpecial', 'NoteUser', 'NoteActivity',
     # Transactions
     'MembershipTransaction', 'Transaction', 'TemplateCategory', 'TransactionTemplate',
     'RecurrentTransaction', 'SpecialTransaction',
diff --git a/apps/note/views.py b/apps/note/views.py
index 252792815f3ae58eacb42ded15b70376fe70c273..ac9b3e40623ce1ec676f340ddf7caa3a81639e43 100644
--- a/apps/note/views.py
+++ b/apps/note/views.py
@@ -9,6 +9,7 @@ from django_tables2 import SingleTableView
 from django.urls import reverse_lazy
 from note_kfet.inputs import AmountInput
 from permission.backends import PermissionBackend
+from permission.views import ProtectQuerysetMixin
 
 from .forms import TransactionTemplateForm
 from .models import Transaction, TransactionTemplate, RecurrentTransaction, NoteSpecial
@@ -16,7 +17,7 @@ from .models.transactions import SpecialTransaction
 from .tables import HistoryTable, ButtonTable
 
 
-class TransactionCreateView(LoginRequiredMixin, SingleTableView):
+class TransactionCreateView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
     """
     View for the creation of Transaction between two note which are not :models:`transactions.RecurrentTransaction`.
     e.g. for donation/transfer between people and clubs or for credit/debit with :models:`note.NoteSpecial`
@@ -26,12 +27,9 @@ class TransactionCreateView(LoginRequiredMixin, SingleTableView):
     model = Transaction
     # Transaction history table
     table_class = HistoryTable
-    table_pagination = {"per_page": 50}
 
-    def get_queryset(self):
-        return Transaction.objects.filter(PermissionBackend.filter_queryset(
-            self.request.user, Transaction, "view")
-        ).order_by("-id").all()[:50]
+    def get_queryset(self, **kwargs):
+        return super().get_queryset(**kwargs).order_by("-id").all()[:50]
 
     def get_context_data(self, **kwargs):
         """
@@ -42,12 +40,14 @@ class TransactionCreateView(LoginRequiredMixin, SingleTableView):
         context['amount_widget'] = AmountInput(attrs={"id": "amount"})
         context['polymorphic_ctype'] = ContentType.objects.get_for_model(Transaction).pk
         context['special_polymorphic_ctype'] = ContentType.objects.get_for_model(SpecialTransaction).pk
-        context['special_types'] = NoteSpecial.objects.order_by("special_type").all()
+        context['special_types'] = NoteSpecial.objects\
+            .filter(PermissionBackend.filter_queryset(self.request.user, NoteSpecial, "view"))\
+            .order_by("special_type").all()
 
         return context
 
 
-class TransactionTemplateCreateView(LoginRequiredMixin, CreateView):
+class TransactionTemplateCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
     """
     Create TransactionTemplate
     """
@@ -56,7 +56,7 @@ class TransactionTemplateCreateView(LoginRequiredMixin, CreateView):
     success_url = reverse_lazy('note:template_list')
 
 
-class TransactionTemplateListView(LoginRequiredMixin, SingleTableView):
+class TransactionTemplateListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
     """
     List TransactionsTemplates
     """
@@ -64,7 +64,7 @@ class TransactionTemplateListView(LoginRequiredMixin, SingleTableView):
     table_class = ButtonTable
 
 
-class TransactionTemplateUpdateView(LoginRequiredMixin, UpdateView):
+class TransactionTemplateUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
     """
     """
     model = TransactionTemplate
@@ -72,21 +72,19 @@ class TransactionTemplateUpdateView(LoginRequiredMixin, UpdateView):
     success_url = reverse_lazy('note:template_list')
 
 
-class ConsoView(LoginRequiredMixin, SingleTableView):
+class ConsoView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
     """
     The Magic View that make people pay their beer and burgers.
     (Most of the magic happens in the dark world of Javascript see consos.js)
     """
+    model = Transaction
     template_name = "note/conso_form.html"
 
     # Transaction history table
     table_class = HistoryTable
-    table_pagination = {"per_page": 50}
 
-    def get_queryset(self):
-        return Transaction.objects.filter(
-            PermissionBackend.filter_queryset(self.request.user, Transaction, "view")
-        ).order_by("-id").all()[:50]
+    def get_queryset(self, **kwargs):
+        return super().get_queryset(**kwargs).order_by("-id").all()[:50]
 
     def get_context_data(self, **kwargs):
         """
diff --git a/apps/permission/backends.py b/apps/permission/backends.py
index e61b07191227d755f52ebe6953a0897029f980fe..f838fc1f0cbbe5c2d7b23efc494372aebe7fadbd 100644
--- a/apps/permission/backends.py
+++ b/apps/permission/backends.py
@@ -5,7 +5,7 @@ from django.contrib.auth.backends import ModelBackend
 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.models import Note, NoteUser, NoteClub, NoteSpecial, NoteActivity
 from note_kfet.middlewares import get_current_session
 from member.models import Membership, Club
 
@@ -35,7 +35,7 @@ class PermissionBackend(ModelBackend):
             model__app_label=model.app_label,  # For polymorphic models, we don't filter on model type
             type=type,
         ).all():
-            if not isinstance(model, permission.model.__class__):
+            if not isinstance(model, permission.model.__class__) or not permission.club:
                 continue
 
             club = Club.objects.get(pk=permission.club)
@@ -49,6 +49,7 @@ class PermissionBackend(ModelBackend):
                 NoteUser=NoteUser,
                 NoteClub=NoteClub,
                 NoteSpecial=NoteSpecial,
+                NoteActivity=NoteActivity,
                 F=F,
                 Q=Q
             )
diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json
index 31b59069ef832598460ae07716b30216ba4a781b..e0430530469cac7cc2ac2433780f9c3baa1a3707 100644
--- a/apps/permission/fixtures/initial.json
+++ b/apps/permission/fixtures/initial.json
@@ -176,7 +176,7 @@
         "note",
         "alias"
       ],
-      "query": "[\"OR\", {\"note__in\": [\"NoteUser\", \"objects\", [\"filter\", {\"user__membership__club__name\": \"Kfet\"}], [\"all\"]]}, {\"note__in\": [\"NoteClub\", \"objects\", [\"all\"]]}]",
+      "query": "[\"OR\", {\"note__in\": [\"NoteUser\", \"objects\", [\"filter\", {\"user__membership__club__name\": \"Kfet\"}], [\"all\"]]}, {\"note__in\": [\"NoteClub\", \"objects\", [\"all\"]]}, {\"note__in\": [\"NoteActivity\", \"objects\", [\"all\"]]}]",
       "type": "view",
       "mask": 1,
       "field": "",
@@ -386,7 +386,7 @@
         "note",
         "transaction"
       ],
-      "query": "[\"AND\", [\"OR\", {\"source\": [\"club\", \"note\"]}, {\"destination\": [\"club\", \"note\"]}], {\"amount__lte\": {\"F\": [\"ADD\", [\"F\", \"source__balance\"], 5000]}}]",
+      "query": "[\"AND\", [\"OR\", {\"source\": [\"club\", \"note\"]}, {\"destination\": [\"club\", \"note\"]}], [\"OR\", {\"amount__lte\": {\"F\": [\"ADD\", [\"F\", \"source__balance\"], 5000]}}, {\"valid\": false}]]",
       "type": "add",
       "mask": 2,
       "field": "",
@@ -783,6 +783,111 @@
       "description": "Validate invitation transactions"
     }
   },
+  {
+    "model": "permission.permission",
+    "pk": 47,
+    "fields": {
+      "model": [
+        "member",
+        "club"
+      ],
+      "query": "{\"pk\": [\"club\", \"pk\"]}",
+      "type": "change",
+      "mask": 1,
+      "field": "",
+      "description": "Update club"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 48,
+    "fields": {
+      "model": [
+        "note",
+        "noteactivity"
+      ],
+      "query": "{\"club\": [\"club\"]}",
+      "type": "change",
+      "mask": 1,
+      "field": "",
+      "description": "Manage notes that are linked to a club"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 49,
+    "fields": {
+      "model": [
+        "note",
+        "noteactivity"
+      ],
+      "query": "{\"club\": [\"club\"]}",
+      "type": "view",
+      "mask": 1,
+      "field": "",
+      "description": "View notes that are linked to a club"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 50,
+    "fields": {
+      "model": [
+        "note",
+        "transaction"
+      ],
+      "query": "[\"AND\", [\"OR\", {\"source__noteactivity__controller\": [\"user\"]}, {\"destination__noteactivity__controller\": [\"user\"]}], [\"OR\", {\"amount__lte\": {\"F\": [\"ADD\", [\"F\", \"source__balance\"], 5000]}}, {\"valid\": false}]]",
+      "type": "add",
+      "mask": 2,
+      "field": "",
+      "description": "Add transactions linked to a noteactivity"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 51,
+    "fields": {
+      "model": [
+        "note",
+        "transaction"
+      ],
+      "query": "[\"AND\", [\"OR\", {\"source__noteactivity__controller\": [\"user\"]}, {\"destination__noteactivity__controller\": [\"user\"]}]]",
+      "type": "view",
+      "mask": 1,
+      "field": "",
+      "description": "View transactions linked to a noteactivity"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 52,
+    "fields": {
+      "model": [
+        "note",
+        "note"
+      ],
+      "query": "{\"noteactivity__controller\": [\"user\"]}",
+      "type": "view",
+      "mask": 1,
+      "field": "",
+      "description": "View note activity"
+    }
+  },
+  {
+    "model": "permission.permission",
+    "pk": 53,
+    "fields": {
+      "model": [
+        "note",
+        "noteactivity"
+      ],
+      "query": "{\"controller\": [\"user\"]}",
+      "type": "view",
+      "mask": 1,
+      "field": "",
+      "description": "View note activity"
+    }
+  },
   {
     "model": "permission.rolepermissions",
     "pk": 1,
@@ -810,7 +915,6 @@
         3,
         4,
         5,
-        6,
         7,
         8,
         9,
@@ -827,7 +931,12 @@
         35,
         36,
         39,
-        40
+        40,
+        6,
+        52,
+        53,
+        51,
+        50
       ]
     }
   },
@@ -838,9 +947,9 @@
       "role": 8,
       "permissions": [
         19,
-        20,
         21,
-        22
+        22,
+        20
       ]
     }
   },
@@ -880,5 +989,18 @@
         46
       ]
     }
+  },
+  {
+    "model": "permission.rolepermissions",
+    "pk": 6,
+    "fields": {
+      "role": 7,
+      "permissions": [
+        22,
+        47,
+        48,
+        49
+      ]
+    }
   }
 ]
diff --git a/apps/permission/models.py b/apps/permission/models.py
index 205f5b418c7baf09254087c939a504ee919c7f0f..d1b55090f04b82af44e7dc80258f50440b3694d0 100644
--- a/apps/permission/models.py
+++ b/apps/permission/models.py
@@ -38,20 +38,29 @@ class InstancedPermission:
             if permission_type == self.type:
                 self.update_query()
 
-                # Don't increase indexes
-                obj.pk = 0
+                # Don't increase indexes, if the primary key is an AutoField
+                if not hasattr(obj, "pk") or not obj.pk:
+                    obj.pk = 0
+                    oldpk = None
+                else:
+                    oldpk = obj.pk
+                # Ensure previous models are deleted
+                self.model.model_class().objects.filter(pk=obj.pk).delete()
                 # 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()
+                ret = self.model.model_class().objects.filter(self.query & Q(pk=obj.pk)).exists()
                 # Delete testing object
                 Model.delete(obj)
+
+                # If the primary key was specified, we restore it
+                obj.pk = oldpk
                 return ret
 
         if permission_type == self.type:
             if self.field and field_name != self.field:
                 return False
             self.update_query()
-            return obj in self.model.model_class().objects.filter(self.query).all()
+            return self.model.model_class().objects.filter(self.query & Q(pk=obj.pk)).exists()
         else:
             return False
 
diff --git a/apps/permission/views.py b/apps/permission/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..bbd9872f92d6d356ae36b87147eccd3a1bb56ac4
--- /dev/null
+++ b/apps/permission/views.py
@@ -0,0 +1,11 @@
+# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+from permission.backends import PermissionBackend
+
+
+class ProtectQuerysetMixin:
+    def get_queryset(self, **kwargs):
+        qs = super().get_queryset(**kwargs)
+
+        return qs.filter(PermissionBackend.filter_queryset(self.request.user, qs.model, "view"))
diff --git a/apps/treasury/forms.py b/apps/treasury/forms.py
index 7fe7de4c3da65bc82e4b854353f4b756f7e33faa..ad479e143ed33ba07275261ecf86a0c2c09fc8b9 100644
--- a/apps/treasury/forms.py
+++ b/apps/treasury/forms.py
@@ -8,6 +8,7 @@ from crispy_forms.layout import Submit
 from django import forms
 from django.utils.translation import gettext_lazy as _
 from note_kfet.inputs import DatePickerInput, AmountInput
+from permission.backends import PermissionBackend
 
 from .models import Invoice, Product, Remittance, SpecialTransactionProxy
 
@@ -131,7 +132,8 @@ class LinkTransactionToRemittanceForm(forms.ModelForm):
         # Add submit button
         self.helper.add_input(Submit('submit', _("Submit"), attr={'class': 'btn btn-block btn-primary'}))
 
-        self.fields["remittance"].queryset = Remittance.objects.filter(closed=False)
+        self.fields["remittance"].queryset = Remittance.objects.filter(closed=False)\
+            .filter(PermissionBackend.filter_queryset(self.request.user, Remittance, "view"))
 
     def clean_last_name(self):
         """
diff --git a/apps/treasury/views.py b/apps/treasury/views.py
index c374ced102a3e342d405e16f7585e568f30bba5c..adf38aaa1067cd654c8784883dddfd745c4fd3ef 100644
--- a/apps/treasury/views.py
+++ b/apps/treasury/views.py
@@ -19,13 +19,15 @@ from django.views.generic.base import View, TemplateView
 from django_tables2 import SingleTableView
 from note.models import SpecialTransaction, NoteSpecial
 from note_kfet.settings.base import BASE_DIR
+from permission.backends import PermissionBackend
+from permission.views import ProtectQuerysetMixin
 
 from .forms import InvoiceForm, ProductFormSet, ProductFormSetHelper, RemittanceForm, LinkTransactionToRemittanceForm
 from .models import Invoice, Product, Remittance, SpecialTransactionProxy
 from .tables import InvoiceTable, RemittanceTable, SpecialTransactionTable
 
 
-class InvoiceCreateView(LoginRequiredMixin, CreateView):
+class InvoiceCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
     """
     Create Invoice
     """
@@ -67,7 +69,7 @@ class InvoiceCreateView(LoginRequiredMixin, CreateView):
         return reverse_lazy('treasury:invoice_list')
 
 
-class InvoiceListView(LoginRequiredMixin, SingleTableView):
+class InvoiceListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
     """
     List existing Invoices
     """
@@ -75,7 +77,7 @@ class InvoiceListView(LoginRequiredMixin, SingleTableView):
     table_class = InvoiceTable
 
 
-class InvoiceUpdateView(LoginRequiredMixin, UpdateView):
+class InvoiceUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
     """
     Create Invoice
     """
@@ -130,7 +132,7 @@ class InvoiceRenderView(LoginRequiredMixin, View):
 
     def get(self, request, **kwargs):
         pk = kwargs["pk"]
-        invoice = Invoice.objects.get(pk=pk)
+        invoice = Invoice.objects.filter(PermissionBackend.filter_queryset(request.user, Invoice, "view")).get(pk=pk)
         products = Product.objects.filter(invoice=invoice).all()
 
         # Informations of the BDE. Should be updated when the school will move.
@@ -188,7 +190,7 @@ class InvoiceRenderView(LoginRequiredMixin, View):
         return response
 
 
-class RemittanceCreateView(LoginRequiredMixin, CreateView):
+class RemittanceCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
     """
     Create Remittance
     """
@@ -201,7 +203,9 @@ class RemittanceCreateView(LoginRequiredMixin, CreateView):
     def get_context_data(self, **kwargs):
         ctx = super().get_context_data(**kwargs)
 
-        ctx["table"] = RemittanceTable(data=Remittance.objects.all())
+        ctx["table"] = RemittanceTable(data=Remittance.objects
+                                       .filter(PermissionBackend.filter_queryset(self.request.user, Remittance, "view"))
+                                       .all())
         ctx["special_transactions"] = SpecialTransactionTable(data=SpecialTransaction.objects.none())
 
         return ctx
@@ -216,22 +220,28 @@ class RemittanceListView(LoginRequiredMixin, TemplateView):
     def get_context_data(self, **kwargs):
         ctx = super().get_context_data(**kwargs)
 
-        ctx["opened_remittances"] = RemittanceTable(data=Remittance.objects.filter(closed=False).all())
-        ctx["closed_remittances"] = RemittanceTable(data=Remittance.objects.filter(closed=True).reverse().all())
+        ctx["opened_remittances"] = RemittanceTable(
+            data=Remittance.objects.filter(closed=False).filter(
+                PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all())
+        ctx["closed_remittances"] = RemittanceTable(
+            data=Remittance.objects.filter(closed=True).filter(
+                PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).reverse().all())
 
         ctx["special_transactions_no_remittance"] = SpecialTransactionTable(
             data=SpecialTransaction.objects.filter(source__in=NoteSpecial.objects.filter(~Q(remittancetype=None)),
-                                                   specialtransactionproxy__remittance=None).all(),
+                                                   specialtransactionproxy__remittance=None).filter(
+                PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all(),
             exclude=('remittance_remove', ))
         ctx["special_transactions_with_remittance"] = SpecialTransactionTable(
             data=SpecialTransaction.objects.filter(source__in=NoteSpecial.objects.filter(~Q(remittancetype=None)),
-                                                   specialtransactionproxy__remittance__closed=False).all(),
+                                                   specialtransactionproxy__remittance__closed=False).filter(
+                PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all(),
             exclude=('remittance_add', ))
 
         return ctx
 
 
-class RemittanceUpdateView(LoginRequiredMixin, UpdateView):
+class RemittanceUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
     """
     Update Remittance
     """
@@ -244,8 +254,10 @@ class RemittanceUpdateView(LoginRequiredMixin, UpdateView):
     def get_context_data(self, **kwargs):
         ctx = super().get_context_data(**kwargs)
 
-        ctx["table"] = RemittanceTable(data=Remittance.objects.all())
-        data = SpecialTransaction.objects.filter(specialtransactionproxy__remittance=self.object).all()
+        ctx["table"] = RemittanceTable(data=Remittance.objects.filter(
+                PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all())
+        data = SpecialTransaction.objects.filter(specialtransactionproxy__remittance=self.object).filter(
+                PermissionBackend.filter_queryset(self.request.user, Remittance, "view")).all()
         ctx["special_transactions"] = SpecialTransactionTable(
             data=data,
             exclude=('remittance_add', 'remittance_remove', ) if self.object.closed else ('remittance_add', ))
@@ -253,7 +265,7 @@ class RemittanceUpdateView(LoginRequiredMixin, UpdateView):
         return ctx
 
 
-class LinkTransactionToRemittanceView(LoginRequiredMixin, UpdateView):
+class LinkTransactionToRemittanceView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
     """
     Attach a special transaction to a remittance
     """
diff --git a/templates/note/noteactivity_detail.html b/templates/note/noteactivity_detail.html
index 731707f61f9255981aa155720fb8ff3ac1122cc3..aab9e05569b538db701798157235179720896753 100644
--- a/templates/note/noteactivity_detail.html
+++ b/templates/note/noteactivity_detail.html
@@ -3,6 +3,7 @@
 {% load i18n %}
 {% load render_table from django_tables2 %}
 {% load pretty_money %}
+{% load perms %}
 
 {% block profile_info %}
 {% include "member/club_info.html" %}
@@ -25,9 +26,11 @@
                 <dd class="col-xl-6">{{ note.balance|pretty_money }}</dd>
             </dl>
 
-            <div class="card-footer text-center">
-                <a class="btn btn-primary btn-sm my-1" href="{% url 'member:club_linked_note_update' club_pk=club.pk pk=note.pk %}"> {% trans "Edit" %}</a>
-            </div>
+            {% if "change_"|has_perm:note %}
+                <div class="card-footer text-center">
+                    <a class="btn btn-primary btn-sm my-1" href="{% url 'member:club_linked_note_update' club_pk=club.pk pk=note.pk %}"> {% trans "Edit" %}</a>
+                </div>
+            {% endif %}
         </div>
 
 
diff --git a/templates/note/noteactivity_list.html b/templates/note/noteactivity_list.html
index b4d695368d0d74b2557d511b7a2f850dc5881ec1..3cd5b786481414d00ead29687341e5323b088e41 100644
--- a/templates/note/noteactivity_list.html
+++ b/templates/note/noteactivity_list.html
@@ -19,9 +19,11 @@
             </div>
         </div>
 
-        <a href="{% url 'member:club_linked_note_create' club_pk=club.pk %}">
-            <button class="btn btn-primary btn-block">{% trans "Add new note" %}</button>
-        </a>
+        {% if can_create %}
+            <a href="{% url 'member:club_linked_note_create' club_pk=club.pk %}">
+                <button class="btn btn-primary btn-block">{% trans "Add new note" %}</button>
+            </a>
+        {% endif %}
     </div>
 </div>
 {% endblock %}