Repository: carpentries/amy

Issue:
Access for `invoicing` group
Person responsible for invoicing is not able to get to the invoicing request views.

@chrismedrela I suggest you to write decorator like `is_in_groups(['grp1', 'grp2'])` and then change `is_admin` to use `is_in_groups` with specified groups (`administrators`, `steering committee` and `invoicing` for now).



Discussion:
@pbanaszkiewicz, What views should be accessible to members of `invoicing` group? If all, we can do the same what we did with `steering committee` group.

For now, please do what you did with the steering committee group.


Resolving patch:
diff --git a/workshops/models.py b/workshops/models.py
--- a/workshops/models.py
+++ b/workshops/models.py
@@ -450,7 +450,8 @@ def is_admin(user):
     else:
         return (user.is_superuser or
                 user.groups.filter(Q(name='administrators') |
-                                   Q(name='steering committee')).exists())
+                                   Q(name='steering committee') |
+                                   Q(name='invoicing')).exists())
 
 
 class ProfileUpdateRequest(ActiveMixin, CreatedUpdatedMixin, models.Model):


Test patch:
diff --git a/workshops/test/test_security.py b/workshops/test/test_security.py
--- a/workshops/test/test_security.py
+++ b/workshops/test/test_security.py
@@ -37,8 +37,9 @@ class TestViews(TestBase):
     def setUp(self):
         super().setUp()
 
-        admins = Group.objects.get(name='administrators')
-        steering_committee = Group.objects.get(name='steering committee')
+        admins, _ = Group.objects.get_or_create(name='administrators')
+        steering_committee, _ = Group.objects.get_or_create(name='steering committee')
+        invoicing_group, _ = Group.objects.get_or_create(name='invoicing')
 
         # superuser who doesn't belong to Admin group should have access to
         # admin dashboard
@@ -60,6 +61,13 @@ def setUp(self):
         )
         self.committee.groups.add(steering_committee)
 
+        # members of invoicing group should have access to admin dashboard too
+        self.invoicing = Person.objects.create_user(
+            username='invoicing', personal='Bob', family='Invoicing',
+            email='invoicing@example.org', password='invoicing',
+        )
+        self.invoicing.groups.add(invoicing_group)
+
         # user with access only to trainee dashboard
         self.trainee = Person.objects.create_user(
             username='trainee', personal='Bob', family='Trainee',
@@ -68,7 +76,7 @@ def setUp(self):
         assert steering_committee not in self.trainee.groups.all()
 
     def assert_accessible(self, url, user=None):
-        if user:
+        if user is not None:
             self.client.login(username=user, password=user)
         response = self.client.get(url)
         self.client.logout()
@@ -81,6 +89,7 @@ def assert_inaccessible(self, url, user=None):
         response = self.client.get(url)
         self.client.logout()
 
+        # We should get 403 page (forbidden) or be redirected to login page.
         if response.status_code != 403:
             login_url = '{}?next={}'.format(reverse('login'), url)
             self.assertRedirects(response, login_url)
@@ -98,6 +107,7 @@ def test_function_based_view_restricted_to_admins(self):
         self.assert_accessible(url, user='superuser')
         self.assert_accessible(url, user='admin')
         self.assert_accessible(url, user='committee')
+        self.assert_accessible(url, user='invoicing')
         self.assert_inaccessible(url, user='trainee')
         self.assert_inaccessible(url, user=None)
 
@@ -114,6 +124,7 @@ def test_function_based_view_restricted_to_authorized_users(self):
         self.assert_accessible(url, user='superuser')
         self.assert_accessible(url, user='admin')
         self.assert_accessible(url, user='committee')
+        self.assert_accessible(url, user='invoicing')
         self.assert_accessible(url, user='trainee')
         self.assert_inaccessible(url, user=None)
 
@@ -130,6 +141,7 @@ def test_function_based_view_accessible_to_unauthorized_users(self):
         self.assert_accessible(url, user='superuser')
         self.assert_accessible(url, user='admin')
         self.assert_accessible(url, user='committee')
+        self.assert_accessible(url, user='invoicing')
         self.assert_accessible(url, user='trainee')
         self.assert_accessible(url, user=None)
 
@@ -145,6 +157,7 @@ def test_class_based_view_restricted_to_admins(self):
         self.assert_accessible(url, user='superuser')
         self.assert_accessible(url, user='admin')
         self.assert_accessible(url, user='committee')
+        self.assert_accessible(url, user='invoicing')
         self.assert_inaccessible(url, user='trainee')
         self.assert_inaccessible(url, user=None)
 
@@ -160,6 +173,7 @@ def test_class_based_view_accessible_to_unauthorized_users(self):
         self.assert_accessible(url, user='superuser')
         self.assert_accessible(url, user='admin')
         self.assert_accessible(url, user='committee')
+        self.assert_accessible(url, user='invoicing')
         self.assert_accessible(url, user='trainee')
         self.assert_accessible(url, user=None)