diff --git a/apps/api/viewsets.py b/apps/api/viewsets.py index 97acac13..faeadee1 100644 --- a/apps/api/viewsets.py +++ b/apps/api/viewsets.py @@ -24,7 +24,6 @@ class ReadProtectedModelViewSet(ModelViewSet): self.model = ContentType.objects.get_for_model(self.serializer_class.Meta.model).model_class() def get_queryset(self): - self.request.session.setdefault("permission_mask", 42) return self.queryset.filter(PermissionBackend.filter_queryset(self.request, self.model, "view")).distinct() @@ -38,7 +37,6 @@ class ReadOnlyProtectedModelViewSet(ReadOnlyModelViewSet): self.model = ContentType.objects.get_for_model(self.serializer_class.Meta.model).model_class() def get_queryset(self): - self.request.session.setdefault("permission_mask", 42) return self.queryset.filter(PermissionBackend.filter_queryset(self.request, self.model, "view")).distinct() diff --git a/apps/logs/signals.py b/apps/logs/signals.py index 4a6f9015..a3166eed 100644 --- a/apps/logs/signals.py +++ b/apps/logs/signals.py @@ -83,7 +83,7 @@ def save_object(sender, instance, **kwargs): ip = request.META.get('REMOTE_ADDR') if not user.is_authenticated: - # For registration purposes + # For registration and OAuth2 purposes user = None # noinspection PyProtectedMember @@ -160,6 +160,10 @@ def delete_object(sender, instance, **kwargs): else: ip = request.META.get('REMOTE_ADDR') + if not user.is_authenticated: + # For registration and OAuth2 purposes + user = None + # On crée notre propre sérialiseur JSON pour pouvoir sauvegarder les modèles class CustomSerializer(ModelSerializer): class Meta: diff --git a/apps/note/api/views.py b/apps/note/api/views.py index e4f7404c..4342b666 100644 --- a/apps/note/api/views.py +++ b/apps/note/api/views.py @@ -39,7 +39,6 @@ class NotePolymorphicViewSet(ReadProtectedModelViewSet): Parse query and apply filters. :return: The filtered set of requested notes """ - self.request.session.setdefault("permission_mask", 42) queryset = self.queryset.filter(PermissionBackend.filter_queryset(self.request, Note, "view") | PermissionBackend.filter_queryset(self.request, NoteUser, "view") | PermissionBackend.filter_queryset(self.request, NoteClub, "view") @@ -204,6 +203,5 @@ class TransactionViewSet(ReadProtectedModelViewSet): ordering_fields = ['created_at', 'amount', ] def get_queryset(self): - self.request.session.setdefault("permission_mask", 42) return self.model.objects.filter(PermissionBackend.filter_queryset(self.request, self.model, "view"))\ .order_by("created_at", "id") diff --git a/apps/permission/backends.py b/apps/permission/backends.py index b4c08efd..7883b427 100644 --- a/apps/permission/backends.py +++ b/apps/permission/backends.py @@ -26,14 +26,31 @@ class PermissionBackend(ModelBackend): @staticmethod @memoize - def get_raw_permissions(user, t): + def get_raw_permissions(request, t): """ Query permissions of a certain type for a user, then memoize it. - :param user: The owner of the permissions + :param request: The current request :param t: The type of the permissions: view, change, add or delete :return: The queryset of the permissions of the user (memoized) grouped by clubs """ - if not user.is_authenticated: + if hasattr(request, 'auth') and request.auth is not None and hasattr(request.auth, 'scope'): + # OAuth2 Authentication + user = request.auth.user + + def permission_filter(membership_obj): + query = Q(pk=-1) + for scope in request.auth.scope.split(' '): + permission_id, club_id = scope.split('_') + if int(club_id) == membership_obj.club_id: + query |= Q(pk=permission_id) + return query + else: + user = request.user + + def permission_filter(membership_obj): + return Q(mask__rank__lte=request.session.get("permission_mask", -1)) + + if user.is_anonymous: # Unauthenticated users have no permissions return Permission.objects.none() @@ -43,8 +60,7 @@ class PermissionBackend(ModelBackend): for membership in memberships: for role in membership.roles.all(): - for perm in role.permissions.filter( - type=t, mask__rank__lte=get_current_request().session.get("permission_mask", -1)).all(): + for perm in role.permissions.filter(permission_filter(membership), type=t).all(): if not perm.permanent: if membership.date_start > date.today() or membership.date_end < date.today(): continue @@ -53,16 +69,22 @@ class PermissionBackend(ModelBackend): return perms @staticmethod - def permissions(user, model, type): + def permissions(request, model, type): """ List all permissions of the given user that applies to a given model and a give type - :param user: The owner of the permissions + :param request: The current request :param model: The model that the permissions shoud apply :param type: The type of the permissions: view, change, add or delete :return: A generator of the requested permissions """ - for permission in PermissionBackend.get_raw_permissions(user, type): + if hasattr(request, 'auth') and request.auth is not None and hasattr(request.auth, 'scope'): + # OAuth2 Authentication + user = request.auth.user + else: + user = request.user + + for permission in PermissionBackend.get_raw_permissions(request, type): if not isinstance(model.model_class()(), permission.model.model_class()) or not permission.membership: continue @@ -98,13 +120,17 @@ class PermissionBackend(ModelBackend): :param field: The field of the model to test, if concerned :return: A query that corresponds to the filter to give to a queryset """ - user = request.user + if hasattr(request, 'auth') and request.auth is not None and hasattr(request.auth, 'scope'): + # OAuth2 Authentication + user = request.auth.user + else: + user = request.user - if user is None or not user.is_authenticated: - # Anonymous users can't do anything + if user is None or user.is_anonymous: + # Anonymous users can't do asetdefaultnything return Q(pk=-1) - if user.is_superuser and get_current_request().session.get("permission_mask", -1) >= 42: + if user.is_superuser and request.session.get("permission_mask", -1) >= 42: # Superusers have all rights return Q() @@ -113,7 +139,7 @@ class PermissionBackend(ModelBackend): # Never satisfied query = Q(pk=-1) - perms = PermissionBackend.permissions(user, model, t) + perms = PermissionBackend.permissions(request, model, t) for perm in perms: if perm.field and field != perm.field: continue @@ -134,12 +160,15 @@ class PermissionBackend(ModelBackend): (e.g. for a transaction, the balance of the user could change) """ user_obj = request.user - - if user_obj is None or not user_obj.is_authenticated: - return False - sess = request.session + if hasattr(request, 'auth') and request.auth is not None and hasattr(request.auth, 'scope'): + # OAuth2 Authentication + user_obj = request.auth.user + + if user_obj is None or user_obj.is_anonymous: + return False + if user_obj.is_superuser and sess.get("permission_mask", -1) >= 42: return True @@ -152,7 +181,7 @@ class PermissionBackend(ModelBackend): ct = ContentType.objects.get_for_model(obj) if any(permission.applies(obj, perm_type, perm_field) - for permission in PermissionBackend.permissions(user_obj, ct, perm_type)): + for permission in PermissionBackend.permissions(request, ct, perm_type)): return True return False @@ -167,4 +196,4 @@ class PermissionBackend(ModelBackend): def get_all_permissions(self, user_obj, obj=None): ct = ContentType.objects.get_for_model(obj) - return list(self.permissions(user_obj, ct, "view")) + return list(self.permissions(get_current_request(), ct, "view")) diff --git a/apps/permission/signals.py b/apps/permission/signals.py index 2e4d6ea9..78d0b8f9 100644 --- a/apps/permission/signals.py +++ b/apps/permission/signals.py @@ -16,6 +16,9 @@ EXCLUDED = [ 'contenttypes.contenttype', 'logs.changelog', 'migrations.migration', + 'oauth2_provider.accesstoken', + 'oauth2_provider.grant', + 'oauth2_provider.refreshtoken', 'sessions.session', ]