Skip to content

Commit

Permalink
Merge pull request #361 from UW-GAC/feature/add-primed-cc-admins-to-s…
Browse files Browse the repository at this point in the history
…ome-groups

Add PRIMED CC admins to groups created by this project
  • Loading branch information
amstilp authored Dec 19, 2023
2 parents 512a9b7 + 1244bef commit 77eb65e
Show file tree
Hide file tree
Showing 8 changed files with 699 additions and 22 deletions.
1 change: 0 additions & 1 deletion config/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,4 +386,3 @@
# Specify the subject for AnVIL account verification emails.
ANVIL_ACCOUNT_LINK_EMAIL_SUBJECT = "Verify your AnVIL account email"
ANVIL_ACCOUNT_VERIFY_NOTIFICATION_EMAIL = "[email protected]"
ANVIL_CDSA_GROUP_NAME = "PRIMED_CDSA"
3 changes: 3 additions & 0 deletions config/settings/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,6 @@
"ANVIL_DATA_ACCESS_GROUP_PREFIX", default="DEV_PRIMED"
)
ANVIL_CDSA_GROUP_NAME = env("ANVIL_CDSA_GROUP_NAME", default="DEV_PRIMED_CDSA")
ANVIL_CC_ADMINS_GROUP_NAME = env(
"ANVIL_CC_ADMINS_GROUP_NAME", default="DEV_PRIMED_CC_ADMINS"
)
2 changes: 2 additions & 0 deletions config/settings/production.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,5 @@
# ANVIL_DBGAP_APPLICATION_GROUP_PREFIX = "PRIMED_DBGAP_ACCESS"
# ANVIL_CDSA_GROUP_PREFIX = "PRIMED_CDSA_ACCESS"
ANVIL_DATA_ACCESS_GROUP_PREFIX = "PRIMED"
ANVIL_CC_ADMINS_GROUP_NAME = "PRIMED_CC_ADMINS"
ANVIL_CDSA_GROUP_NAME = "PRIMED_CDSA"
1 change: 1 addition & 0 deletions config/settings/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
ANVIL_API_SERVICE_ACCOUNT_FILE = "foo"
ANVIL_DATA_ACCESS_GROUP_PREFIX = "TEST_PRIMED"
ANVIL_CDSA_GROUP_NAME = "TEST_PRIMED_CDSA"
ANVIL_CC_ADMINS_GROUP_NAME = "TEST_PRIMED_CC_ADMINS"

# template tests require debug to be set
# get the last templates entry and set debug option
Expand Down
533 changes: 519 additions & 14 deletions primed/cdsa/tests/test_views.py

Large diffs are not rendered by default.

31 changes: 30 additions & 1 deletion primed/cdsa/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
AnVILConsortiumManagerStaffViewRequired,
AnVILProjectManagerAccess,
)
from anvil_consortium_manager.models import GroupAccountMembership, ManagedGroup
from anvil_consortium_manager.models import (
GroupAccountMembership,
GroupGroupMembership,
ManagedGroup,
)
from django.conf import settings
from django.contrib import messages
from django.contrib.messages.views import SuccessMessageMixin
Expand Down Expand Up @@ -238,6 +242,18 @@ def get_agreement(self, form, formset):
# Make sure the group doesn't exist already.
access_group.full_clean()
access_group.save()
# Add the cc admins group as a member.
cc_admins_group = ManagedGroup.objects.get(
name=settings.ANVIL_CC_ADMINS_GROUP_NAME
)
self.admin_access_membership = GroupGroupMembership(
parent_group=access_group,
child_group=cc_admins_group,
role=GroupGroupMembership.ADMIN,
)
self.admin_access_membership.full_clean()
self.admin_access_membership.save()
# Now finally save the agreement.
agreement = form.save(commit=False)
agreement.anvil_access_group = access_group
agreement.type = self.agreement_type_model.AGREEMENT_TYPE
Expand All @@ -254,6 +270,7 @@ def anvil_create(self):
"""Create resources on ANVIL."""
# Create AnVIL groups.
self.object.anvil_access_group.anvil_create()
self.admin_access_membership.anvil_create()

def form_valid(self, form):
formset = self.get_formset()
Expand Down Expand Up @@ -330,6 +347,7 @@ def anvil_create(self):
"""Create resources on ANVIL."""
super().anvil_create()
self.object.dataaffiliateagreement.anvil_upload_group.anvil_create()
self.admin_upload_membership.anvil_create()

def get_agreement_type(self, form, formset):
agreement_type = super().get_agreement_type(form, formset)
Expand All @@ -344,6 +362,17 @@ def get_agreement_type(self, form, formset):
# Make sure the group doesn't exist already.
upload_group.full_clean()
upload_group.save()
# Add the cc admins group as a member.
cc_admins_group = ManagedGroup.objects.get(
name=settings.ANVIL_CC_ADMINS_GROUP_NAME
)
self.admin_upload_membership = GroupGroupMembership(
parent_group=upload_group,
child_group=cc_admins_group,
role=GroupGroupMembership.ADMIN,
)
self.admin_upload_membership.full_clean()
self.admin_upload_membership.save()
agreement_type.anvil_upload_group = upload_group
return agreement_type

Expand Down
125 changes: 121 additions & 4 deletions primed/dbgap/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from anvil_consortium_manager import views as acm_views
from anvil_consortium_manager.models import (
AnVILProjectManagerAccess,
GroupGroupMembership,
ManagedGroup,
Workspace,
)
Expand Down Expand Up @@ -1587,6 +1588,8 @@ def setUp(self):
codename=AnVILProjectManagerAccess.STAFF_EDIT_PERMISSION_CODENAME
)
)
# Create the admin group.
self.cc_admin_group = ManagedGroupFactory.create(name="TEST_PRIMED_CC_ADMINS")

def get_url(self, *args):
"""Get the url for the view being tested."""
Expand Down Expand Up @@ -1662,6 +1665,13 @@ def test_can_create_object(self):
self.anvil_response_mock.add(
responses.POST, api_url, status=201, json={"message": "mock message"}
)
# CC admins group membership.
self.anvil_response_mock.add(
responses.PUT,
self.api_client.sam_entry_point
+ "/api/groups/v1/TEST_PRIMED_DBGAP_ACCESS_1/admin/[email protected]",
status=204,
)
response = self.client.post(
self.get_url(), {"principal_investigator": pi.pk, "dbgap_project_id": 1}
)
Expand All @@ -1684,6 +1694,12 @@ def test_redirect_url(self):
self.anvil_response_mock.add(
responses.POST, api_url, status=201, json={"message": "mock message"}
)
# CC admins group membership.
self.anvil_response_mock.add(
responses.PUT,
api_url + "/admin/[email protected]",
status=204,
)
response = self.client.post(
self.get_url(), {"principal_investigator": pi.pk, "dbgap_project_id": 1}
)
Expand All @@ -1702,6 +1718,12 @@ def test_success_message(self):
self.anvil_response_mock.add(
responses.POST, api_url, status=201, json={"message": "mock message"}
)
# CC admins group membership.
self.anvil_response_mock.add(
responses.PUT,
api_url + "/admin/[email protected]",
status=204,
)
response = self.client.post(
self.get_url(),
{"principal_investigator": pi.pk, "dbgap_project_id": 1},
Expand Down Expand Up @@ -1817,27 +1839,46 @@ def test_creates_anvil_access_group(self):
self.anvil_response_mock.add(
responses.POST, api_url, status=201, json={"message": "mock message"}
)
# CC admins group membership.
self.anvil_response_mock.add(
responses.PUT,
api_url + "/admin/[email protected]",
status=204,
)
response = self.client.post(
self.get_url(), {"principal_investigator": pi.pk, "dbgap_project_id": 12498}
)
self.assertEqual(response.status_code, 302)
new_object = models.dbGaPApplication.objects.latest("pk")
self.assertEqual(ManagedGroup.objects.count(), 1)
# A new group was created.
new_group = ManagedGroup.objects.latest("pk")
self.assertEqual(new_object.anvil_access_group, new_group)
self.assertEqual(new_group.name, "TEST_PRIMED_DBGAP_ACCESS_12498")
self.assertTrue(new_group.is_managed_by_app)
# Also creates the CC admins group membership
membership = GroupGroupMembership.objects.get(
parent_group=new_group,
child_group=self.cc_admin_group,
)
self.assertEqual(membership.role, GroupGroupMembership.ADMIN)

@override_settings(ANVIL_DATA_ACCESS_GROUP_PREFIX="foo")
def test_creates_anvil_access_group_different_setting(self):
def test_creates_anvil_access_group_different_setting_data_access_group_prefix(
self,
):
"""View creates a managed group upon when form is valid."""
self.client.force_login(self.user)
pi = UserFactory.create()
# API response to create the associated anvil_access_group.
api_url = (
self.api_client.sam_entry_point + "/api/groups/v1/foo_DBGAP_ACCESS_12498"
)
# CC admins group membership.
self.anvil_response_mock.add(
responses.PUT,
api_url + "/admin/[email protected]",
status=204,
)
self.anvil_response_mock.add(
responses.POST, api_url, status=201, json={"message": "mock message"}
)
Expand All @@ -1846,12 +1887,54 @@ def test_creates_anvil_access_group_different_setting(self):
)
self.assertEqual(response.status_code, 302)
new_object = models.dbGaPApplication.objects.latest("pk")
self.assertEqual(ManagedGroup.objects.count(), 1)
# A new group was created.
new_group = ManagedGroup.objects.latest("pk")
self.assertEqual(new_object.anvil_access_group, new_group)
self.assertEqual(new_group.name, "foo_DBGAP_ACCESS_12498")
self.assertTrue(new_group.is_managed_by_app)
# Also creates the CC admins group membership
membership = GroupGroupMembership.objects.get(
parent_group=new_group,
child_group=self.cc_admin_group,
)
self.assertEqual(membership.role, GroupGroupMembership.ADMIN)

@override_settings(ANVIL_CC_ADMINS_GROUP_NAME="foo")
def test_creates_anvil_access_group_different_setting_cc_admin_group(self):
"""View creates a managed group upon when form is valid."""
admin_group = ManagedGroupFactory.create(name="foo", email="[email protected]")
self.client.force_login(self.user)
pi = UserFactory.create()
# API response to create the associated anvil_access_group.
api_url = (
self.api_client.sam_entry_point
+ "/api/groups/v1/TEST_PRIMED_DBGAP_ACCESS_12498"
)
self.anvil_response_mock.add(
responses.POST, api_url, status=201, json={"message": "mock message"}
)
# CC admins group membership.
self.anvil_response_mock.add(
responses.PUT,
api_url + "/admin/[email protected]",
status=204,
)
response = self.client.post(
self.get_url(), {"principal_investigator": pi.pk, "dbgap_project_id": 12498}
)
self.assertEqual(response.status_code, 302)
new_object = models.dbGaPApplication.objects.latest("pk")
# A new group was created.
new_group = ManagedGroup.objects.latest("pk")
self.assertEqual(new_object.anvil_access_group, new_group)
self.assertEqual(new_group.name, "TEST_PRIMED_DBGAP_ACCESS_12498")
self.assertTrue(new_group.is_managed_by_app)
# Also creates the CC admins group membership
membership = GroupGroupMembership.objects.get(
parent_group=new_group,
child_group=admin_group,
)
self.assertEqual(membership.role, GroupGroupMembership.ADMIN)

def test_manage_group_create_api_error(self):
"""Nothing is created when the form is valid but there is an API error when creating the group."""
Expand All @@ -1878,7 +1961,7 @@ def test_manage_group_create_api_error(self):
self.assertEqual("AnVIL API Error: other error", str(messages[0]))
# No objects were created.
self.assertEqual(models.dbGaPApplication.objects.count(), 0)
self.assertEqual(ManagedGroup.objects.count(), 0)
self.assertEqual(ManagedGroup.objects.count(), 1) # Just the admin group.

def test_managed_group_already_exists_in_app(self):
"""No objects are created if the managed group already exists in the app."""
Expand All @@ -1902,6 +1985,40 @@ def test_managed_group_already_exists_in_app(self):
# No dbGaPApplication was created.
self.assertEqual(models.dbGaPApplication.objects.count(), 0)

def test_admin_group_membership_api_error(self):
"""Nothing is created when the form is valid but there is an API error when creating admin group membership."""
self.client.force_login(self.user)
pi = UserFactory.create()
# API response to create the associated anvil_access_group.
api_url = (
self.api_client.sam_entry_point
+ "/api/groups/v1/TEST_PRIMED_DBGAP_ACCESS_1"
)
self.anvil_response_mock.add(
responses.POST, api_url, status=201, json={"message": "other error"}
)
# CC admins group membership.
self.anvil_response_mock.add(
responses.PUT,
api_url + "/admin/[email protected]",
status=404,
json={"message": "other error"},
)
response = self.client.post(
self.get_url(), {"principal_investigator": pi.pk, "dbgap_project_id": 1}
)
self.assertEqual(response.status_code, 200)
# The form is valid...
form = response.context["form"]
self.assertTrue(form.is_valid())
# ...but there was some error from the API.
messages = list(response.context["messages"])
self.assertEqual(len(messages), 1)
self.assertEqual("AnVIL API Error: other error", str(messages[0]))
# No objects were created.
self.assertEqual(models.dbGaPApplication.objects.count(), 0)
self.assertEqual(ManagedGroup.objects.count(), 1) # Just the admin group.


class dbGaPDataAccessSnapshotCreateTest(dbGaPResponseTestMixin, TestCase):
"""Tests for the dbGaPDataAccessRequestCreateFromJson view."""
Expand Down
25 changes: 23 additions & 2 deletions primed/dbgap/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
)
from anvil_consortium_manager.models import (
AnVILProjectManagerAccess,
GroupGroupMembership,
ManagedGroup,
Workspace,
)
Expand Down Expand Up @@ -205,7 +206,6 @@ class dbGaPApplicationCreate(
anvil_access_group_pattern = "PRIMED_DBGAP_ACCESS_{project_id}"
ERROR_CREATING_GROUP = "Error creating Managed Group in app."

# @transaction.atomic
def form_valid(self, form):
"""Create a managed group in the app on AnVIL and link it to this application."""
project_id = form.cleaned_data["dbgap_project_id"]
Expand All @@ -229,7 +229,28 @@ def form_valid(self, form):
self.request, messages.ERROR, "AnVIL API Error: " + str(e)
)
return self.render_to_response(self.get_context_data(form=form))
managed_group.save()
# Need to wrap this entire block in a transaction because we are creating multiple objects, and don't want
# any of them to be saved if the API call fails.
try:
with transaction.atomic():
managed_group.save()
# Create the dbgap access group.
cc_admins_group = ManagedGroup.objects.get(
name=settings.ANVIL_CC_ADMINS_GROUP_NAME
)
membership = GroupGroupMembership.objects.create(
parent_group=managed_group,
child_group=cc_admins_group,
role=GroupGroupMembership.ADMIN,
)
membership.full_clean()
membership.anvil_create()
membership.save()
except AnVILAPIError as e:
messages.add_message(
self.request, messages.ERROR, "AnVIL API Error: " + str(e)
)
return self.render_to_response(self.get_context_data(form=form))
form.instance.anvil_access_group = managed_group
return super().form_valid(form)

Expand Down

0 comments on commit 77eb65e

Please sign in to comment.