Add unit tests to check the issues found in the bug bounty report
This commit is contained in:
parent
79cd70cbd8
commit
338e4cfcfc
|
|
@ -1,4 +1,5 @@
|
|||
import graphene
|
||||
from graphql_relay import to_global_id
|
||||
|
||||
|
||||
class HiddenForMixin:
|
||||
|
|
@ -18,3 +19,12 @@ class VisibleForMixin:
|
|||
|
||||
class HiddenAndVisibleForMixin(HiddenForMixin, VisibleForMixin):
|
||||
pass
|
||||
|
||||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -3,17 +3,9 @@ from django.db import models
|
|||
from django_extensions.db.models import TitleSlugDescriptionModel
|
||||
from graphql_relay import to_global_id
|
||||
|
||||
from core.mixins import GraphqlNodeMixin
|
||||
from users.models import User
|
||||
|
||||
|
||||
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)
|
||||
appearance = models.CharField(blank=True, null=False, max_length=255)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from graphene_django import DjangoObjectType
|
|||
from api.utils import get_by_id_or_slug
|
||||
from portfolio.models import Project, ProjectEntry
|
||||
from users.models import Role, UserRole, User
|
||||
from users.schema import PublicUserNode
|
||||
|
||||
|
||||
class ProjectEntryNode(DjangoObjectType):
|
||||
|
|
@ -19,6 +20,7 @@ class ProjectNode(DjangoObjectType):
|
|||
pk = graphene.Int()
|
||||
entries_count = graphene.Int()
|
||||
entries = graphene.List(ProjectEntryNode)
|
||||
owner = graphene.Field(PublicUserNode)
|
||||
|
||||
class Meta:
|
||||
model = Project
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ 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 portfolio.factories import ProjectFactory
|
||||
from users.factories import SchoolClassFactory
|
||||
from users.models import User
|
||||
|
|
@ -10,7 +11,7 @@ from users.services import create_users
|
|||
from api.test_utils import create_client, DefaultUserTestCase
|
||||
from portfolio.models import Project
|
||||
|
||||
class ProjectQuery(TestCase):
|
||||
class ProjectQuery(SkillboxTestCase):
|
||||
def setUp(self):
|
||||
create_users()
|
||||
self.teacher = User.objects.get(username='teacher')
|
||||
|
|
@ -49,7 +50,6 @@ class ProjectQuery(TestCase):
|
|||
self.assertEqual(Project.objects.count(), 0)
|
||||
|
||||
def test_should_not_be_able_to_delete_other_projects(self):
|
||||
|
||||
self.assertEqual(Project.objects.count(), 1)
|
||||
request = RequestFactory().get('/')
|
||||
request.user = self.student2
|
||||
|
|
@ -58,6 +58,41 @@ class ProjectQuery(TestCase):
|
|||
result = self.client.execute(self.mutation, variables=self.variables)
|
||||
self.assertEqual(result.get('errors')[0]['message'], 'Permission denied: Incorrect project')
|
||||
|
||||
def test_should_not_be_able_to_edit_other_projects(self):
|
||||
self.assertEqual(Project.objects.count(), 1)
|
||||
request = RequestFactory().get('/')
|
||||
request.user = self.student2
|
||||
self.client = Client(schema=schema, context_value=request)
|
||||
mutation = '''
|
||||
mutation UpdateProjectMutation($input: UpdateProjectInput!){
|
||||
updateProject(input: $input) {
|
||||
project {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
'''
|
||||
# project:
|
||||
# {
|
||||
# title: String
|
||||
# description: String
|
||||
# objectives: String
|
||||
# appearance: String
|
||||
# id: ID!
|
||||
# final: Boolean
|
||||
# }
|
||||
input = {
|
||||
'project': {
|
||||
'id': self.project1.graphql_id,
|
||||
'title': 'BAD! THIS IS BAD!'
|
||||
}
|
||||
}
|
||||
result = self.get_client(self.student2).get_result(mutation, variables={
|
||||
'input': input
|
||||
})
|
||||
self.assertIsNotNone(result.errors)
|
||||
self.assertTrue('Permission' in result.errors)
|
||||
|
||||
|
||||
class ProjectMutationsTestCase(DefaultUserTestCase):
|
||||
def test_add_project(self):
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
from graphql_relay import to_global_id
|
||||
|
||||
from core.tests.base_test import SkillboxTestCase
|
||||
from portfolio.factories import ProjectFactory
|
||||
from portfolio.models import Project
|
||||
|
|
@ -13,7 +15,7 @@ query ProjectQuery($id: ID!) {
|
|||
"""
|
||||
|
||||
|
||||
class ProjectQueryTestCaswe(SkillboxTestCase):
|
||||
class ProjectQueryTestCase(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
|
||||
|
|
@ -30,6 +32,7 @@ class ProjectQueryTestCaswe(SkillboxTestCase):
|
|||
school_class2 = SchoolClassFactory(users=[self.teacher2, self.student2])
|
||||
|
||||
self.project1 = ProjectFactory(student=self.student1)
|
||||
self.project1_id = to_global_id('ProjectNode', self.project1.id)
|
||||
self.query = '''
|
||||
query ProjectsQuery {
|
||||
projects {
|
||||
|
|
@ -113,3 +116,19 @@ class ProjectQueryTestCaswe(SkillboxTestCase):
|
|||
self._test_direct_project_access(self.teacher2, False)
|
||||
# non-owner can't access project
|
||||
self._test_direct_project_access(self.student2, False)
|
||||
|
||||
def test_project_owner(self):
|
||||
query = """
|
||||
query ProjectQuery($id: ID!) {
|
||||
project(id: $id) {
|
||||
id
|
||||
owner {
|
||||
email
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
result = self.get_client(self.student1).get_result(query, variables={
|
||||
'id': self.project1.graphql_id
|
||||
})
|
||||
self.assertIsNotNone(result.errors)
|
||||
|
|
|
|||
|
|
@ -5,10 +5,11 @@ from wagtail.core.fields import StreamField
|
|||
|
||||
from books.blocks import DocumentBlock, ImageUrlBlock, LinkBlock, VideoBlock
|
||||
from books.models import TextBlock
|
||||
from core.mixins import GraphqlNodeMixin
|
||||
from users.models import SchoolClass
|
||||
|
||||
|
||||
class Room(TitleSlugDescriptionModel):
|
||||
class Room(TitleSlugDescriptionModel, GraphqlNodeMixin):
|
||||
class Meta:
|
||||
verbose_name = 'Raum'
|
||||
verbose_name_plural = 'Räume'
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ from graphql_relay import from_global_id
|
|||
|
||||
from core.tests.base_test import SkillboxTestCase
|
||||
from core.tests.helpers import GQLResult
|
||||
from rooms.models import Room
|
||||
|
||||
|
||||
class GQLRoom:
|
||||
def __init__(self, room_data):
|
||||
|
|
@ -30,29 +32,30 @@ class AddRoomResult:
|
|||
class NewRoomMutationTestCase(SkillboxTestCase):
|
||||
def setUp(self) -> None:
|
||||
self.createDefault()
|
||||
self.mutation = """
|
||||
mutation AddRoom($input: AddRoomInput!){
|
||||
addRoom(input: $input) {
|
||||
room {
|
||||
id
|
||||
slug
|
||||
title
|
||||
entryCount
|
||||
appearance
|
||||
description
|
||||
schoolClass {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
def test_create_new_room(self):
|
||||
mutation = """
|
||||
mutation AddRoom($input: AddRoomInput!){
|
||||
addRoom(input: $input) {
|
||||
room {
|
||||
id
|
||||
slug
|
||||
title
|
||||
entryCount
|
||||
appearance
|
||||
description
|
||||
schoolClass {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
self.assertEqual(Room.objects.count(), 0)
|
||||
title = 'some title'
|
||||
appearance='blue'
|
||||
res = self.get_client().execute(mutation, variables={
|
||||
res = self.get_client().execute(self.mutation, variables={
|
||||
'input': {
|
||||
'room': {
|
||||
'title': title,
|
||||
|
|
@ -68,5 +71,23 @@ mutation AddRoom($input: AddRoomInput!){
|
|||
self.assertEqual(room.appearance, appearance)
|
||||
self.assertIsNone(room.description)
|
||||
self.assertEqual(int(from_global_id(room.school_class.get('id'))[1]), self.teacher.selected_class.id)
|
||||
self.assertEqual(Room.objects.count(), 1)
|
||||
|
||||
def test_create_new_room_for_other_school_class(self):
|
||||
self.assertEqual(Room.objects.count(), 0)
|
||||
result = self.get_client(self.teacher2).get_result(self.mutation, variables={
|
||||
'input': {
|
||||
'room': {
|
||||
'title': 'BIG NO NO!',
|
||||
# description
|
||||
'schoolClass': {
|
||||
'id': self.school_class.graphql_id
|
||||
},
|
||||
'appearance': 'red'
|
||||
}
|
||||
}
|
||||
})
|
||||
self.assertIsNotNone(result.errors)
|
||||
self.assertTrue('Permission' in result.errors)
|
||||
self.assertEqual(Room.objects.count(), 0)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,12 +4,13 @@ from graphql_relay import to_global_id
|
|||
|
||||
from api.schema import schema
|
||||
from core.factories import UserFactory
|
||||
from core.tests.base_test import SkillboxTestCase
|
||||
from rooms.factories import RoomEntryFactory, RoomFactory
|
||||
from rooms.models import RoomEntry
|
||||
from users.factories import SchoolClassFactory
|
||||
|
||||
|
||||
class RoomEntryMutationsTestCase(TestCase):
|
||||
class RoomEntryMutationsTestCase(SkillboxTestCase):
|
||||
def setUp(self):
|
||||
self.user = UserFactory(username='aschi')
|
||||
self.another_user = UserFactory(username='pesche')
|
||||
|
|
@ -17,6 +18,8 @@ class RoomEntryMutationsTestCase(TestCase):
|
|||
s = SchoolClassFactory(users=[self.user, self.another_user])
|
||||
s2 = SchoolClassFactory(users=[self.yet_another_user])
|
||||
self.room_entry = RoomEntryFactory(author=self.user, room=RoomFactory(school_class=s))
|
||||
self.room = self.room_entry.room
|
||||
self.first_school_class = s
|
||||
|
||||
request = RequestFactory().get('/')
|
||||
request.user = self.user
|
||||
|
|
@ -135,3 +138,47 @@ class RoomEntryMutationsTestCase(TestCase):
|
|||
entry = RoomEntry.objects.get(pk=self.room_entry.pk)
|
||||
self.assertIsNotNone(result.get('errors'))
|
||||
self.assertEqual(entry.title, self.room_entry.title)
|
||||
|
||||
def test_add_room_entry_not_owner_from_other_class(self):
|
||||
self.assertEqual(RoomEntry.objects.count(), 1)
|
||||
mutation = """
|
||||
fragment RoomEntryParts on RoomEntryNode {
|
||||
id
|
||||
slug
|
||||
title
|
||||
contents
|
||||
author {
|
||||
id
|
||||
firstName
|
||||
lastName
|
||||
avatarUrl
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mutation AddRoomEntry($input: AddRoomEntryInput!){
|
||||
addRoomEntry(input: $input) {
|
||||
roomEntry {
|
||||
...RoomEntryParts
|
||||
}
|
||||
errors
|
||||
|
||||
}
|
||||
}
|
||||
"""
|
||||
# input:
|
||||
# title = graphene.String(required=True)
|
||||
# contents = graphene.List(ContentElementInput)
|
||||
# room = graphene.ID(required=True)
|
||||
room_entry = {
|
||||
'title': 'Bad Actor!',
|
||||
'room': self.room.graphql_id
|
||||
}
|
||||
|
||||
result = self.get_client(self.yet_another_user).get_result(mutation, variables={
|
||||
'input': {
|
||||
'roomEntry': room_entry
|
||||
}
|
||||
})
|
||||
self.assertIsNotNone(result.errors)
|
||||
self.assertTrue('Permission' in result.errors)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ from django.utils.functional import cached_property
|
|||
from django.utils.timezone import is_aware, make_aware
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from core.mixins import GraphqlNodeMixin
|
||||
from users.licenses import MYSKILLBOX_LICENSES
|
||||
from users.managers import LicenseManager, RoleManager, UserManager, UserRoleManager
|
||||
|
||||
|
|
@ -176,7 +177,7 @@ class Team(GroupWithCode):
|
|||
return self.name
|
||||
|
||||
|
||||
class SchoolClass(GroupWithCode):
|
||||
class SchoolClass(GroupWithCode, GraphqlNodeMixin):
|
||||
users = models.ManyToManyField(get_user_model(), related_name='school_classes', blank=True,
|
||||
through='users.SchoolClassMember')
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue