Add more access test for projects

This commit is contained in:
Ramon Wenger 2021-10-06 10:54:52 +02:00
parent 36268581f9
commit bb694e443d
4 changed files with 74 additions and 15 deletions

View File

@ -2,9 +2,15 @@ from django.test import TestCase, RequestFactory
from graphene.test import Client from graphene.test import Client
from api.schema import schema from api.schema import schema
from core.tests.helpers import GQLResult
from users.models import SchoolClass, User from users.models import SchoolClass, User
from users.services import create_users from users.services import create_users
class GQLClient(Client):
def get_result(self, *args, **kwargs):
return GQLResult(self.execute(*args, **kwargs))
class SkillboxTestCase(TestCase): class SkillboxTestCase(TestCase):
def createDefault(self) -> None: def createDefault(self) -> None:
@ -22,4 +28,6 @@ class SkillboxTestCase(TestCase):
if user is None: if user is None:
user = self.teacher user = self.teacher
request.user = user request.user = user
return Client(schema=schema, context_value=request) return GQLClient(schema=schema, context_value=request)

View File

@ -1,9 +1,20 @@
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.db import models from django.db import models
from django_extensions.db.models import TitleSlugDescriptionModel from django_extensions.db.models import TitleSlugDescriptionModel
from graphql_relay import to_global_id
from users.models import User
class Project(TitleSlugDescriptionModel): class GraphqlNodeMixin:
def default_node_name(self):
return f'{self.__class__.__name__}Node'
@property
def graphql_id(self):
return to_global_id(self.default_node_name(), self.id)
class Project(TitleSlugDescriptionModel, GraphqlNodeMixin):
objectives = models.TextField(blank=True) objectives = models.TextField(blank=True)
appearance = models.CharField(blank=True, null=False, max_length=255) appearance = models.CharField(blank=True, null=False, max_length=255)
student = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, related_name='projects') student = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, related_name='projects')
@ -13,6 +24,10 @@ class Project(TitleSlugDescriptionModel):
def __str__(self): def __str__(self):
return self.title return self.title
def is_viewable_by(self, user: User):
return user.id == self.student.id or (
self.final and self.student.get_teacher().id == user.id
)
class ProjectEntry(models.Model): class ProjectEntry(models.Model):
activity = models.TextField(blank=True) activity = models.TextField(blank=True)

View File

@ -5,7 +5,7 @@ from graphene_django import DjangoObjectType
from api.utils import get_by_id_or_slug from api.utils import get_by_id_or_slug
from portfolio.models import Project, ProjectEntry from portfolio.models import Project, ProjectEntry
from users.models import Role, UserRole from users.models import Role, UserRole, User
class ProjectEntryNode(DjangoObjectType): class ProjectEntryNode(DjangoObjectType):
@ -52,4 +52,9 @@ class PortfolioQuery(object):
return Project.objects.filter(student=user) return Project.objects.filter(student=user)
def resolve_project(self, info, **kwargs): def resolve_project(self, info, **kwargs):
return get_by_id_or_slug(Project, **kwargs) user = info.context.user # type: User
project = get_by_id_or_slug(Project, **kwargs) #type: Project
if project.is_viewable_by(user):
return project
return None

View File

@ -1,18 +1,29 @@
from django.test import TestCase, RequestFactory
from graphene.test import Client
from graphql_relay import to_global_id
from api.schema import schema
from core.tests.base_test import SkillboxTestCase from core.tests.base_test import SkillboxTestCase
from portfolio.factories import ProjectFactory from portfolio.factories import ProjectFactory
from portfolio.models import Project from portfolio.models import Project
from rooms.models import Room
from users.factories import SchoolClassFactory from users.factories import SchoolClassFactory
from users.models import User, SchoolClass from users.models import User
from users.services import create_users
project_query = """
query ProjectQuery($id: ID!) {
project(id: $id) {
id
}
}
"""
class ProjectQuery(SkillboxTestCase): class ProjectQueryTestCaswe(SkillboxTestCase):
def _test_direct_project_access(self, user: User, should_have_access: bool):
result = self.get_client(user).get_result(project_query, variables={
'id': self.project1.graphql_id
})
self.assertIsNone(result.errors)
if should_have_access:
self.assertEqual(result.data.get('project').get('id'), self.project1.graphql_id)
else:
self.assertIsNone(result.data.get('project'))
def setUp(self): def setUp(self):
self.createDefault() self.createDefault()
school_class1 = SchoolClassFactory(users=[self.teacher, self.student1]) school_class1 = SchoolClassFactory(users=[self.teacher, self.student1])
@ -71,14 +82,34 @@ class ProjectQuery(SkillboxTestCase):
self.assertEqual(result.get('data').get('projects')[0].get('title'), self.assertEqual(result.get('data').get('projects')[0].get('title'),
self.project1.title) self.project1.title)
def test_other_teacher_should_not_see_projects(self): def test_other_teacher_should_not_see_projects(self):
self.project1.final = True self.project1.final = True
self.project1.save() self.project1.save()
self.assertEqual(Project.objects.count(), 1) self.assertEqual(Project.objects.count(), 1)
result = self.get_client(self.teacher2).execute(self.query) result = self.get_client(self.teacher2).execute(self.query)
self.assertIsNone(result.get('errors')) self.assertIsNone(result.get('errors'))
self.assertEqual(len(result.get('data').get('projects')), 0) self.assertEqual(len(result.get('data').get('projects')), 0)
def test_direct_project_access(self):
# student can access own project directly
self._test_direct_project_access(self.student1, True)
# teacher can't access project, as it's not final
self._test_direct_project_access(self.teacher, False)
self._test_direct_project_access(self.teacher2, False)
# non-owner can't access project
self._test_direct_project_access(self.student2, False)
def test_direct_final_project_access(self):
self.project1.final = True
self.project1.save()
# student can access own project directly
self._test_direct_project_access(self.student1, True)
# teacher of student can access project, as it's final
self._test_direct_project_access(self.teacher, True)
# other teacher can't access project, as it's not final
self._test_direct_project_access(self.teacher2, False)
# non-owner can't access project
self._test_direct_project_access(self.student2, False)