From 579c8a24bf6021a2c16a324160feb7a1ef74f62c Mon Sep 17 00:00:00 2001 From: John Tordoff <> Date: Tue, 3 Dec 2024 08:44:12 -0500 Subject: [PATCH] add validation to institutional admins --- osf/models/contributor.py | 19 +++++++++++++++++++ osf/utils/machines.py | 28 +++++++++++++++++++++------- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/osf/models/contributor.py b/osf/models/contributor.py index 4c5807f3ee2a..c755013a7588 100644 --- a/osf/models/contributor.py +++ b/osf/models/contributor.py @@ -30,6 +30,12 @@ def permission(self): class Contributor(AbstractBaseContributor): node = models.ForeignKey('AbstractNode', on_delete=models.CASCADE) + institutional_admin = models.ForeignKey( + 'Institution', + on_delete=models.CASCADE, + null=True, + blank=True + ) @property def _id(self): @@ -40,6 +46,19 @@ class Meta: # Make contributors orderable # NOTE: Adds an _order column order_with_respect_to = 'node' + constraints = [ + models.CheckConstraint( + check=~(models.Q(visible=True) & models.Q(institutional_admin__isnull=False)), + name="no_visible_with_institutional_admin", + ) + ] + + def clean(self): + """Validate the contributor instance.""" + if self.visible and self.institutional_admin is not None: + raise ValidationError( + "A contributor cannot be visible and associated with an institutional admin." + ) class PreprintContributor(AbstractBaseContributor): diff --git a/osf/utils/machines.py b/osf/utils/machines.py index 8e4b8f073385..1c1ff06d3f22 100644 --- a/osf/utils/machines.py +++ b/osf/utils/machines.py @@ -192,19 +192,33 @@ class NodeRequestMachine(BaseMachine): def save_changes(self, ev): """ Handles contributorship changes and state transitions """ + node_request = self.machineable if ev.event.name == DefaultTriggers.EDIT_COMMENT.value and self.action is not None: - self.machineable.comment = self.action.comment - self.machineable.save() + node_request.comment = self.action.comment + node_request.save() if ev.event.name == DefaultTriggers.ACCEPT.value: - if not self.machineable.target.is_contributor(self.machineable.creator): - contributor_permissions = ev.kwargs.get('permissions', permissions.READ) - self.machineable.target.add_contributor( - self.machineable.creator, + # Add the creator as a contributor if not already + if not node_request.target.is_contributor(node_request.creator): + contributor_permissions = ev.kwargs.get('permissions', node_request.requested_permissions or permissions.READ) + node_request.target.add_contributor( + node_request.creator, auth=Auth(ev.kwargs['user']), permissions=contributor_permissions, visible=ev.kwargs.get('visible', True), - send_email=f'{self.machineable.request_type}_request') + send_email=f'{node_request.request_type}_request', + ) + + # Add the institutional admin if an institution is associated + if node_request.institution: + institutional_admin = node_request.institution + if not node_request.target.contributor_set.filter(user=node_request.creator, institutional_admin=institutional_admin).exists(): + node_request.target.contributor_set.create( + user=node_request.creator, + node=node_request.target, + visible=False, # Institutional admins should not be publicly visible contributors EVER + institutional_admin=institutional_admin, + ) def resubmission_allowed(self, ev): # TODO: [PRODUCT-395]