Ejemplo n.º 1
0
    def test_simple(self):
        project = self.create_project()

        self.login_as(user=self.user)

        plugins.get("webhooks").enable(project)

        url = reverse(
            "sentry-api-0-project-plugin-details",
            kwargs={
                "organization_slug": project.organization.slug,
                "project_slug": project.slug,
                "plugin_id": "webhooks",
            },
        )
        audit = AuditLogEntry.objects.filter(target_object=project.id)

        assert not audit

        response = self.client.delete(url)
        audit = AuditLogEntry.objects.get(target_object=project.id)

        assert audit.event == 112
        assert response.status_code == 204, (response.status_code,
                                             response.content)
        assert ProjectOption.objects.get(key="webhooks:enabled",
                                         project=project).value is False
Ejemplo n.º 2
0
 def test_configured_multiple_projects(self):
     plugins.get("trello").set_option("key", "some_value", self.projectA)
     plugins.get("trello").set_option("key", "another_value", self.projectB)
     response = self.client.get(self.url)
     projectList = list(
         filter(lambda x: x["slug"] == "trello",
                response.data))[0]["projectList"]
     assert list(
         filter(lambda x: x["projectId"] == self.projectA.id,
                projectList))[0] == {
                    "projectId": self.projectA.id,
                    "projectSlug": self.projectA.slug,
                    "projectName": self.projectA.name,
                    "enabled": False,
                    "configured": True,
                    "projectPlatform": None,
                }
     assert list(
         filter(lambda x: x["projectId"] == self.projectB.id,
                projectList))[0] == {
                    "projectId": self.projectB.id,
                    "projectSlug": self.projectB.slug,
                    "projectName": self.projectB.name,
                    "enabled": False,
                    "configured": True,
                    "projectPlatform": "react",
                }
 def test_disabled_proejct(self):
     plugins.get("trello").enable(self.projectA)
     plugins.get("trello").set_option("key", "some_value", self.projectA)
     self.projectA.status = 1
     self.projectA.save()
     response = self.client.get(self.url)
     assert list(filter(lambda x: x["slug"] == "trello", response.data))[0]["projectList"] == []
Ejemplo n.º 4
0
    def test_simple(self, test_configuration):
        plugins.get("webhooks").disable(self.project)

        self.get_success_response(self.project.organization.slug,
                                  self.project.slug, "webhooks")

        audit = AuditLogEntry.objects.get(target_object=self.project.id)
        assert audit.event == 110
        assert ProjectOption.objects.get(key="webhooks:enabled",
                                         project=self.project).value is True
        audit.delete()

        # Testing the Plugin
        self.get_success_response(self.project.organization.slug,
                                  self.project.slug, "webhooks",
                                  **{"test": True})
        test_configuration.assert_called_once_with(self.project)

        # Reset the plugin
        response = self.get_success_response(self.project.organization.slug,
                                             self.project.slug, "webhooks",
                                             **{"reset": True})
        audit = AuditLogEntry.objects.get(target_object=self.project.id)
        test_configuration.assert_called_once_with(self.project)
        assert audit.event == 111

        configs = response.data.get("config")

        for config in configs:
            assert config.get("value") is None
Ejemplo n.º 5
0
    def setUp(self):
        self.projectA = self.create_project()
        self.projectB = self.create_project(
            organization=self.projectA.organization)

        plugins.get("webhooks").enable(self.projectA)
        plugins.get("mail").enable(self.projectB)

        self.login_as(user=self.user)
Ejemplo n.º 6
0
    def test_simple(self):
        plugins.get("webhooks").enable(self.project)

        self.get_success_response(self.project.organization.slug,
                                  self.project.slug, "webhooks")

        audit = AuditLogEntry.objects.get(target_object=self.project.id)
        assert audit.event == 112
        assert (ProjectOption.objects.get(key="webhooks:enabled",
                                          project=self.project).value is False)
Ejemplo n.º 7
0
 def test_configured_and_enabled(self):
     plugins.get("trello").enable(self.projectA)
     plugins.get("trello").set_option("key", "some_value", self.projectA)
     response = self.client.get(self.url)
     assert filter(lambda x: x["slug"] == "trello", response.data)[0]["projectList"] == [
         {
             "projectId": self.projectA.id,
             "projectSlug": self.projectA.slug,
             "projectName": self.projectA.name,
             "enabled": True,
             "configured": True,
         }
     ]
def get_provider_name(provider_type, provider_slug):
    """
    The things that users think of as "integrations" are actually three
    different things: integrations, plugins, and sentryapps. A user requesting
    than an integration be installed only actually knows the "provider" they
    want and not what type they want. This function looks up the display name
    for the integration they want installed.

    :param provider_type: One of: "first_party", "plugin", or "sentry_app".
    :param provider_slug: The unique identifier for the provider.
    :return: The display name for the provider.

    :raises: ValueError if provider_type is not one of the three from above.
    :raises: RuntimeError if the provider is not found.
    """
    try:
        if provider_type == "first_party":
            return integrations.get(provider_slug).name
        elif provider_type == "plugin":
            return plugins.get(provider_slug).title
        elif provider_type == "sentry_app":
            return SentryApp.objects.get(slug=provider_slug).name
        else:
            raise ValueError(f"Invalid providerType {provider_type}")
    except (KeyError, SentryApp.DoesNotExist):
        raise RuntimeError(f"Provider {provider_slug} not found")
Ejemplo n.º 9
0
    def test_disable_plugin_when_fully_migrated(self):
        self._stub_github()

        project = Project.objects.create(organization_id=self.organization.id)

        plugin = plugins.get("github")
        plugin.enable(project)

        # Accessible to new Integration - mocked in _stub_github
        Repository.objects.create(
            organization_id=self.organization.id,
            name="Test-Organization/foo",
            url="https://github.com/Test-Organization/foo",
            provider="github",
            external_id="123",
            config={"name": "Test-Organization/foo"},
        )

        # Enabled before
        assert "github" in [p.slug for p in plugins.for_project(project)]

        with self.tasks():
            self.assert_setup_flow()

        # Disabled after Integration installed
        assert "github" not in [p.slug for p in plugins.for_project(project)]
    def test_get(self):
        project = self.create_project()

        issues = plugins.get("issuetrackingplugin2")
        with patch.object(issues, "is_hidden", return_value=True):
            self.login_as(user=self.user)

            url = reverse(
                "sentry-api-0-project-plugins",
                kwargs={
                    "organization_slug": project.organization.slug,
                    "project_slug": project.slug,
                },
            )
            response = self.client.get(url)

        assert response.status_code == 200, (response.status_code,
                                             response.content)
        assert len(response.data) >= 9

        auto_tag = response.data[0]
        assert auto_tag["name"] == "Auto Tag: Browsers"
        assert auto_tag["enabled"] is True
        assert auto_tag["isHidden"] is False
        self.assert_plugin_shape(auto_tag)

        issues = filter(lambda p: p["slug"] == "issuetrackingplugin2",
                        response.data)[0]
        assert issues["name"] == "IssueTrackingPlugin2"
        assert issues["enabled"] is False
        assert issues["isHidden"] is True
        self.assert_plugin_shape(issues)
Ejemplo n.º 11
0
    def test_simple(self, test_configuration):
        project = self.create_project()

        self.login_as(user=self.user)

        plugins.get("webhooks").disable(project)

        url = reverse(
            "sentry-api-0-project-plugin-details",
            kwargs={
                "organization_slug": project.organization.slug,
                "project_slug": project.slug,
                "plugin_id": "webhooks",
            },
        )
        audit = AuditLogEntry.objects.filter(target_object=project.id)

        assert not audit

        response = self.client.post(url)
        audit = AuditLogEntry.objects.get(target_object=project.id)

        assert audit.event == 110
        assert response.status_code == 201, (response.status_code,
                                             response.content)
        assert ProjectOption.objects.get(key="webhooks:enabled",
                                         project=project).value is True
        audit.delete()

        # Testing the Plugin
        response = self.client.post(url, {"test": True})
        test_configuration.assert_called_once_with(project)
        assert response.status_code == 200, (response.status_code,
                                             response.content)

        # Reset the plugin
        response = self.client.post(url, {"reset": True})
        audit = AuditLogEntry.objects.get(target_object=project.id)
        test_configuration.assert_called_once_with(project)
        assert audit.event == 111
        assert response.status_code == 200, (response.status_code,
                                             response.content)

        configs = response.data.get("config")

        for config in configs:
            assert config.get("value") is None
Ejemplo n.º 12
0
 def setUp(self):
     super(IssuePlugin2GroupActionTest, self).setUp()
     self.project = self.create_project()
     self.plugin_instance = plugins.get(slug="issuetrackingplugin2")
     min_ago = iso_format(before_now(minutes=1))
     self.event = self.store_event(
         data={"timestamp": min_ago, "fingerprint": ["group-1"]}, project_id=self.project.id
     )
     self.group = self.event.group
Ejemplo n.º 13
0
 def setUp(self):
     self.organization = self.create_organization()
     self.user = self.create_user()
     self.user_2 = self.create_user()
     self.team = self.create_team(self.organization,
                                  members=[self.user, self.user_2])
     self.project = self.create_project(organization=self.organization,
                                        teams=[self.team])
     self.mail = plugins.get("mail")
Ejemplo n.º 14
0
    def test_disable_for_all_projects(self):
        plugin = plugins.get("example")
        plugin.enable(self.project)

        assert plugin in plugins.for_project(self.project)

        self.migrator.disable_for_all_projects(plugin)

        assert plugin not in plugins.for_project(self.project)
Ejemplo n.º 15
0
 def setUp(self):
     self.organization = self.create_organization()
     self.user = self.create_user()
     self.user_2 = self.create_user()
     self.team = self.create_team(self.organization,
                                  members=[self.user, self.user_2])
     self.project = self.create_project(organization=self.organization,
                                        teams=[self.team])
     self.project.flags.has_issue_alerts_targeting = False
     self.project.save()
     self.mail = plugins.get("mail")
Ejemplo n.º 16
0
    def post(self, request, plugin_id, project_id, signature):
        try:
            project = Project.objects.get_from_cache(id=project_id)
        except Project.DoesNotExist:
            logger.warn(
                "release-webhook.invalid-project",
                extra={"project_id": project_id, "plugin_id": plugin_id},
            )
            return HttpResponse(status=404)

        logger.info(
            "release-webhook.incoming", extra={"project_id": project_id, "plugin_id": plugin_id}
        )

        token = ProjectOption.objects.get_value(project, "sentry:release-token")

        if token is None:
            logger.warn(
                "release-webhook.missing-token",
                extra={"project_id": project_id, "plugin_id": plugin_id},
            )
            return HttpResponse(status=403)

        if not self.verify(plugin_id, project_id, token, signature):
            logger.warn(
                "release-webhook.invalid-signature",
                extra={"project_id": project_id, "plugin_id": plugin_id},
            )
            return HttpResponse(status=403)

        if plugin_id == "builtin":
            return self._handle_builtin(request, project)

        plugin = plugins.get(plugin_id)
        if not plugin.is_enabled(project):
            logger.warn(
                "release-webhook.plugin-disabled",
                extra={"project_id": project_id, "plugin_id": plugin_id},
            )
            return HttpResponse(status=403)

        cls = plugin.get_release_hook()
        hook = cls(project)
        try:
            hook.handle(request)
        except HookValidationError as exc:
            return HttpResponse(
                status=400,
                content=json.dumps({"error": six.text_type(exc)}),
                content_type="application/json",
            )

        return HttpResponse(status=204)
Ejemplo n.º 17
0
    def after(self, event, state):
        service = self.get_option("service")

        extra = {"event_id": event.event_id}
        if not service:
            self.logger.info("rules.fail.is_configured", extra=extra)
            return

        plugin = None
        app = None
        try:
            app = SentryApp.objects.get(slug=service)
        except SentryApp.DoesNotExist:
            pass

        if app:
            kwargs = {"sentry_app": app}
            metrics.incr("notifications.sent",
                         instance=app.slug,
                         skip_internal=False)
            yield self.future(notify_sentry_app, **kwargs)

        try:
            plugin = plugins.get(service)
        except KeyError:
            if not app:
                # If we can't find the sentry app OR plugin,
                # we've removed the plugin no need to error, just skip.
                extra["plugin"] = service
                self.logger.info("rules.fail.plugin_does_not_exist",
                                 extra=extra)
                return

        if plugin:
            if not plugin.is_enabled(self.project):
                extra["project_id"] = self.project.id
                self.logger.info("rules.fail.is_enabled", extra=extra)
                return

            group = event.group

            if not plugin.should_notify(group=group, event=event):
                extra["group_id"] = group.id
                self.logger.info("rule.fail.should_notify", extra=extra)
                return

            metrics.incr("notifications.sent",
                         instance=plugin.slug,
                         skip_internal=False)
            yield self.future(plugin.rule_notify)
Ejemplo n.º 18
0
def plugin_post_process_group(plugin_slug, event, **kwargs):
    """
    Fires post processing hooks for a group.
    """
    set_current_project(event.project_id)

    from sentry.plugins.base import plugins

    plugin = plugins.get(plugin_slug)
    safe_execute(plugin.post_process,
                 event=event,
                 group=event.group,
                 expected_errors=(PluginError, ),
                 **kwargs)
Ejemplo n.º 19
0
def plugin_post_process_group(plugin_slug, event, **kwargs):
    """
    Fires post processing hooks for a group.
    """
    with configure_scope() as scope:
        scope.set_tag("project", event.project_id)

    from sentry.plugins.base import plugins

    plugin = plugins.get(plugin_slug)
    safe_execute(
        plugin.post_process,
        event=event,
        group=event.group,
        expected_errors=(PluginError,),
        **kwargs
    )
 def test_sort_by_slug(self):
     another = self.create_project(slug="another")
     plugins.get("trello").set_option("key", "some_value", self.projectA)
     plugins.get("trello").set_option("key", "some_value", self.projectB)
     plugins.get("trello").set_option("key", "some_value", another)
     url = self.url + "?plugins=trello"
     response = self.client.get(url)
     assert map(lambda x: x["projectSlug"], response.data[0]["projectList"]) == [
         "another",
         "proj_a",
         "proj_b",
     ]
Ejemplo n.º 21
0
    def handle(self, request, organization, project, group_id, slug):
        group = get_object_or_404(Group, pk=group_id, project=project)

        try:
            plugin = plugins.get(slug)
        except KeyError:
            raise Http404("Plugin not found")

        GroupMeta.objects.populate_cache([group])

        response = plugin.get_view_response(request, group)
        if response:
            return response

        redirect = request.META.get("HTTP_REFERER", "")
        if not is_safe_url(redirect, allowed_hosts=(request.get_host(),)):
            redirect = f"/{organization.slug}/{group.project.slug}/"
        return HttpResponseRedirect(redirect)
Ejemplo n.º 22
0
    def test_integrated(self):
        event = self.store_event(data={}, project_id=self.project.id)
        action_data = {
            "id": "sentry.rules.actions.notify_event.NotifyEventAction"
        }
        condition_data = {
            "id": "sentry.rules.conditions.every_event.EveryEventCondition"
        }

        Rule.objects.filter(project=event.project).delete()
        rule = Rule.objects.create(project=event.project,
                                   data={
                                       "conditions": [condition_data],
                                       "actions": [action_data]
                                   })

        rp = RuleProcessor(
            event,
            is_new=True,
            is_regression=True,
            is_new_group_environment=True,
            has_reappeared=True,
        )
        results = list(rp.apply())
        assert len(results) == 1
        callback, futures = results[0]
        assert callback == plugins.get("mail").rule_notify
        assert len(futures) == 1
        assert futures[0].rule == rule
        assert futures[0].kwargs == {}

        # should not apply twice due to default frequency
        results = list(rp.apply())
        assert len(results) == 0

        # now ensure that moving the last update backwards
        # in time causes the rule to trigger again
        GroupRuleStatus.objects.filter(
            rule=rule).update(last_active=timezone.now() -
                              timedelta(minutes=Rule.DEFAULT_FREQUENCY + 1))

        results = list(rp.apply())
        assert len(results) == 1
Ejemplo n.º 23
0
    def test_plugin_external_issue_annotation(self):
        group = self.create_group()
        GroupMeta.objects.create(group=group, key="trello:tid", value="134")

        plugins.get("trello").enable(group.project)
        plugins.get("trello").set_option("key", "some_value", group.project)
        plugins.get("trello").set_option("token", "another_value", group.project)

        self.login_as(user=self.user)

        url = f"/api/0/issues/{group.id}/"
        response = self.client.get(url, format="json")

        assert response.data["annotations"] == ['<a href="https://trello.com/c/134">Trello-134</a>']
Ejemplo n.º 24
0
    def test_disable_plugin_when_fully_migrated(self):
        project = Project.objects.create(organization_id=self.organization.id)

        plugin = plugins.get("bitbucket")
        plugin.enable(project)

        # Accessible to new Integration
        Repository.objects.create(
            organization_id=self.organization.id,
            name="sentryuser/repo",
            url="https://bitbucket.org/sentryuser/repo",
            provider="bitbucket",
            external_id="123456",
            config={"name": "sentryuser/repo"},
        )

        self.client.post(self.path, data=self.data_from_bitbucket)

        integration = Integration.objects.get(provider=self.provider,
                                              external_id=self.client_key)

        responses.add(
            responses.GET,
            "https://api.bitbucket.org/2.0/repositories/sentryuser/repo/hooks",
            json={"values": [{
                "description": "sentry-bitbucket-repo-hook"
            }]},
        )

        assert "bitbucket" in [p.slug for p in plugins.for_project(project)]

        with self.tasks():
            BitbucketIntegrationProvider().post_install(
                integration, self.organization)

            assert "bitbucket" not in [
                p.slug for p in plugins.for_project(project)
            ]
Ejemplo n.º 25
0
def get_provider_name(provider_type: str, provider_slug: str) -> str | None:
    """
    The things that users think of as "integrations" are actually three
    different things: integrations, plugins, and sentryapps. A user requesting
    than an integration be installed only actually knows the "provider" they
    want and not what type they want. This function looks up the display name
    for the integration they want installed.

    :param provider_type: One of: "first_party", "plugin", or "sentry_app".
    :param provider_slug: The unique identifier for the provider.
    :return: The display name for the provider or None.
    """
    if provider_type == "first_party":
        if integrations.exists(provider_slug):
            return integrations.get(provider_slug).name
    elif provider_type == "plugin":
        if plugins.exists(provider_slug):
            return plugins.get(provider_slug).title
    elif provider_type == "sentry_app":
        sentry_app = SentryApp.objects.filter(slug=provider_slug).first()
        if sentry_app:
            return sentry_app.name
    return None
Ejemplo n.º 26
0
    def get(self, request, organization):

        """
        List one or more plugin configurations, including a `projectList` for each plugin which contains
        all the projects that have that specific plugin both configured and enabled.

        - similar to the `OrganizationPluginsEndpoint`, and can eventually replace it

        :qparam plugins array[string]: an optional list of plugin ids (slugs) if you want specific plugins.
                                    If not set, will return configurations for all plugins.
        """

        desired_plugins = []
        for slug in request.GET.getlist("plugins") or ():
            # if the user request a plugin that doesn't exist, throw 404
            try:
                desired_plugins.append(plugins.get(slug))
            except KeyError:
                return Response({"detail": "Plugin %s not found" % slug}, status=404)

        # if no plugins were specified, grab all plugins but limit by those that have the ability to be configured
        if not desired_plugins:
            desired_plugins = list(plugins.plugin_that_can_be_configured())

        # `keys_to_check` are the ProjectOption keys that tell us if a plugin is enabled (e.g. `plugin:enabled`) or are
        # configured properly, meaning they have the required information - plugin.required_field - needed for the
        # plugin to work (ex:`opsgenie:api_key`)
        keys_to_check = []
        for plugin in desired_plugins:
            keys_to_check.append("%s:enabled" % plugin.slug)
            if plugin.required_field:
                keys_to_check.append("%s:%s" % (plugin.slug, plugin.required_field))

        # Get all the project options for org that have truthy values
        project_options = ProjectOption.objects.filter(
            key__in=keys_to_check, project__organization=organization
        ).exclude(value__in=[False, ""])

        """
        This map stores info about whether a plugin is configured and/or enabled
        {
            "plugin_slug": {
                "project_id": { "enabled": True, "configured": False },
            },
        }
        """
        info_by_plugin_project = {}
        for project_option in project_options:
            [slug, field] = project_option.key.split(":")
            project_id = project_option.project_id

            # first add to the set of all projects by plugin
            info_by_plugin_project.setdefault(slug, {}).setdefault(
                project_id, {"enabled": False, "configured": False}
            )

            # next check if enabled
            if field == "enabled":
                info_by_plugin_project[slug][project_id]["enabled"] = True
            # if the projectoption is not the enable field, it's configuration field
            else:
                info_by_plugin_project[slug][project_id]["configured"] = True

        # get the IDs of all projects for found project options and grab them from the DB
        project_id_set = set([project_option.project_id for project_option in project_options])
        projects = Project.objects.filter(id__in=project_id_set, status=ObjectStatus.VISIBLE)

        # create a key/value map of our projects
        project_map = {project.id: project for project in projects}

        # iterate through the desired plugins and serialize them
        serialized_plugins = []
        for plugin in desired_plugins:
            serialized_plugin = serialize(plugin, request.user, PluginSerializer())

            serialized_plugin["projectList"] = []

            info_by_project = info_by_plugin_project.get(plugin.slug, {})

            # iterate through the projects
            for project_id, plugin_info in six.iteritems(info_by_project):
                # if the project is being deleted
                if project_id not in project_map:
                    continue
                project = project_map[project_id]

                # only include plugins which are configured
                if not plugin_info["configured"]:
                    continue

                serialized_plugin["projectList"].append(
                    {
                        "projectId": project.id,
                        "projectSlug": project.slug,
                        "projectName": project.name,  # TODO(steve): do we need?
                        "enabled": plugin_info["enabled"],
                        "configured": plugin_info["configured"],  # TODO(steve): do we need?
                        "projectPlatform": project.platform,
                    }
                )
            # sort by the projectSlug
            serialized_plugin["projectList"].sort(key=lambda x: x["projectSlug"])
            serialized_plugins.append(serialized_plugin)

        return Response(serialized_plugins)
Ejemplo n.º 27
0
    def test_does_not_disable_any_plugin(self):
        plugin = plugins.get("webhooks")
        plugin.enable(self.project)

        self.migrator.call()
        assert plugin in plugins.for_project(self.project)
Ejemplo n.º 28
0
def split_key(key):
    from sentry.plugins.base import plugins

    plugin_slug, _, project_id = key.split(":", 2)
    return plugins.get(plugin_slug), Project.objects.get(pk=project_id)
Ejemplo n.º 29
0
 def _get_plugin(self, plugin_id):
     try:
         return plugins.get(plugin_id)
     except KeyError:
         raise ResourceDoesNotExist
Ejemplo n.º 30
0
 def test_enabled_not_configured(self):
     plugins.get("webhooks").enable(self.projectA)
     response = self.client.get(self.url)
     assert filter(lambda x: x["slug"] == "webhooks",
                   response.data)[0]["projectList"] == []