Repository: Cloud-CV/EvalAI

Issue:
Allow and block particular email domains for a challenge
Currently, anyone can participate in a challenge. It would be great to have a functionality to allow/block only particular email domains to participate in a challenge as specified by the challenge host. 

## Deliverables

- [x] Add a field to Challenge model to store the `allowed_email_domains` (default=None, blank=True)
- [x] Add a field to Challenge model to store the `blocked_emails_domains` (default=None, blank=True)
- [ ] Update the challenge creation API to add support to incorporate `allowed_email_domains` and `blocked_email_domains`
- [ ] Update the Challenge creation using zip file documentation to add support for `allowed_email_domains`   and `blocked_email_domains`
- [ ] Update the challenge participation API to only allow `allowed_email_domains` and block `blocked_email_domains`


Discussion:
@deshraj @RishabhJain2018 Can I work on this?
Yeah sure. Assigning this to you. 
@deshraj for the `allowed_email_domains` and the other field I should be using `ArrayField`, yes?
Yes, you are right. 

Resolving patch:
diff --git a/apps/challenges/views.py b/apps/challenges/views.py
--- a/apps/challenges/views.py
+++ b/apps/challenges/views.py
@@ -168,6 +168,38 @@ def add_participant_team_to_challenge(request, challenge_pk, participant_team_pk
         response_data = {'error': 'ParticipantTeam does not exist'}
         return Response(response_data, status=status.HTTP_406_NOT_ACCEPTABLE)
 
+    # Check if user is in allowed list.
+
+    user_email = request.user.email
+
+    if len(challenge.allowed_email_domains) > 0:
+        present = False
+        for domain in challenge.allowed_email_domains:
+            if domain.lower() in user_email.lower():
+                present = True
+                break
+        if not present:
+            message = 'Sorry, users with {} email domain(s) are only allowed to participate in this challenge.'
+            domains = ""
+            for domain in challenge.allowed_email_domains:
+                domains = "{}{}{}".format(domains, "/", domain)
+            domains = domains[1:]
+            response_data = {'error': message.format(domains)}
+            return Response(response_data, status=status.HTTP_406_NOT_ACCEPTABLE)
+
+    # Check if user is in blocked list.
+
+    for domain in challenge.blocked_email_domains:
+        domain = "@" + domain
+        if domain.lower() in user_email.lower():
+            message = 'Sorry, users with {} email domain(s) are not allowed to participate in this challenge.'
+            domains = ""
+            for domain in challenge.blocked_email_domains:
+                domains = "{}{}{}".format(domains, "/", domain)
+            domains = domains[1:]
+            response_data = {'error': message.format(domains)}
+            return Response(response_data, status=status.HTTP_406_NOT_ACCEPTABLE)
+
     # check to disallow the user if he is a Challenge Host for this challenge
 
     challenge_host_team_pk = challenge.creator.pk


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
@@ -386,15 +386,18 @@ def setUp(self):
         # user who create a challenge host team
         self.user2 = User.objects.create(
             username='someuser2',
+            email='user@example2.com',
             password='some_secret_password')
         # user who maps a participant team to a challenge
         self.user3 = User.objects.create(
             username='someuser3',
+            email='user@example3.com',
             password='some_secret_password')
 
         # user invited to the participant team
         self.user4 = User.objects.create(
             username='someuser4',
+            email='user@example4.com',
             password='some_secret_password')
 
         self.challenge_host_team2 = ChallengeHostTeam.objects.create(
@@ -425,6 +428,8 @@ def setUp(self):
             anonymous_leaderboard=False,
             start_date=timezone.now() - timedelta(days=2),
             end_date=timezone.now() + timedelta(days=1),
+            allowed_email_domains=[],
+            blocked_email_domains=[]
         )
 
         self.participant_team2 = ParticipantTeam.objects.create(
@@ -521,6 +526,42 @@ def test_add_participant_team_to_challenge_when_some_members_have_already_partic
         self.assertEqual(response.data, expected)
         self.assertEqual(response.status_code, status.HTTP_406_NOT_ACCEPTABLE)
 
+    def test_participation_when_participant_is_in_blocked_list(self):
+        self.challenge2.blocked_email_domains.extend(["test", "test1"])
+        self.challenge2.save()
+        self.url = reverse_lazy('challenges:add_participant_team_to_challenge',
+                                kwargs={'challenge_pk': self.challenge2.pk,
+                                        'participant_team_pk': self.participant_team3.pk})
+
+        response = self.client.post(self.url, {})
+        message = 'Sorry, users with {} email domain(s) are not allowed to participate in this challenge.'
+        expected = {'error': message.format('test/test1')}
+        self.assertEqual(response.data, expected)
+        self.assertEqual(response.status_code, status.HTTP_406_NOT_ACCEPTABLE)
+
+    def test_participation_when_participant_is_not_in_allowed_list(self):
+        self.challenge2.allowed_email_domains.extend(["example1", "example2"])
+        self.challenge2.save()
+        self.url = reverse_lazy('challenges:add_participant_team_to_challenge',
+                                kwargs={'challenge_pk': self.challenge2.pk,
+                                        'participant_team_pk': self.participant_team3.pk})
+
+        response = self.client.post(self.url, {})
+        message = 'Sorry, users with {} email domain(s) are only allowed to participate in this challenge.'
+        expected = {'error': message.format("example1/example2")}
+
+        self.assertEqual(response.data, expected)
+        self.assertEqual(response.status_code, status.HTTP_406_NOT_ACCEPTABLE)
+
+    def test_participation_when_participant_is_in_allowed_list(self):
+        self.challenge2.allowed_email_domains.append("test")
+        self.challenge2.save()
+        self.url = reverse_lazy('challenges:add_participant_team_to_challenge',
+                                kwargs={'challenge_pk': self.challenge2.pk,
+                                        'participant_team_pk': self.participant_team3.pk})
+        response = self.client.post(self.url, {})
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+
 
 class DisableChallengeTest(BaseAPITestClass):