mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Fix Editor filter and view permissions
This commit is contained in:
parent
f4c2731b72
commit
93d56ef4fa
|
@ -1,5 +1,5 @@
|
||||||
''' Custom Permission classes.
|
''' Custom Permission classes.
|
||||||
Hierarchy: Anonymous -> User -> Editor -> Owner -> Admin
|
Hierarchy: Anyone -> User -> Editor -> Owner -> Admin
|
||||||
'''
|
'''
|
||||||
from typing import Any, cast
|
from typing import Any, cast
|
||||||
|
|
||||||
|
@ -66,6 +66,16 @@ class ItemEditor(ItemOwner):
|
||||||
return super().has_object_permission(request, view, obj)
|
return super().has_object_permission(request, view, obj)
|
||||||
|
|
||||||
|
|
||||||
|
class ItemAnyone(ItemEditor):
|
||||||
|
''' Item permission: Anyone if public. '''
|
||||||
|
|
||||||
|
def has_object_permission(self, request: Request, view: APIView, obj: Any) -> bool:
|
||||||
|
item = _extract_item(obj)
|
||||||
|
if item.access_policy == m.AccessPolicy.PUBLIC:
|
||||||
|
return True
|
||||||
|
return super().has_object_permission(request, view, obj)
|
||||||
|
|
||||||
|
|
||||||
class EditorMixin(APIView):
|
class EditorMixin(APIView):
|
||||||
''' Editor permissions mixin for API views. '''
|
''' Editor permissions mixin for API views. '''
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,9 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
||||||
'load_trs', 'cst_create', 'cst_delete_multiple',
|
'load_trs', 'cst_create', 'cst_delete_multiple',
|
||||||
'reset_aliases', 'cst_rename', 'cst_substitute'
|
'reset_aliases', 'cst_rename', 'cst_substitute'
|
||||||
]:
|
]:
|
||||||
permission_list = [permissions.ItemOwner]
|
permission_list = [permissions.ItemEditor]
|
||||||
|
elif self.action in ['contents', 'details', 'export_trs', 'resolve', 'check']:
|
||||||
|
permission_list = [permissions.ItemAnyone]
|
||||||
else:
|
else:
|
||||||
permission_list = [permissions.Anyone]
|
permission_list = [permissions.Anyone]
|
||||||
return [permission() for permission in permission_list]
|
return [permission() for permission in permission_list]
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.contrib.auth import authenticate
|
||||||
from django.contrib.auth.password_validation import validate_password
|
from django.contrib.auth.password_validation import validate_password
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from apps.rsform.models import Subscription
|
from apps.rsform.models import Editor, Subscription
|
||||||
|
|
||||||
from . import messages as msg
|
from . import messages as msg
|
||||||
from . import models
|
from . import models
|
||||||
|
@ -69,14 +69,16 @@ class AuthSerializer(serializers.Serializer):
|
||||||
'id': None,
|
'id': None,
|
||||||
'username': '',
|
'username': '',
|
||||||
'is_staff': False,
|
'is_staff': False,
|
||||||
'subscriptions': []
|
'subscriptions': [],
|
||||||
|
'editor': []
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
return {
|
return {
|
||||||
'id': instance.pk,
|
'id': instance.pk,
|
||||||
'username': instance.username,
|
'username': instance.username,
|
||||||
'is_staff': instance.is_staff,
|
'is_staff': instance.is_staff,
|
||||||
'subscriptions': [sub.item.pk for sub in Subscription.objects.filter(user=instance)]
|
'subscriptions': [sub.item.pk for sub in Subscription.objects.filter(user=instance)],
|
||||||
|
'editor': [edit.item.pk for edit in Editor.objects.filter(editor=instance)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ class TestUserAPIViews(EndpointTester):
|
||||||
self.assertEqual(response.data['username'], self.user.username)
|
self.assertEqual(response.data['username'], self.user.username)
|
||||||
self.assertEqual(response.data['is_staff'], self.user.is_staff)
|
self.assertEqual(response.data['is_staff'], self.user.is_staff)
|
||||||
self.assertEqual(response.data['subscriptions'], [])
|
self.assertEqual(response.data['subscriptions'], [])
|
||||||
|
self.assertEqual(response.data['editor'], [])
|
||||||
|
|
||||||
self.logout()
|
self.logout()
|
||||||
response = self.executeOK()
|
response = self.executeOK()
|
||||||
|
@ -51,6 +52,7 @@ class TestUserAPIViews(EndpointTester):
|
||||||
self.assertEqual(response.data['username'], '')
|
self.assertEqual(response.data['username'], '')
|
||||||
self.assertEqual(response.data['is_staff'], False)
|
self.assertEqual(response.data['is_staff'], False)
|
||||||
self.assertEqual(response.data['subscriptions'], [])
|
self.assertEqual(response.data['subscriptions'], [])
|
||||||
|
self.assertEqual(response.data['editor'], [])
|
||||||
|
|
||||||
|
|
||||||
class TestUserUserProfileAPIView(EndpointTester):
|
class TestUserUserProfileAPIView(EndpointTester):
|
||||||
|
|
|
@ -112,8 +112,10 @@ class UpdatePassword(views.APIView):
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
old_password = serializer.data.get("old_password")
|
old_password = serializer.data.get("old_password")
|
||||||
if not self.object.check_password(old_password):
|
if not self.object.check_password(old_password):
|
||||||
return Response({"old_password": ["Wrong password."]},
|
return Response(
|
||||||
status=c.HTTP_400_BAD_REQUEST)
|
{"old_password": ["Wrong password."]},
|
||||||
|
status=c.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
# Note: set_password also hashes the password that the user will get
|
# Note: set_password also hashes the password that the user will get
|
||||||
self.object.set_password(serializer.data.get("new_password"))
|
self.object.set_password(serializer.data.get("new_password"))
|
||||||
self.object.save()
|
self.object.save()
|
||||||
|
|
|
@ -172,7 +172,7 @@ DATABASES = {
|
||||||
SPECTACULAR_SETTINGS = {
|
SPECTACULAR_SETTINGS = {
|
||||||
'TITLE': 'ConceptPortal API',
|
'TITLE': 'ConceptPortal API',
|
||||||
'DESCRIPTION': 'Портал для работы с экспликациями концептуальных схем',
|
'DESCRIPTION': 'Портал для работы с экспликациями концептуальных схем',
|
||||||
'VERSION': '0.1.1',
|
'VERSION': '0.1.2',
|
||||||
'SERVE_INCLUDE_SCHEMA': False,
|
'SERVE_INCLUDE_SCHEMA': False,
|
||||||
|
|
||||||
'COMPONENT_SPLIT_PATCH': True,
|
'COMPONENT_SPLIT_PATCH': True,
|
||||||
|
|
|
@ -81,7 +81,7 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
||||||
result = result.filter(item => filter.isSubscribed == user?.subscriptions.includes(item.id));
|
result = result.filter(item => filter.isSubscribed == user?.subscriptions.includes(item.id));
|
||||||
}
|
}
|
||||||
if (filter.isEditor !== undefined) {
|
if (filter.isEditor !== undefined) {
|
||||||
// TODO: load editors from backend
|
result = result.filter(item => filter.isEditor == user?.editor.includes(item.id));
|
||||||
}
|
}
|
||||||
if (filter.query) {
|
if (filter.query) {
|
||||||
result = result.filter(item => matchLibraryItem(item, filter.query!));
|
result = result.filter(item => matchLibraryItem(item, filter.query!));
|
||||||
|
|
|
@ -27,6 +27,7 @@ export interface IUser {
|
||||||
*/
|
*/
|
||||||
export interface ICurrentUser extends Pick<IUser, 'id' | 'username' | 'is_staff'> {
|
export interface ICurrentUser extends Pick<IUser, 'id' | 'username' | 'is_staff'> {
|
||||||
subscriptions: LibraryItemID[];
|
subscriptions: LibraryItemID[];
|
||||||
|
editor: LibraryItemID[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -262,7 +262,7 @@ export function labelLocationHead(head: LocationHead): string {
|
||||||
switch (head) {
|
switch (head) {
|
||||||
case LocationHead.USER: return 'личные (/U)';
|
case LocationHead.USER: return 'личные (/U)';
|
||||||
case LocationHead.COMMON: return 'общие (/S)';
|
case LocationHead.COMMON: return 'общие (/S)';
|
||||||
case LocationHead.LIBRARY: return 'неизменные (/L)';
|
case LocationHead.LIBRARY: return 'примеры (/L)';
|
||||||
case LocationHead.PROJECTS: return 'проекты (/P)';
|
case LocationHead.PROJECTS: return 'проекты (/P)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -275,7 +275,7 @@ export function describeLocationHead(head: LocationHead): string {
|
||||||
switch (head) {
|
switch (head) {
|
||||||
case LocationHead.USER: return 'Личные схемы пользователя';
|
case LocationHead.USER: return 'Личные схемы пользователя';
|
||||||
case LocationHead.COMMON: return 'Рабочий каталог публичных схем';
|
case LocationHead.COMMON: return 'Рабочий каталог публичных схем';
|
||||||
case LocationHead.LIBRARY: return 'Каталог неизменных схем';
|
case LocationHead.LIBRARY: return 'Каталог неизменных схем-примеров';
|
||||||
case LocationHead.PROJECTS: return 'Рабочий каталог проектных схем';
|
case LocationHead.PROJECTS: return 'Рабочий каталог проектных схем';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user