diff --git a/imperial_coldfront_plugin/migrations/0005_groupmembership_is_manager.py b/imperial_coldfront_plugin/migrations/0005_groupmembership_is_manager.py new file mode 100644 index 0000000..b589751 --- /dev/null +++ b/imperial_coldfront_plugin/migrations/0005_groupmembership_is_manager.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.11 on 2025-01-17 09:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('imperial_coldfront_plugin', '0004_alter_groupmembership_member_alter_unixuid_user'), + ] + + operations = [ + migrations.AddField( + model_name='groupmembership', + name='is_manager', + field=models.BooleanField(default=False), + ), + ] diff --git a/imperial_coldfront_plugin/models.py b/imperial_coldfront_plugin/models.py index 4c1916e..8e4e34c 100644 --- a/imperial_coldfront_plugin/models.py +++ b/imperial_coldfront_plugin/models.py @@ -40,6 +40,7 @@ class GroupMembership(models.Model): AUTH_USER_MODEL. Deletes related memberships when the owner is deleted. member (ForeignKey): A reference to the user designated as member, connected to AUTH_USER_MODEL. Deletes related memberships when the member is deleted. + is_manager (BooleanField): A boolean value indicating if a member is a manager. """ group = models.ForeignKey( @@ -52,6 +53,8 @@ class GroupMembership(models.Model): on_delete=models.CASCADE, ) + is_manager = models.BooleanField(default=False) + class UnixUID(models.Model): """Identity data to map a user to a unique identifier. diff --git a/imperial_coldfront_plugin/views.py b/imperial_coldfront_plugin/views.py index 483595b..069d6d0 100644 --- a/imperial_coldfront_plugin/views.py +++ b/imperial_coldfront_plugin/views.py @@ -89,7 +89,12 @@ def check_access(request: HttpRequest): @login_required def send_group_invite(request: HttpRequest) -> HttpResponse: """Invite an individual to a group.""" - if not ResearchGroup.objects.filter(owner=request.user).exists(): + if ( + not ResearchGroup.objects.filter(owner=request.user).exists() + and not GroupMembership.objects.filter( + member=request.user, is_manager=True + ).exists() + ): return HttpResponseForbidden("You are not a group owner.") if request.method == "POST": diff --git a/tests/conftest.py b/tests/conftest.py index 4ab7633..277937e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -155,3 +155,14 @@ def user_client(auth_client_factory, user): def pi_client(auth_client_factory, pi): """Return an authenticated Django test client for a PI.""" return auth_client_factory(pi) + + +@pytest.fixture +def manager_in_group(user_factory, research_group_factory): + """Return a user who is a manager in a research group.""" + from imperial_coldfront_plugin.models import GroupMembership + + manager = user_factory() + group, memberships = research_group_factory(number_of_members=0) + GroupMembership.objects.create(group=group, member=manager, is_manager=True) + return manager, group diff --git a/tests/test_views.py b/tests/test_views.py index be6147b..ae6b4a2 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -94,6 +94,13 @@ def test_get(self, pi_group, pi_client): assert response.status_code == HTTPStatus.OK assert isinstance(response.context["form"], GroupMembershipForm) + def test_manager_can_access(self, manager_in_group, auth_client_factory): + """Test that a group manager can access the view.""" + manager, group = manager_in_group + client = auth_client_factory(manager) + response = client.get(self._get_url()) + assert response.status_code == 200 + def test_post_valid( self, pi, pi_group, pi_client, mailoutbox, timestamp_signer_mock ):