Пример #1
0
    def validate_codeowners_associations(self, attrs, project):
        from sentry.api.endpoints.project_codeowners import validate_association
        from sentry.models import ExternalActor, UserEmail, actor_type_to_string
        from sentry.ownership.grammar import parse_code_owners

        # Get list of team/user names from CODEOWNERS file
        team_names, usernames, emails = parse_code_owners(attrs["raw"])

        # Check if there exists Sentry users with the emails listed in CODEOWNERS
        user_emails = UserEmail.objects.filter(
            email__in=emails,
            user__sentry_orgmember_set__organization=project.organization,
        )

        # Check if the usernames/teamnames have an association
        external_actors = ExternalActor.objects.filter(
            external_name__in=usernames + team_names,
            organization=project.organization,
        )

        # Convert CODEOWNERS into IssueOwner syntax
        users_dict = {}
        teams_dict = {}
        teams_without_access = []
        for external_actor in external_actors:
            type = actor_type_to_string(external_actor.actor.type)
            if type == "user":
                user = external_actor.actor.resolve()
                users_dict[external_actor.external_name] = user.email
            elif type == "team":
                team = external_actor.actor.resolve()
                # make sure the sentry team has access to the project
                # tied to the codeowner
                if project in team.get_projects():
                    teams_dict[external_actor.external_name] = f"#{team.slug}"
                else:
                    teams_without_access.append(f"#{team.slug}")

        emails_dict = {item.email: item.email for item in user_emails}
        associations = {**users_dict, **teams_dict, **emails_dict}

        errors = {
            "missing_user_emails":
            validate_association(emails, user_emails, "emails"),
            "missing_external_users":
            validate_association(usernames, external_actors, "usernames"),
            "missing_external_teams":
            validate_association(team_names, external_actors, "team names"),
            "teams_without_access":
            teams_without_access,
        }
        return associations, errors
Пример #2
0
    def validate(self, attrs: Mapping[str, Any]) -> Mapping[str, Any]:
        # If it already exists, set default attrs with existing values
        if self.instance:
            attrs = {
                "raw": self.instance.raw,
                "code_mapping_id":
                self.instance.repository_project_path_config,
                **attrs,
            }

        if not attrs.get("raw", "").strip():
            return attrs

        external_association_err: List[str] = []
        # Get list of team/user names from CODEOWNERS file
        team_names, usernames, emails = parse_code_owners(attrs["raw"])

        # Check if there exists Sentry users with the emails listed in CODEOWNERS
        user_emails = UserEmail.objects.filter(
            email__in=emails,
            user__sentry_orgmember_set__organization=self.context["project"].
            organization,
        )

        user_emails_diff = validate_association(emails, user_emails, "emails")
        external_association_err.extend(user_emails_diff)

        # Check if the usernames have an association
        external_actors = ExternalActor.objects.filter(
            external_name__in=usernames + team_names,
            organization=self.context["project"].organization,
        )

        external_users_diff = validate_association(usernames, external_actors,
                                                   "usernames")
        external_association_err.extend(external_users_diff)

        external_teams_diff = validate_association(team_names, external_actors,
                                                   "team names")
        external_association_err.extend(external_teams_diff)

        if len(external_association_err):
            raise serializers.ValidationError(
                {"raw": "\n".join(external_association_err)})

        # Convert CODEOWNERS into IssueOwner syntax
        users_dict = {}
        teams_dict = {}
        for external_actor in external_actors:
            type = actor_type_to_string(external_actor.actor.type)
            if type == "user":
                user = external_actor.actor.resolve()
                users_dict[external_actor.external_name] = user.email
            elif type == "team":
                team = external_actor.actor.resolve()
                teams_dict[external_actor.external_name] = f"#{team.slug}"

        emails_dict = {email: email for email in emails}
        associations = {**users_dict, **teams_dict, **emails_dict}

        issue_owner_rules = convert_codeowners_syntax(attrs["raw"],
                                                      associations,
                                                      attrs["code_mapping_id"])

        # Convert IssueOwner syntax into schema syntax
        validated_data = ProjectOwnershipSerializer(
            context=self.context).validate({"raw": issue_owner_rules})

        return {**validated_data, **attrs}
Пример #3
0
def test_parse_code_owners():
    assert parse_code_owners(codeowners_fixture_data) == (
        ["@getsentry/frontend", "@getsentry/docs", "@getsentry/ecosystem"],
        ["@NisanthanNanthakumar", "@AnotherUser"],
        ["*****@*****.**"],
    )
Пример #4
0
    def validate_codeowners_associations(self, codeowners, project):
        from sentry.api.endpoints.project_codeowners import validate_association
        from sentry.models import (
            ExternalActor,
            OrganizationMember,
            OrganizationMemberTeam,
            Project,
            UserEmail,
            actor_type_to_string,
        )
        from sentry.ownership.grammar import parse_code_owners
        from sentry.types.integrations import ExternalProviders

        # Get list of team/user names from CODEOWNERS file
        team_names, usernames, emails = parse_code_owners(codeowners)

        # Check if there exists Sentry users with the emails listed in CODEOWNERS
        user_emails = UserEmail.objects.filter(
            email__in=emails,
            user__sentry_orgmember_set__organization=project.organization,
        )

        # Check if the usernames/teamnames have an association
        external_actors = ExternalActor.objects.filter(
            external_name__in=usernames + team_names,
            organization=project.organization,
            provider__in=[
                ExternalProviders.GITHUB.value, ExternalProviders.GITLAB.value
            ],
        )

        # Convert CODEOWNERS into IssueOwner syntax
        users_dict = {}
        teams_dict = {}
        teams_without_access = []
        users_without_access = []
        for external_actor in external_actors:
            type = actor_type_to_string(external_actor.actor.type)
            if type == "user":
                user = external_actor.actor.resolve()
                organization_members_ids = OrganizationMember.objects.filter(
                    user_id=user.id,
                    organization_id=project.organization_id).values_list(
                        "id", flat=True)
                team_ids = OrganizationMemberTeam.objects.filter(
                    organizationmember_id__in=Subquery(
                        organization_members_ids)).values_list("team_id",
                                                               flat=True)
                projects = Project.objects.get_for_team_ids(Subquery(team_ids))

                if project in projects:
                    users_dict[external_actor.external_name] = user.email
                else:
                    users_without_access.append(f"{user.get_display_name()}")
            elif type == "team":
                team = external_actor.actor.resolve()
                # make sure the sentry team has access to the project
                # tied to the codeowner
                if project in team.get_projects():
                    teams_dict[external_actor.external_name] = f"#{team.slug}"
                else:
                    teams_without_access.append(f"#{team.slug}")

        emails_dict = {item.email: item.email for item in user_emails}
        associations = {**users_dict, **teams_dict, **emails_dict}

        errors = {
            "missing_user_emails":
            validate_association(emails, user_emails, "emails"),
            "missing_external_users":
            validate_association(usernames, external_actors, "usernames"),
            "missing_external_teams":
            validate_association(team_names, external_actors, "team names"),
            "teams_without_access":
            teams_without_access,
            "users_without_access":
            users_without_access,
        }
        return associations, errors
Пример #5
0
    def validate(self, attrs):
        # If it already exists, set default attrs with existing values
        if self.instance:
            attrs = {
                "raw": self.instance.raw,
                "code_mapping_id":
                self.instance.repository_project_path_config,
                **attrs,
            }

        if not attrs.get("raw", "").strip():
            return attrs
        external_association_err = []
        # Get list of team/user names from CODEOWNERS file
        teamnames, usernames, emails = parse_code_owners(attrs["raw"])

        # Check if there exists Sentry users with the emails listed in CODEOWNERS
        user_emails = UserEmail.objects.filter(email__in=emails)
        user_emails_diff = self._validate_association(emails, user_emails,
                                                      "emails")

        external_association_err.extend(user_emails_diff)

        # Check if the usernames have an association
        external_users = ExternalUser.objects.filter(
            external_name__in=usernames,
            organizationmember__organization=self.context["project"].
            organization,
        )

        external_users_diff = self._validate_association(
            usernames, external_users, "usernames")

        external_association_err.extend(external_users_diff)

        # Check if the team names have an association
        external_teams = ExternalTeam.objects.filter(
            external_name__in=teamnames,
            team__organization=self.context["project"].organization,
        )

        external_teams_diff = self._validate_association(
            teamnames, external_teams, "team names")

        external_association_err.extend(external_teams_diff)

        if len(external_association_err):
            raise serializers.ValidationError(
                {"raw": "\n".join(external_association_err)})

        # Convert CODEOWNERS into IssueOwner syntax
        users_dict = {
            user.external_name: user.organizationmember.user.email
            for user in external_users
        }
        teams_dict = {
            team.external_name: f"#{team.team.slug}"
            for team in external_teams
        }
        emails_dict = {email: email for email in emails}
        associations = {**users_dict, **teams_dict, **emails_dict}

        issue_owner_rules = convert_codeowners_syntax(attrs["raw"],
                                                      associations,
                                                      attrs["code_mapping_id"])

        # Convert IssueOwner syntax into schema syntax
        validated_data = ProjectOwnershipSerializer(
            context=self.context).validate({"raw": issue_owner_rules})

        return {**validated_data, **attrs}