mirror of https://gitlab.crans.org/bde/nk20
Permissions support fully OAuth2 scopes
Signed-off-by: Yohann D'ANELLO <ynerant@crans.org>
This commit is contained in:
parent
ea092803d7
commit
8be16e7b58
|
@ -24,7 +24,6 @@ class ReadProtectedModelViewSet(ModelViewSet):
|
||||||
self.model = ContentType.objects.get_for_model(self.serializer_class.Meta.model).model_class()
|
self.model = ContentType.objects.get_for_model(self.serializer_class.Meta.model).model_class()
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
self.request.session.setdefault("permission_mask", 42)
|
|
||||||
return self.queryset.filter(PermissionBackend.filter_queryset(self.request, self.model, "view")).distinct()
|
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()
|
self.model = ContentType.objects.get_for_model(self.serializer_class.Meta.model).model_class()
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
self.request.session.setdefault("permission_mask", 42)
|
|
||||||
return self.queryset.filter(PermissionBackend.filter_queryset(self.request, self.model, "view")).distinct()
|
return self.queryset.filter(PermissionBackend.filter_queryset(self.request, self.model, "view")).distinct()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,7 @@ def save_object(sender, instance, **kwargs):
|
||||||
ip = request.META.get('REMOTE_ADDR')
|
ip = request.META.get('REMOTE_ADDR')
|
||||||
|
|
||||||
if not user.is_authenticated:
|
if not user.is_authenticated:
|
||||||
# For registration purposes
|
# For registration and OAuth2 purposes
|
||||||
user = None
|
user = None
|
||||||
|
|
||||||
# noinspection PyProtectedMember
|
# noinspection PyProtectedMember
|
||||||
|
@ -160,6 +160,10 @@ def delete_object(sender, instance, **kwargs):
|
||||||
else:
|
else:
|
||||||
ip = request.META.get('REMOTE_ADDR')
|
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
|
# On crée notre propre sérialiseur JSON pour pouvoir sauvegarder les modèles
|
||||||
class CustomSerializer(ModelSerializer):
|
class CustomSerializer(ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -39,7 +39,6 @@ class NotePolymorphicViewSet(ReadProtectedModelViewSet):
|
||||||
Parse query and apply filters.
|
Parse query and apply filters.
|
||||||
:return: The filtered set of requested notes
|
: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")
|
queryset = self.queryset.filter(PermissionBackend.filter_queryset(self.request, Note, "view")
|
||||||
| PermissionBackend.filter_queryset(self.request, NoteUser, "view")
|
| PermissionBackend.filter_queryset(self.request, NoteUser, "view")
|
||||||
| PermissionBackend.filter_queryset(self.request, NoteClub, "view")
|
| PermissionBackend.filter_queryset(self.request, NoteClub, "view")
|
||||||
|
@ -204,6 +203,5 @@ class TransactionViewSet(ReadProtectedModelViewSet):
|
||||||
ordering_fields = ['created_at', 'amount', ]
|
ordering_fields = ['created_at', 'amount', ]
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
self.request.session.setdefault("permission_mask", 42)
|
|
||||||
return self.model.objects.filter(PermissionBackend.filter_queryset(self.request, self.model, "view"))\
|
return self.model.objects.filter(PermissionBackend.filter_queryset(self.request, self.model, "view"))\
|
||||||
.order_by("created_at", "id")
|
.order_by("created_at", "id")
|
||||||
|
|
|
@ -26,14 +26,31 @@ class PermissionBackend(ModelBackend):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@memoize
|
@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.
|
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
|
: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
|
: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
|
# Unauthenticated users have no permissions
|
||||||
return Permission.objects.none()
|
return Permission.objects.none()
|
||||||
|
|
||||||
|
@ -43,8 +60,7 @@ class PermissionBackend(ModelBackend):
|
||||||
|
|
||||||
for membership in memberships:
|
for membership in memberships:
|
||||||
for role in membership.roles.all():
|
for role in membership.roles.all():
|
||||||
for perm in role.permissions.filter(
|
for perm in role.permissions.filter(permission_filter(membership), type=t).all():
|
||||||
type=t, mask__rank__lte=get_current_request().session.get("permission_mask", -1)).all():
|
|
||||||
if not perm.permanent:
|
if not perm.permanent:
|
||||||
if membership.date_start > date.today() or membership.date_end < date.today():
|
if membership.date_start > date.today() or membership.date_end < date.today():
|
||||||
continue
|
continue
|
||||||
|
@ -53,16 +69,22 @@ class PermissionBackend(ModelBackend):
|
||||||
return perms
|
return perms
|
||||||
|
|
||||||
@staticmethod
|
@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
|
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 model: The model that the permissions shoud apply
|
||||||
:param type: The type of the permissions: view, change, add or delete
|
:param type: The type of the permissions: view, change, add or delete
|
||||||
:return: A generator of the requested permissions
|
: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:
|
if not isinstance(model.model_class()(), permission.model.model_class()) or not permission.membership:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@ -98,13 +120,17 @@ class PermissionBackend(ModelBackend):
|
||||||
:param field: The field of the model to test, if concerned
|
:param field: The field of the model to test, if concerned
|
||||||
:return: A query that corresponds to the filter to give to a queryset
|
:return: A query that corresponds to the filter to give to a queryset
|
||||||
"""
|
"""
|
||||||
|
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
|
user = request.user
|
||||||
|
|
||||||
if user is None or not user.is_authenticated:
|
if user is None or user.is_anonymous:
|
||||||
# Anonymous users can't do anything
|
# Anonymous users can't do asetdefaultnything
|
||||||
return Q(pk=-1)
|
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
|
# Superusers have all rights
|
||||||
return Q()
|
return Q()
|
||||||
|
|
||||||
|
@ -113,7 +139,7 @@ class PermissionBackend(ModelBackend):
|
||||||
|
|
||||||
# Never satisfied
|
# Never satisfied
|
||||||
query = Q(pk=-1)
|
query = Q(pk=-1)
|
||||||
perms = PermissionBackend.permissions(user, model, t)
|
perms = PermissionBackend.permissions(request, model, t)
|
||||||
for perm in perms:
|
for perm in perms:
|
||||||
if perm.field and field != perm.field:
|
if perm.field and field != perm.field:
|
||||||
continue
|
continue
|
||||||
|
@ -134,12 +160,15 @@ class PermissionBackend(ModelBackend):
|
||||||
(e.g. for a transaction, the balance of the user could change)
|
(e.g. for a transaction, the balance of the user could change)
|
||||||
"""
|
"""
|
||||||
user_obj = request.user
|
user_obj = request.user
|
||||||
|
|
||||||
if user_obj is None or not user_obj.is_authenticated:
|
|
||||||
return False
|
|
||||||
|
|
||||||
sess = request.session
|
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:
|
if user_obj.is_superuser and sess.get("permission_mask", -1) >= 42:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -152,7 +181,7 @@ class PermissionBackend(ModelBackend):
|
||||||
|
|
||||||
ct = ContentType.objects.get_for_model(obj)
|
ct = ContentType.objects.get_for_model(obj)
|
||||||
if any(permission.applies(obj, perm_type, perm_field)
|
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 True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -167,4 +196,4 @@ class PermissionBackend(ModelBackend):
|
||||||
|
|
||||||
def get_all_permissions(self, user_obj, obj=None):
|
def get_all_permissions(self, user_obj, obj=None):
|
||||||
ct = ContentType.objects.get_for_model(obj)
|
ct = ContentType.objects.get_for_model(obj)
|
||||||
return list(self.permissions(user_obj, ct, "view"))
|
return list(self.permissions(get_current_request(), ct, "view"))
|
||||||
|
|
|
@ -16,6 +16,9 @@ EXCLUDED = [
|
||||||
'contenttypes.contenttype',
|
'contenttypes.contenttype',
|
||||||
'logs.changelog',
|
'logs.changelog',
|
||||||
'migrations.migration',
|
'migrations.migration',
|
||||||
|
'oauth2_provider.accesstoken',
|
||||||
|
'oauth2_provider.grant',
|
||||||
|
'oauth2_provider.refreshtoken',
|
||||||
'sessions.session',
|
'sessions.session',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue