Repository: Cloud-CV/EvalAI

Issue:
Use IsChallengeCreator decorator in Challenge Phase detail API Endpoint
Currently, the check for if the requesting user is Creator of Challenge or not is missing from the `challenge_phase_detail` API View. We would like to add the `IsChallengeCreator` decorator (https://github.com/Cloud-CV/EvalAI/blob/master/apps/challenges/permissions.py#L6) to this endpoint. 

Also, please add test cases after making the above changes. 


Discussion:
@AnshulBasia Sure. :) 
@deshraj  can you tell me what exactly do we do after the check?

@Shashi456 I think @AnshulBasia is already working on this. Feel free to pick any other unassigned issue. :) 
@deshraj what about #863 
@AnshulBasia any updates on this? If you're busy, I can take it forward.
@varunagrawal Sorry,I am done but testing is yet to be done. I got stuck with my assignments. Please take it forward. Thanks. 
Sure thing mate @AnshulBasia. Thanks for letting us know. :)
@deshraj please assign this to me.
Sure. 

@deshraj Can you please tell me what we need to do exactly.
@modestlearner: This is quite critical and I would like to fix it myself. Can you please pick some other issue? 
@deshraj Sure
@deshraj Is this solved?
Hi @isht3, @deshraj has been on vacation the past few weeks and should be back today. Expect this to be solved sometime this week. :)
@varunagrawal Hope he had a good time, I'm going on vacation today! lol.
@isht3: no, this issue is not solved yet. Please feel free to take this up. 
@isht3 Deshraj has given you the go ahead. If you don't have the time to work on this, we understand, and we can assign this to @Obtin
@varunagrawal, @Obtin Yes, I already started working on this. I was going through how to work on decorators, that's why this was taking time. I'll finish this by sometime next week. :smile: 
@deshraj The `IsChallengeCreator` Class in `permissions.py` and its use-cases should be completely replaced with the `IsChallengeCreator` decorator, yes?
@isht3 no that class is required in the permissions since other API endpoints depend on it. You should just be able to add that permission class to the `@permission_classes` decorator and have it work. The crucial aspect of this issue are the test cases which need to ensure all corner cases are handled.

I know this seems like very little work technically, but then again, this is an easy issue to get people started with CloudCV contribution. :)
@varunagrawal I think the changes are already added to the `permission_classes`, I'll get started with writing the tests. Thank you for clearing it up! 

Resolving patch:
diff --git a/apps/challenges/views.py b/apps/challenges/views.py
--- a/apps/challenges/views.py
+++ b/apps/challenges/views.py
@@ -331,7 +331,7 @@ def challenge_phase_list(request, challenge_pk):
 
 @throttle_classes([UserRateThrottle])
 @api_view(['GET', 'PUT', 'PATCH', 'DELETE'])
-@permission_classes((permissions.IsAuthenticatedOrReadOnly, HasVerifiedEmail))
+@permission_classes((permissions.IsAuthenticatedOrReadOnly, HasVerifiedEmail, IsChallengeCreator))
 @authentication_classes((ExpiringTokenAuthentication,))
 def challenge_phase_detail(request, challenge_pk, pk):
     try:


Test patch:
diff --git a/tests/unit/challenges/test_views.py b/tests/unit/challenges/test_views.py
--- a/tests/unit/challenges/test_views.py
+++ b/tests/unit/challenges/test_views.py
@@ -198,6 +198,54 @@ def test_get_particular_challenge(self):
         self.assertEqual(response.data, expected)
         self.assertEqual(response.status_code, status.HTTP_200_OK)
 
+    def test_update_challenge_when_user_is_not_its_creator(self):
+        self.user1 = User.objects.create(
+            username='someuser1',
+            email="user1@test.com",
+            password='secret_psassword')
+
+        EmailAddress.objects.create(
+            user=self.user1,
+            email='user1@test.com',
+            primary=True,
+            verified=True)
+
+        self.client.force_authenticate(user=self.user1)
+
+        expected = {"detail": "Sorry, you are not allowed to perform this operation!"}
+
+        response = self.client.put(self.url, {'title': 'Rose Challenge', 'description': 'Version 2.0'})
+        self.assertEqual(response.data, expected)
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+
+    def test_update_challenge_when_user_is_its_creator(self):
+        new_title = 'Rose Challenge'
+        new_description = 'New description.'
+        expected = {
+            "id": self.challenge.pk,
+            "title": new_title,
+            "short_description": self.challenge.short_description,
+            "description": new_description,
+            "terms_and_conditions": self.challenge.terms_and_conditions,
+            "submission_guidelines": self.challenge.submission_guidelines,
+            "evaluation_details": self.challenge.evaluation_details,
+            "image": None,
+            "start_date": "{0}{1}".format(self.challenge.start_date.isoformat(), 'Z').replace("+00:00", ""),
+            "end_date": "{0}{1}".format(self.challenge.end_date.isoformat(), 'Z').replace("+00:00", ""),
+            "creator": {
+                "id": self.challenge.creator.pk,
+                "team_name": self.challenge.creator.team_name,
+                "created_by": self.challenge.creator.created_by.username
+            },
+            "published": self.challenge.published,
+            "enable_forum": self.challenge.enable_forum,
+            "anonymous_leaderboard": self.challenge.anonymous_leaderboard,
+            "is_active": True
+        }
+        response = self.client.put(self.url, {'title': new_title, 'description': new_description})
+        self.assertEqual(response.data, expected)
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+
     def test_particular_challenge_does_not_exist(self):
         self.url = reverse_lazy('challenges:get_challenge_detail',
                                 kwargs={'challenge_host_team_pk': self.challenge_host_team.pk,
@@ -1243,6 +1291,47 @@ def test_get_particular_challenge_phase(self):
         self.assertEqual(response.data, expected)
         self.assertEqual(response.status_code, status.HTTP_200_OK)
 
+    def test_update_challenge_phase_when_user_is_not_its_creator(self):
+        self.user1 = User.objects.create(
+            username='someuser1',
+            email="user1@test.com",
+            password='secret_psassword')
+
+        EmailAddress.objects.create(
+            user=self.user1,
+            email='user1@test.com',
+            primary=True,
+            verified=True)
+
+        self.client.force_authenticate(user=self.user1)
+
+        expected = {"detail": "Sorry, you are not allowed to perform this operation!"}
+
+        response = self.client.put(self.url, {'name': 'Rose Phase', 'description': 'New description.'})
+        self.assertEqual(response.data, expected)
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+
+    def test_update_challenge_phase_when_user_is_its_creator(self):
+        new_name = 'Rose Phase'
+        new_description = 'New description.'
+        expected = {
+            "id": self.challenge_phase.id,
+            "name": new_name,
+            "description": new_description,
+            "leaderboard_public": self.challenge_phase.leaderboard_public,
+            "start_date": "{0}{1}".format(self.challenge_phase.start_date.isoformat(), 'Z').replace("+00:00", ""),
+            "end_date": "{0}{1}".format(self.challenge_phase.end_date.isoformat(), 'Z').replace("+00:00", ""),
+            "challenge": self.challenge_phase.challenge.pk,
+            "is_public": self.challenge_phase.is_public,
+            "is_active": True,
+            "codename": "Phase Code Name",
+            "max_submissions_per_day": self.challenge_phase.max_submissions_per_day,
+            "max_submissions": self.challenge_phase.max_submissions,
+        }
+        response = self.client.put(self.url, {'name': new_name, 'description': new_description})
+        self.assertEqual(response.data, expected)
+        self.assertEqual(response.status_code, status.HTTP_200_OK)
+
     def test_particular_challenge_phase_does_not_exist(self):
         self.url = reverse_lazy('challenges:get_challenge_phase_detail',
                                 kwargs={'challenge_pk': self.challenge.pk,