def test_access_api_can_manage_moderators_instructor_other_forum(self): """ If a user can manage another forum, he can't update moderators from a forum where is doesn't have this permission. """ update_user = UserFactory() api_user = UserFactory(lti_consumer=update_user.lti_consumer) lti_context = LTIContextFactory(lti_consumer=update_user.lti_consumer) forum = ForumFactory() forum.lti_contexts.add(lti_context) update_user.refresh_from_db() # Assign the permission assign_perm("can_manage_moderator", api_user, ForumFactory(), True) # Creates the session self.client.force_login(api_user, "ashley.auth.backend.LTIBackend") session = self.client.session session[SESSION_LTI_CONTEXT_ID] = lti_context.id session.save() action = random.choice( ["add_group_moderator", "remove_group_moderator"]) # Revoke user to moderator response = self.client.patch( f"/api/v1.0/users/{update_user.id}/{action}/", content_type="application/json", ) self.assertEqual(response.status_code, 403) self.assertEqual( response.json(), {"detail": "You do not have permission to perform this action."}, )
def test_forum_search_with_unautorized_forum_from_other_lti_context(self): """ Try to search in a forum that is not part of our LTIContext by submitting in the search form a forum from another LTIContext. """ user = UserFactory() lti_context = LTIContextFactory(lti_consumer=user.lti_consumer) lti_context2 = LTIContextFactory(lti_consumer=user.lti_consumer) forum = ForumFactory() forum2 = ForumFactory() forum.lti_contexts.add(lti_context) forum2.lti_contexts.add(lti_context2) PostFactory( topic=TopicFactory(forum=forum), text="Good morning world", ) PostFactory( topic=TopicFactory(forum=forum2), text="Hello world", ) # Index posts in Elasticsearch call_command("rebuild_index", interactive=False) # Connects and gets acces to the forum self.client.force_login(user, "ashley.auth.backend.LTIBackend") assign_perm("can_read_forum", user, forum) assign_perm("can_read_forum", user, forum2) session = self.client.session session[SESSION_LTI_CONTEXT_ID] = lti_context.id session.save() form = SearchForm(user=user, lti_context=lti_context) # Checks that only the forum that is allowed is proposed as choice self.assertEqual( form.fields["search_forums"].choices, [(forum.id, "{} {}".format("-" * forum.margin_level, forum.name))], ) # Despite that, we force the request on the forum that is not allowed response = self.client.get( f"/forum/search/?q=world&search_forums={forum2.id}") self.assertEqual(response.status_code, 200) # Controls that we get an error and the search is not executed self.assertContains( response, f"Select a valid choice. {forum2.id} is not one of the available choices.", html=True, ) # Valid request, we search on the forum that is allowed, we get only one result # as forum2 is ignored response = self.client.get( f"/forum/search/?q=world&search_forums={forum.id}") self.assertEqual(response.status_code, 200) self.assertContains(response, "Your search has returned <b>1</b> result", html=True)
def test_course_create_multiple_forums_listing_on(self): """ Instructor wants to lock the course that has multiple forums. The list of forums should be displayed before the confirmation's button. """ forum1 = self._connects("instructor") forum2 = ForumFactory(name="Forum2") forum3 = ForumFactory(name="Forum3") # connects user and add forums to the lti_context forum2 = self._connects("instructor", forum_uuid=forum2.lti_id) forum3 = self._connects("instructor", forum_uuid=forum3.lti_id) # go on the page to lock the course from the forum1 response = self.client.get(f"/forum/admin/lock-course/{forum1.id}/") self.assertEqual(200, response.status_code) # this listing is common for all the forums of the course forums_list = ( f'<p class="ml-3 pb-3"><strong>{forum1.name}</strong><p>' '<p class="ml-3 pb-3"><strong>Forum2</strong><p>' '<p class="ml-3 pb-3"><strong>Forum3</strong><p>') self.assertContains(response, forums_list) # go on the page to lock the course from the forum2 response = self.client.get(f"/forum/admin/lock-course/{forum2.id}/") self.assertEqual(200, response.status_code) self.assertContains(response, forums_list) # go on the page to lock the course from the forum3 response = self.client.get(f"/forum/admin/lock-course/{forum3.id}/") self.assertEqual(200, response.status_code) self.assertContains(response, forums_list)
def test_forum_list(self): user = UserFactory() lti_context_a = LTIContextFactory(lti_consumer=user.lti_consumer) lti_context_b = LTIContextFactory(lti_consumer=user.lti_consumer) # Create 2 forums for context A forum_a1 = ForumFactory(name="Forum A1") forum_a1.lti_contexts.add(lti_context_a) forum_a2 = ForumFactory(name="Forum A2") forum_a2.lti_contexts.add(lti_context_a) # Create 2 forums for context B forum_b1 = ForumFactory(name="Forum B1") forum_b1.lti_contexts.add(lti_context_b) forum_b2 = ForumFactory(name="Forum B2") forum_b2.lti_contexts.add(lti_context_b) # Grant read-only access for forums A1, A2 and B1 to our user assign_perm("can_see_forum", user, forum_a1, True) assign_perm("can_read_forum", user, forum_a1, True) assign_perm("can_see_forum", user, forum_a2, True) assign_perm("can_read_forum", user, forum_a2, True) assign_perm("can_see_forum", user, forum_b1, True) assign_perm("can_read_forum", user, forum_b1, True) self.client.force_login(user, "ashley.auth.backend.LTIBackend") session = self.client.session # Make a request to get the forum list, with an empty session response = self.client.get("/forum/") # We should see all forums we have access to self.assertContains(response, "Forum A1") self.assertContains(response, "Forum A2") self.assertContains(response, "Forum B1") self.assertNotContains(response, "Forum B2") # Update the client session to limit the user to the LTIContext A # Make a request to get he forum list again session[SESSION_LTI_CONTEXT_ID] = lti_context_a.id session.save() response = self.client.get("/forum/") # We should see only forums related to LTIContext A self.assertContains(response, "Forum A1") self.assertContains(response, "Forum A2") self.assertNotContains(response, "Forum B1") self.assertNotContains(response, "Forum B2") # Update the client session to limit the user to the LTIContext B # Make a request to get he forum list again session[SESSION_LTI_CONTEXT_ID] = lti_context_b.id session.save() response = self.client.get("/forum/") # We should see only forum B1 self.assertNotContains(response, "Forum A1") self.assertNotContains(response, "Forum A2") self.assertContains(response, "Forum B1") self.assertNotContains(response, "Forum B2")
def test_get_readable_forums(self): """ The get_readable_forums() function should filter the results according to the LTIContext of the user, if available. """ user = UserFactory() lti_context_a = LTIContextFactory(lti_consumer=user.lti_consumer) lti_context_b = LTIContextFactory(lti_consumer=user.lti_consumer) # Create 2 forums for context A forum_a1 = ForumFactory(name="Forum A1") forum_a1.lti_contexts.add(lti_context_a) forum_a2 = ForumFactory(name="Forum A2") forum_a2.lti_contexts.add(lti_context_a) # Create 2 forums for context B forum_b1 = ForumFactory(name="Forum B1") forum_b1.lti_contexts.add(lti_context_b) forum_b2 = ForumFactory(name="Forum B2") forum_b2.lti_contexts.add(lti_context_b) # Grant read-only access for forums A1, A2 and B1 to our user assign_perm("can_see_forum", user, forum_a1, True) assign_perm("can_read_forum", user, forum_a1, True) assign_perm("can_see_forum", user, forum_a2, True) assign_perm("can_read_forum", user, forum_a2, True) assign_perm("can_see_forum", user, forum_b1, True) assign_perm("can_read_forum", user, forum_b1, True) # Instantiate the permission Handler permission_handler = PermissionHandler() # When the permission handler has no lti context specified, # the get_readable_forums should return all forums the user # has access to forums_qs = Forum.objects.all() forums_list = list(Forum.objects.all()) readable_forums = permission_handler.get_readable_forums(forums_qs, user) self.assertCountEqual(readable_forums, [forum_a1, forum_a2, forum_b1]) # Inject a LTI context into the permission handler and ensure that # the results are filtered according to it permission_handler.current_lti_context_id = lti_context_a.id readable_forums = permission_handler.get_readable_forums(forums_qs, user) self.assertCountEqual(readable_forums, [forum_a1, forum_a2]) # Check the same with a list of forums instead of a QuerySet readable_forums = permission_handler.get_readable_forums(forums_list, user) self.assertCountEqual(readable_forums, [forum_a1, forum_a2]) # Inject another LTIContext into the permission handler permission_handler.current_lti_context_id = lti_context_b.id readable_forums = permission_handler.get_readable_forums(forums_qs, user) self.assertCountEqual(readable_forums, [forum_b1]) readable_forums = permission_handler.get_readable_forums(forums_list, user) self.assertCountEqual(readable_forums, [forum_b1])
def _get_url_list_forum_with_forums(self): """Creates four forums and a user to access the forum that has the permission to access it. It's a shortcut used in all the tests below. """ user = UserFactory() lti_context = LTIContextFactory(lti_consumer=user.lti_consumer) forum = ForumFactory(name="Z Letter") forum.lti_contexts.add(lti_context) # create 3 topics, and one topic with 10 posts topic = TopicFactory(forum=forum) for i in range(10): PostFactory(topic=topic) PostFactory(topic=TopicFactory(forum=forum)) PostFactory(topic=TopicFactory(forum=forum)) # create a forum with 2 topics forum2 = ForumFactory(name="A Letter") forum2.lti_contexts.add(lti_context) PostFactory(topic=TopicFactory(forum=forum2)) PostFactory(topic=TopicFactory(forum=forum2)) # create a forum with 1 topic and 5 messages forum3 = ForumFactory(name="Q Letter") forum3.lti_contexts.add(lti_context) topic3 = TopicFactory(forum=forum3) PostFactory(topic=topic3) PostFactory(topic=topic3) PostFactory(topic=topic3) PostFactory(topic=topic3) PostFactory(topic=topic3) PostFactory(topic=TopicFactory(forum=forum3)) PostFactory(topic=TopicFactory(forum=forum3)) PostFactory(topic=TopicFactory(forum=forum3)) # create a forum with no topic forum4 = ForumFactory(name="M Letter") forum4.lti_contexts.add(lti_context) # Grant access assign_perm("can_see_forum", user, forum, True) assign_perm("can_read_forum", user, forum, True) assign_perm("can_see_forum", user, forum2, True) assign_perm("can_read_forum", user, forum2, True) assign_perm("can_see_forum", user, forum3, True) assign_perm("can_read_forum", user, forum3, True) assign_perm("can_see_forum", user, forum4, True) assign_perm("can_read_forum", user, forum4, True) self.client.force_login(user, "ashley.auth.backend.LTIBackend") return forum, forum2, forum3, forum4
def test_browsing_with_can_manage_moderator_forum_permission(self): """ A user with the `can_manage_moderator_forum` permission and `SESSION_LTI_CONTEXT_ID` should be able to access it. """ user = UserFactory() lti_context = LTIContextFactory(lti_consumer=user.lti_consumer) forum = ForumFactory() forum.lti_contexts.add(lti_context) assign_perm("can_manage_moderator", user, forum, True) self.client.force_login(user, "ashley.auth.backend.LTIBackend") response = self.client.get("/moderators/") # Controls the page is forbidden as session is not set self.assertEqual(403, response.status_code) session = self.client.session session[SESSION_LTI_CONTEXT_ID] = lti_context.id session.save() self.assertEqual( self.client.session.get(SESSION_LTI_CONTEXT_ID), lti_context.id ) # Now session and permission are set, we should be able to access it response = self.client.get("/moderators/") self.assertEqual(200, response.status_code) self.assertContains(response, "Manage forum's moderators")
def test_access_can_manage_moderators_moderator_list_students(self): """Users that can manage moderators should be able to use the API to request list of students""" user = UserFactory() lti_context = LTIContextFactory(lti_consumer=user.lti_consumer) forum = ForumFactory() forum.lti_contexts.add(lti_context) self.client.force_login(user, "ashley.auth.backend.LTIBackend") response = self.client.get("/api/v1.0/users/?role=student") # First it's forbidden self.assertEqual(403, response.status_code) # Add session session = self.client.session session[SESSION_LTI_CONTEXT_ID] = lti_context.id session.save() self.assertEqual(self.client.session.get(SESSION_LTI_CONTEXT_ID), lti_context.id) response = self.client.get("/api/v1.0/users/?role=student") # Still forbidden session ok but missing permission self.assertEqual(response.status_code, 403) assign_perm("can_manage_moderator", user, forum, True) # Should now be authorized response = self.client.get("/api/v1.0/users/?role=student") self.assertEqual(response.status_code, 200)
def test_access_api_can_manage_moderators_update_student_no_session(self): """Users with no session can't update user""" update_user = UserFactory() api_user = UserFactory(lti_consumer=update_user.lti_consumer) lti_context = LTIContextFactory(lti_consumer=update_user.lti_consumer) forum = ForumFactory() forum.lti_contexts.add(lti_context) # Assign student group to user lti_context.sync_user_groups(update_user, ["student"]) # Assign the permission assign_perm("can_manage_moderator", api_user, forum, True) # response = self.client.patch( f"/api/v1.0/users/{update_user.id}/add_group_moderator/", content_type="application/json", ) self.assertEqual(response.status_code, 403) content = json.loads(response.content) self.assertEqual( content, {"detail": "Authentication credentials were not provided."}) # Create the session and it should work self.client.force_login(api_user, "ashley.auth.backend.LTIBackend") session = self.client.session session[SESSION_LTI_CONTEXT_ID] = lti_context.id session.save() response = self.client.patch( f"/api/v1.0/users/{update_user.id}/add_group_moderator/", content_type="application/json", ) self.assertEqual(response.status_code, 200)
def test_access_api_can_manage_moderators_update_student_no_group_moderator( self): """If moderator group doesn't exist user can be updated and group created Case for forum created before this feature""" update_user = UserFactory() api_user = UserFactory(lti_consumer=update_user.lti_consumer) lti_context = LTIContextFactory(lti_consumer=update_user.lti_consumer) forum = ForumFactory() forum.lti_contexts.add(lti_context) # Add group student lti_context.sync_user_groups(update_user, ["student"]) # Assign the permission assign_perm("can_manage_moderator", api_user, forum, True) # Creates the session self.client.force_login(api_user, "ashley.auth.backend.LTIBackend") session = self.client.session session[SESSION_LTI_CONTEXT_ID] = lti_context.id session.save() # Data to promote user to moderator response = self.client.patch( f"/api/v1.0/users/{update_user.id}/add_group_moderator/", content_type="application/json", ) self.assertEqual(response.status_code, 200)
def test_access_api_update_instructor_patch(self): """ Standard instructor call to patch user is allowed but doesn't change anything, as serializer's attributes are read-only. """ # Creates a forum user = UserFactory(id=1, public_username="******") lti_context = LTIContextFactory(lti_consumer=user.lti_consumer) forum = ForumFactory() forum.lti_contexts.add(lti_context) lti_context.sync_user_groups(user, ["instructor"]) assign_perm("can_manage_moderator", user, forum, True) # Creates the session self.client.force_login(user, "ashley.auth.backend.LTIBackend") session = self.client.session session[SESSION_LTI_CONTEXT_ID] = lti_context.id session.save() data = {"id": 10, "public_username": "******"} response = self.client.patch( f"/api/v1.0/users/{user.id}/", json.dumps(data), content_type="application/json", ) self.assertEqual(response.status_code, 200) self.assertEqual(response.json(), {"id": 1, "public_username": "******"})
def test_with_can_rename_forum_permission(self): """ A user with the `can_rename_forum` permission should be able to rename it. """ user = UserFactory() lti_context = LTIContextFactory(lti_consumer=user.lti_consumer) forum = ForumFactory(name="Initial forum name") forum.lti_contexts.add(lti_context) assign_perm("can_rename_forum", user, forum, True) self.client.force_login(user, "ashley.auth.backend.LTIBackend") response = self.client.get(f"/forum/admin/rename/{forum.pk}/") self.assertEqual(200, response.status_code) self.assertContains(response, "Rename the forum") update_response = self.client.post( f"/forum/admin/rename/{forum.pk}/", data={"name": "Modified forum name"}) self.assertEqual(302, update_response.status_code) self.assertEqual("Modified forum name", Forum.objects.get(pk=forum.pk).name)
def test_basic_user(self): """ A user without the `can_archive_forum` permission should not be able to archive it. """ user = UserFactory() lti_context = LTIContextFactory(lti_consumer=user.lti_consumer) forum = ForumFactory() forum.lti_contexts.add(lti_context) assign_perm("can_read_forum", user, forum) self.assertFalse(forum.archived) self.client.force_login(user, "ashley.auth.backend.LTIBackend") # The user can read the forum response = self.client.get(f"/forum/forum/{forum.slug}-{forum.pk}/") self.assertEqual(200, response.status_code) self.assertContains(response, forum.name) # but he's not allowed to archive it response = self.client.get(f"/forum/admin/archive/{forum.pk}/") self.assertEqual(403, response.status_code) update_response = self.client.post(f"/forum/admin/archive/{forum.pk}/") self.assertEqual(403, update_response.status_code) self.assertFalse(Forum.objects.get(pk=forum.pk).archived)
def test_forum_display_button_manage_moderator(self): """ Connects a user with standard forum permission and controls that CTA to manage moderator is not present, then add the permission can_manage_moderator and control that we now see the CTA as expected """ user = UserFactory() lti_context = LTIContextFactory(lti_consumer=user.lti_consumer) forum = ForumFactory(name="Initial forum name") forum.lti_contexts.add(lti_context) assign_perm("can_read_forum", user, forum) # Connects user and go on the forum page self.client.force_login(user) response = self.client.get(f"/forum/forum/{forum.name}-{forum.id}/") # Check the CTA to manage moderators is not present self.assertNotContains( response, ('<a href="/moderators/" title="Manage moderators" class="dropdown-item">' "Manage moderators</a>"), html=True, ) # Assign permission can_manage_moderator assign_perm("can_manage_moderator", user, forum) response = self.client.get(f"/forum/forum/{forum.name}-{forum.id}/") # Check the CTA to manage moderators is now present self.assertContains( response, ('<a href="/moderators/" title="Manage moderators" class="dropdown-item">' "Manage moderators</a>"), html=True, )
def test_access_can_manage_moderators_list_non_moderators(self): """Users that can manage moderators should be able to use the API to request list of users that are not moderators""" user = UserFactory() lti_context = LTIContextFactory(lti_consumer=user.lti_consumer) forum = ForumFactory() forum.lti_contexts.add(lti_context) self.client.force_login(user, "ashley.auth.backend.LTIBackend") response = self.client.get("/api/v1.0/users/?role=!moderator") # First it's forbidden self.assertEqual(403, response.status_code) # Add permission assign_perm("can_manage_moderator", user, forum, True) # Still forbidden, missing the session response = self.client.get("/api/v1.0/users/?!role=moderator") self.assertEqual(403, response.status_code) # Add session session = self.client.session session[SESSION_LTI_CONTEXT_ID] = lti_context.id session.save() self.assertEqual(self.client.session.get(SESSION_LTI_CONTEXT_ID), lti_context.id) response = self.client.get("/api/v1.0/users/?role=!moderator") # Permission + session added, it should be allowed self.assertEqual(response.status_code, 200)
def test_api_create_upload_image(self): """Verify that image gets created and uploaded""" user = UserFactory() forum = ForumFactory() self.client.force_login(user, "ashley.auth.backend.LTIBackend") filename = f"test-{datetime.datetime.now().strftime('%f')}.png" data = { "file": self._generate_image(filename), "forum": forum.id, "poster": user.id, } # At first, no image exists self.assertEqual(UploadImage.objects.count(), 0) # Upload image response = self.client.post("/api/v1.0/images/", data, format="multipart") self.assertEqual(response.status_code, 201) # Gets last id generated image_db = UploadImage.objects.last() # Image should have be added with right values self.assertEqual(UploadImage.objects.count(), 1) self.assertEqual(image_db.forum.id, forum.id) self.assertEqual(image_db.poster.id, user.id) # Controls image's path is respected on server self.assertIn(f"image_uploads/{forum.id}/{user.id}/", image_db.file.name) # Controls file has been uploaded uploaded_file = image_db.file.open() self.assertGreater(uploaded_file.size, 0) self.assertIsNotNone(uploaded_file.read())
def test_api_create_upload_bad_extension(self): """Make sure we can't upload a file with an unauthorized extension""" user = UserFactory() forum = ForumFactory() self.client.force_login(user, "ashley.auth.backend.LTIBackend") video = SimpleUploadedFile( "file.mp4", b"file_content", content_type="video/mp4" ) data = { "file": video, "forum": forum.id, "poster": user.id, } # Upload image response = self.client.post("/api/v1.0/images/", data, format="multipart") self.assertEqual(response.status_code, 400) self.assertEqual( json.loads(response.content)["file"], [ ( "Upload a valid image. The file you uploaded was either " + "not an image or a corrupted image." ) ], )
def test_testing_topic_announce(self): """Controls topics that are of type announcement don't have sorted options""" # Creates posts for announcement topics forum = ForumFactory() PostFactory(topic=TopicFactory(forum=forum, type=Topic.TOPIC_ANNOUNCE)) PostFactory(topic=TopicFactory(forum=forum, type=Topic.TOPIC_ANNOUNCE)) user = UserFactory() assign_perm("can_read_forum", user, forum) self.client.force_login(user) response = self.client.get(f"/forum/forum/{forum.slug}-{forum.pk}/") html = lxml.html.fromstring(response.content) # Select the header block of the announcement block, the first block announce_block = str( etree.tostring(html.cssselect(".topiclist .card-header")[0])) # Controls that announce_block is about announcements and not topics self.assertIn("Announcements", announce_block) self.assertNotIn("Topics", announce_block) self.assertIn("Replies", announce_block) self.assertIn("Views", announce_block) self.assertIn("Last post", announce_block) # There's no sortable informations self.assertNotIn("sortable sorted", announce_block) # There's no column that has a sorting link on self.assertNotIn("<a href=", announce_block) # There's no toggle sorting self.assertNotIn("Toggle sorting", announce_block)
def test_api_cant_delete_image(self): """Controls user doesn't have the right to delete a record""" user = UserFactory() forum = ForumFactory() self.client.force_login(user, "ashley.auth.backend.LTIBackend") filename = "image.png" data = { "file": self._generate_image(filename), "forum": forum.id, "poster": user.id, } # At first, no image exists self.assertEqual(UploadImage.objects.count(), 0) # Upload image response = self.client.post("/api/v1.0/images/", data, format="multipart") self.assertEqual(response.status_code, 201) # Gets last id generated self.assertEqual(UploadImage.objects.count(), 1) image_db = UploadImage.objects.last() # Controls image's path is respected on server self.assertIn(f"image_uploads/{forum.id}/{user.id}/", image_db.file.name) # Specify the id of the image for the update data = { "id": image_db.id, "file": self._generate_image(filename), "forum": forum.id, "poster": user.id, } response = self.client.delete("/api/v1.0/images/", data, format="multipart") self.assertEqual(response.status_code, 405) content = json.loads(response.content) self.assertEqual(content, {"detail": 'Method "DELETE" not allowed.'})
def _get_url_list_topic_with_three_topics(self): """Creates a forum with three topics and a user to access the forum that has the permission to access it. It's a shortcut used in all the tests below.""" forum = ForumFactory() # create 3 topics with distinct subject, views_count and date PostFactory( topic=TopicFactory(forum=forum, views_count=9), subject="TOPIC B the eldest with 9 views_count", ) PostFactory( topic=TopicFactory(forum=forum, views_count=6), subject="TOPIC A created second with 6 views_count", ) PostFactory( topic=TopicFactory(forum=forum, views_count=12), subject="TOPIC C the newest one with 12 views_count", ) user = UserFactory() assign_perm("can_read_forum", user, forum) self.client.force_login(user) # Setup url_list_topic = f"/forum/forum/{forum.slug}-{forum.pk}/" return forum, url_list_topic
def test_access_api_update_student_patch(self): """Standard call should not be allowed to update a student.""" # Creates a forum user = UserFactory() lti_context = LTIContextFactory(lti_consumer=user.lti_consumer) forum = ForumFactory() forum.lti_contexts.add(lti_context) assign_perm("can_read_forum", user, forum, True) # Creates the session self.client.force_login(user, "ashley.auth.backend.LTIBackend") session = self.client.session session[SESSION_LTI_CONTEXT_ID] = lti_context.id session.save() data = { "public_username": "******", } response = self.client.patch( f"/api/v1.0/users/{user.id}/", json.dumps(data), content_type="application/json", ) self.assertEqual(response.status_code, 403) self.assertEqual( response.json(), {"detail": "You do not have permission to perform this action."}, )
def test_get_readable_forums_super_user(self): """ Super user has access to all the forum filtered on LTIContext and archived ones but don't need to have access in reading. """ super_user = UserFactory(is_superuser=True) basic_user = UserFactory() lti_context_a = LTIContextFactory(lti_consumer=super_user.lti_consumer) lti_context_b = LTIContextFactory(lti_consumer=super_user.lti_consumer) # Create 2 forums for context A forum_a1 = ForumFactory(name="Forum A1") forum_a1.lti_contexts.add(lti_context_a) forum_a2_archived = ForumFactory(name="Forum A2") forum_a2_archived.lti_contexts.add(lti_context_a) forum_a2_archived.archived = True forum_a2_archived.save() # Create 1 forum for context B forum_b1 = ForumFactory(name="Forum B1") forum_b1.lti_contexts.add(lti_context_b) # Instantiate the permission Handler permission_handler = PermissionHandler() forums_qs = Forum.objects.all() # With no lti_context super_user can see all forums readable_forums = permission_handler.get_readable_forums(forums_qs, super_user) self.assertCountEqual(readable_forums, [forum_a1, forum_b1]) # standard user can't see any readable_forums = permission_handler.get_readable_forums(forums_qs, basic_user) self.assertCountEqual(readable_forums, []) # Inject a LTI context into the permission handler and ensure that # the results are filtered according to it permission_handler.current_lti_context_id = lti_context_a.id readable_forums = permission_handler.get_readable_forums(forums_qs, super_user) # even if super user has no can_read_forum permission is still can see all # unarchived forums of his lti_context self.assertCountEqual(readable_forums, [forum_a1]) # standard user can't see any readable_forums = permission_handler.get_readable_forums(forums_qs, basic_user) self.assertCountEqual(readable_forums, [])
def setUp(self): super().setUp() # Setup # Create consumer, context and user self.lti_consumer = LTIConsumerFactory() self.context = LTIContextFactory(lti_consumer=self.lti_consumer) # Create forum and add context self.forum = ForumFactory(name="Forum") self.forum.lti_contexts.add(self.context)
def test_testing_topic_announce_dont_get_ordered(self): """ Controls topics that are of type announcement don't get ordered if sorting is submitted in url. Orders are only applied to Topic posts. """ forum = ForumFactory() user = UserFactory() assign_perm("can_read_forum", user, forum) self.client.force_login(user) # Creates posts for announcement topics topicAnnounce1 = TopicFactory(forum=forum, type=Topic.TOPIC_ANNOUNCE, views_count=100) topicAnnounce2 = TopicFactory(forum=forum, type=Topic.TOPIC_ANNOUNCE, views_count=200) PostFactory( topic=topicAnnounce1, subject="TOPIC A TYPE ANNOUNCED", ) PostFactory( topic=topicAnnounce2, subject="TOPIC B TYPE ANNOUNCED", ) # Post of topicAnnounce2 has been created last, it should be the first one on the list self.assertLess(topicAnnounce1.last_post_on, topicAnnounce2.last_post_on) # Orders on column view_post response = self.client.get( f"/forum/forum/{forum.slug}-{forum.pk}/?o=2") # Orders is respected on default creation order self.assertContentBefore(response, "TOPIC B TYPE ANNOUNCED", "TOPIC A TYPE ANNOUNCED") # Reverses order response = self.client.get( f"/forum/forum/{forum.slug}-{forum.pk}/?o=-2") # Orders of announcement topics stays the same self.assertContentBefore(response, "TOPIC B TYPE ANNOUNCED", "TOPIC A TYPE ANNOUNCED") # Orders on replies column response = self.client.get( f"/forum/forum/{forum.slug}-{forum.pk}/?o=-1") # Shows order is respected on default creation order self.assertContentBefore(response, "TOPIC B TYPE ANNOUNCED", "TOPIC A TYPE ANNOUNCED") # Reverses order response = self.client.get( f"/forum/forum/{forum.slug}-{forum.pk}/?o=1") # Orders of announcement topics stays the same self.assertContentBefore(response, "TOPIC B TYPE ANNOUNCED", "TOPIC A TYPE ANNOUNCED")
def test_can_tell_if_the_user_is_instructor_of_this_forum(): """ Given two users. User1 is student of the forum, User2 is instructor. We control that is_user_instructor can detect if a user is an instructor. Then a forum can be part of multiple contexts. If a user is instructor in one context, he is considered intructor of this forum in all contexts. We add a new context to the forum where user is instructor and test that user is now considered as instructor """ # load template def get_rendered(topic, user): template = Template( "{% load custom_tags %}" + "{% if topic|is_user_instructor:user %}YES{% else %}NO{% endif %}" ) context = Context({"topic": topic, "user": user}) rendered = template.render(context) return rendered lti_consumer = LTIConsumerFactory() # Create two LTI Context context1 = LTIContextFactory(lti_consumer=lti_consumer) context2 = LTIContextFactory(lti_consumer=lti_consumer) # Create two users user1 = UserFactory(lti_consumer=lti_consumer) user2 = UserFactory(lti_consumer=lti_consumer) # Sync user1 groups in context1 with role "student" context1.sync_user_groups(user1, ["student"]) # Sync user1 groups in context2 with role "instructor" context2.sync_user_groups(user1, ["instructor"]) # Sync user2 groups in context1 with role "instructor" context1.sync_user_groups(user2, ["instructor"]) # Create forum and add context1 forum = ForumFactory(name="Initial forum name") forum.lti_contexts.add(context1) # Set up topic topic = TopicFactory(forum=forum, poster=user1, subject="topic création") # Chek that user1 is not instructor assert get_rendered(topic, user1) == "NO" # Chek that user2 is instructor assert get_rendered(topic, user2) == "YES" # Add forum to context2 where user1 has role "instructor" forum.lti_contexts.add(context2) # Check that user1 is now instructor as well assert get_rendered(topic, user1) == "YES"
def test_api_create_upload_image_wrong_user_id(self): """Verify that if user id is wrong we get a 400 code""" user = UserFactory() forum = ForumFactory() self.client.force_login(user, "ashley.auth.backend.LTIBackend") data = { "file": self._generate_image("test.png"), "forum": forum.id, "poster": user.id + 10, } # Upload image response = self.client.post("/api/v1.0/images/", data, format="multipart") self.assertEqual(response.status_code, 400)
def test_api_create_upload_image_same_filename(self): """ If a user try to upload an image with the same filename, we make sure that two images gets uploaded with a different filename """ user = UserFactory() forum = ForumFactory() self.client.force_login(user, "ashley.auth.backend.LTIBackend") filename = "image.png" data = { "file": self._generate_image(filename), "forum": forum.id, "poster": user.id, } # At first, no image exists self.assertEqual(UploadImage.objects.count(), 0) # Upload image response = self.client.post("/api/v1.0/images/", data, format="multipart") self.assertEqual(response.status_code, 201) # Gets last id generated self.assertEqual(UploadImage.objects.count(), 1) image_db = UploadImage.objects.last() # Controls image's path is respected on server self.assertIn(f"image_uploads/{forum.id}/{user.id}/", image_db.file.name) # Controls file has been uploaded uploaded_file = image_db.file.open() self.assertGreater(uploaded_file.size, 0) self.assertIsNotNone(uploaded_file.read()) # Upload an image with the same name data = { "file": self._generate_image(filename), "forum": forum.id, "poster": user.id, } response = self.client.post("/api/v1.0/images/", data, format="multipart") self.assertEqual(response.status_code, 201) # A new image has been uploaded self.assertEqual(UploadImage.objects.count(), 2) image_db2 = UploadImage.objects.last() # Name of the file is different for the two images self.assertNotEqual(image_db.file.name, image_db2.file.name) # Controls image's path is respected on server self.assertIn(f"image_uploads/{forum.id}/{user.id}/", image_db2.file.name) # Controls file has been uploaded uploaded_file = image_db2.file.open() self.assertGreater(uploaded_file.size, 0) self.assertIsNotNone(uploaded_file.read())
def test_course_admin_cant_lock_forum_in_other_context(self): """ Instructors can't lock course from a forum that isn't in the same context. """ forum = self._connects("instructor") forum_other_context = ForumFactory() # user can't access the view to lock a course response = self.client.get( f"/forum/admin/lock-course/{forum_other_context.id}/") self.assertEqual(403, response.status_code) # user can access the view to lock a course response = self.client.get(f"/forum/admin/lock-course/{forum.id}/") self.assertEqual(200, response.status_code)
def test_api_create_upload_max_size(self): """Make sure we can't upload file over limit size""" user = UserFactory() forum = ForumFactory() self.client.force_login(user, "ashley.auth.backend.LTIBackend") data = { "file": self._generate_image("test.png"), "forum": forum.id, "poster": user.id, } # Upload image response = self.client.post("/api/v1.0/images/", data, format="multipart") self.assertEqual(response.status_code, 400) self.assertEqual( json.loads(response.content)["file"], ["The maximum file size that can be uploaded is 0 MB"], )
def test_api_create_upload_image_no_user_id(self): """Verify that if user id is not send it's setup automatically to current user id""" user = UserFactory() forum = ForumFactory() self.client.force_login(user, "ashley.auth.backend.LTIBackend") data = { "file": self._generate_image("test.png"), "forum": forum.id, } # At first, no image exists self.assertEqual(UploadImage.objects.count(), 0) # Upload image response = self.client.post("/api/v1.0/images/", data, format="multipart") self.assertEqual(response.status_code, 201) # Poster id should be the current user id logged in self.assertEqual(UploadImage.objects.count(), 1)