From 56308488f4a5c86203ab634ea0f23c662953ffea Mon Sep 17 00:00:00 2001 From: Charlie Luo Date: Mon, 30 Mar 2026 11:05:51 -0700 Subject: [PATCH 1/2] feat(assignments): allow assigning any org member when open membership is on With open membership (allow_joinleave), users can join any team and thus gain access to any project. The user assignee validation now checks org membership instead of project membership in this case. Co-authored-by: Claude --- .../api/helpers/group_index/validators/group.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/sentry/api/helpers/group_index/validators/group.py b/src/sentry/api/helpers/group_index/validators/group.py index 336b8d8f656ab7..9cce916edf00f9 100644 --- a/src/sentry/api/helpers/group_index/validators/group.py +++ b/src/sentry/api/helpers/group_index/validators/group.py @@ -83,12 +83,16 @@ class GroupValidator(serializers.Serializer[Group]): snoozeDuration = serializers.IntegerField(allow_null=True) def validate_assignedTo(self, value: Actor) -> Actor: - if ( - value - and value.is_user - and not self.context["project"].member_set.filter(user_id=value.id).exists() - ): - raise serializers.ValidationError("Cannot assign to non-team member") + if value and value.is_user: + organization = self.context["organization"] + project = self.context["project"] + # With open membership, any org member can be assigned since they + # can join any team (and thus gain project access) at will. + if organization.flags.allow_joinleave: + if not organization.member_set.filter(user_id=value.id).exists(): + raise serializers.ValidationError("Cannot assign to non-organization member") + elif not project.member_set.filter(user_id=value.id).exists(): + raise serializers.ValidationError("Cannot assign to non-team member") if ( value From 3eb8f39d90d1657eabcae028e79167653d05465f Mon Sep 17 00:00:00 2001 From: Charlie Luo Date: Mon, 30 Mar 2026 11:09:42 -0700 Subject: [PATCH 2/2] feat(assignments): allow assigning any org team when open membership is on With open membership (allow_joinleave), skip the team-must-have-project-access check. The team's org membership is already validated upstream by ActorField.to_internal_value via parse_and_validate_actor. Co-authored-by: Claude --- .../helpers/group_index/validators/group.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/sentry/api/helpers/group_index/validators/group.py b/src/sentry/api/helpers/group_index/validators/group.py index 9cce916edf00f9..2431c5abd306e4 100644 --- a/src/sentry/api/helpers/group_index/validators/group.py +++ b/src/sentry/api/helpers/group_index/validators/group.py @@ -94,14 +94,16 @@ def validate_assignedTo(self, value: Actor) -> Actor: elif not project.member_set.filter(user_id=value.id).exists(): raise serializers.ValidationError("Cannot assign to non-team member") - if ( - value - and value.is_team - and not self.context["project"].teams.filter(id=value.id).exists() - ): - raise serializers.ValidationError( - "Cannot assign to a team without access to the project" - ) + if value and value.is_team: + organization = self.context["organization"] + project = self.context["project"] + # With open membership, allow assigning any org team. The team's + # org membership is already validated by ActorField.to_internal_value. + if not organization.flags.allow_joinleave: + if not project.teams.filter(id=value.id).exists(): + raise serializers.ValidationError( + "Cannot assign to a team without access to the project" + ) return value