Add unit tests to check the issues found in the bug bounty report

This commit is contained in:
Ramon Wenger 2021-11-02 12:18:05 +01:00 committed by Christian Cueni
parent 79cd70cbd8
commit 338e4cfcfc
9 changed files with 162 additions and 34 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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):

View File

@ -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)

View File

@ -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'

View File

@ -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)

View File

@ -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)

View File

@ -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')