Exemplo n.º 1
0
def build_action_response(registered_type, integration=None, organization=None, sentry_app=None):
    """
    Build the "available action" objects for the API. Each one can have different fields.

    :param registered_type: One of the registered AlertRuleTriggerAction types.
    :param integration: Optional. The Integration if this action uses a one.
    :param organization: Optional. If this is a PagerDuty action, we need the organization to look up services.
    :param sentry_app: Optional. The SentryApp if this action uses a one.
    :return: The available action object.
    """

    action_response = {
        "type": registered_type.slug,
        "allowedTargetTypes": [
            action_target_type_to_string.get(target_type)
            for target_type in registered_type.supported_target_types
        ],
    }

    if integration:
        action_response["integrationName"] = integration.name
        action_response["integrationId"] = integration.id

        if registered_type.type == AlertRuleTriggerAction.Type.PAGERDUTY:
            action_response["options"] = [
                {"value": service["id"], "label": service["service_name"]}
                for service in get_pagerduty_services(organization, integration.id)
            ]

    elif sentry_app:
        action_response["sentryAppName"] = sentry_app.name
        action_response["sentryAppId"] = sentry_app.id
        action_response["status"] = SentryAppStatus.as_str(sentry_app.status)

    return action_response
Exemplo n.º 2
0
    def test_creates_internal_integration(self):
        self.create_project(organization=self.org)
        self.login_as(self.user)

        response = self._post(isInternal=True)

        assert response.data['slug'] == 'myapp'
        assert response.data['status'] == SentryAppStatus.as_str(
            SentryAppStatus.INTERNAL)
Exemplo n.º 3
0
    def test_creates_internal_integration(self):
        self.create_project(organization=self.org)
        self.login_as(self.user)

        response = self._post(isInternal=True)

        assert re.match(r'myapp\-[0-9a-zA-Z]+', response.data['slug'])
        assert response.data['status'] == SentryAppStatus.as_str(
            SentryAppStatus.INTERNAL)
        assert not response.data['verifyInstall']
Exemplo n.º 4
0
def build_action_response(registered_type,
                          integration=None,
                          organization=None,
                          sentry_app_installation=None):
    """
    Build the "available action" objects for the API. Each one can have different fields.

    :param registered_type: One of the registered AlertRuleTriggerAction types.
    :param integration: Optional. The Integration if this action uses a one.
    :param organization: Optional. If this is a PagerDuty action, we need the organization to look up services.
    :param sentry_app: Optional. The SentryApp if this action uses a one.
    :return: The available action object.
    """

    action_response = {
        "type":
        registered_type.slug,
        "allowedTargetTypes": [
            ACTION_TARGET_TYPE_TO_STRING.get(target_type)
            for target_type in registered_type.supported_target_types
        ],
    }

    if integration:
        action_response["integrationName"] = integration.name
        action_response["integrationId"] = integration.id

        if registered_type.type == AlertRuleTriggerAction.Type.PAGERDUTY:
            action_response["options"] = [{
                "value": service["id"],
                "label": service["service_name"]
            } for service in get_pagerduty_services(organization,
                                                    integration.id)]

    elif sentry_app_installation:
        action_response[
            "sentryAppName"] = sentry_app_installation.sentry_app.name
        action_response["sentryAppId"] = sentry_app_installation.sentry_app_id
        action_response[
            "sentryAppInstallationUuid"] = sentry_app_installation.uuid
        action_response["status"] = SentryAppStatus.as_str(
            sentry_app_installation.sentry_app.status)

        # Sentry Apps can be alertable but not have an Alert Rule UI Component
        component = sentry_app_installation.prepare_sentry_app_components(
            "alert-rule-action")
        if component:
            action_response["settings"] = component.schema.get("settings", {})

    return action_response
Exemplo n.º 5
0
class SentryApp(ParanoidModel, HasApiScopes):
    __core__ = True

    application = models.OneToOneField('sentry.ApiApplication',
                                       related_name='sentry_app')

    # Much of the OAuth system in place currently depends on a User existing.
    # This "proxy user" represents the SentryApp in those cases.
    proxy_user = models.OneToOneField('sentry.User',
                                      related_name='sentry_app')

    # The owner is an actual Sentry User who created the SentryApp. Used to
    # determine who can manage the SentryApp itself.
    owner = FlexibleForeignKey('sentry.User',
                               related_name='owned_sentry_apps')

    name = models.TextField()
    slug = models.CharField(max_length=64, unique=True)
    status = BoundedPositiveIntegerField(
        default=SentryAppStatus.UNPUBLISHED,
        choices=SentryAppStatus.as_choices(),
        db_index=True,
    )
    uuid = models.CharField(max_length=64,
                            default=default_uuid)

    webhook_url = models.TextField()

    date_added = models.DateTimeField(default=timezone.now)
    date_updated = models.DateTimeField(default=timezone.now)

    class Meta:
        app_label = 'sentry'
        db_table = 'sentry_sentryapp'

    def save(self, *args, **kwargs):
        self._set_slug()
        return super(SentryApp, self).save(*args, **kwargs)

    def _set_slug(self):
        """
        Matches ``name``, but in lowercase, dash form.

        >>> self._set_slug('My Cool App')
        >>> self.slug
        my-cool-app
        """
        if not self.slug:
            self.slug = slugify(self.name)
Exemplo n.º 6
0
    def test_creates_internal_integration(self):
        self.create_project(organization=self.org)
        self.login_as(self.user)

        response = self._post(isInternal=True)

        assert re.match(r'myapp\-[0-9a-zA-Z]+', response.data['slug'])
        assert response.data['status'] == SentryAppStatus.as_str(SentryAppStatus.INTERNAL)
        assert not response.data['verifyInstall']

        # verify tokens are created properly
        sentry_app = SentryApp.objects.get(slug=response.data['slug'])
        sentry_app_installation = SentryAppInstallation.objects.get(sentry_app=sentry_app)

        sentry_app_installation_token = SentryAppInstallationToken.objects.get(
            sentry_app_installation=sentry_app_installation)

        # Below line will fail once we stop assigning api_token on the sentry_app_installation
        assert sentry_app_installation_token.api_token == sentry_app_installation.api_token
Exemplo n.º 7
0
    def test_creates_internal_integration(self):
        self.create_project(organization=self.organization)

        response = self.get_success_response(**self.get_data(isInternal=True))
        assert re.match(r"myapp\-[0-9a-zA-Z]+", response.data["slug"])
        assert response.data["status"] == SentryAppStatus.as_str(
            SentryAppStatus.INTERNAL)
        assert not response.data["verifyInstall"]

        # Verify tokens are created properly.
        sentry_app = SentryApp.objects.get(slug=response.data["slug"])
        sentry_app_installation = SentryAppInstallation.objects.get(
            sentry_app=sentry_app)

        sentry_app_installation_token = SentryAppInstallationToken.objects.get(
            sentry_app_installation=sentry_app_installation)

        # Below line will fail once we stop assigning api_token on the sentry_app_installation.
        assert sentry_app_installation_token.api_token == sentry_app_installation.api_token
Exemplo n.º 8
0
class SentryApp(ParanoidModel, HasApiScopes):
    __core__ = True

    application = models.OneToOneField("sentry.ApiApplication",
                                       null=True,
                                       on_delete=models.SET_NULL,
                                       related_name="sentry_app")

    # Much of the OAuth system in place currently depends on a User existing.
    # This "proxy user" represents the SentryApp in those cases.
    proxy_user = models.OneToOneField("sentry.User",
                                      null=True,
                                      on_delete=models.SET_NULL,
                                      related_name="sentry_app")

    # The Organization the Sentry App was created in "owns" it. Members of that
    # Org have differing access, dependent on their role within the Org.
    owner = FlexibleForeignKey("sentry.Organization",
                               related_name="owned_sentry_apps")

    name = models.TextField()
    slug = models.CharField(max_length=SENTRY_APP_SLUG_MAX_LENGTH, unique=True)
    author = models.TextField(null=True)
    status = BoundedPositiveIntegerField(default=SentryAppStatus.UNPUBLISHED,
                                         choices=SentryAppStatus.as_choices(),
                                         db_index=True)
    uuid = models.CharField(max_length=64, default=default_uuid)

    redirect_url = models.URLField(null=True)
    webhook_url = models.URLField(null=True)
    # does the application subscribe to `event.alert`,
    # meaning can it be used in alert rules as a {service} ?
    is_alertable = models.BooleanField(default=False)

    # does the application need to wait for verification
    # on behalf of the external service to know if its installations
    # are successfully installed ?
    verify_install = models.BooleanField(default=True)

    events = ArrayField(of=models.TextField, null=True)

    overview = models.TextField(null=True)
    schema = EncryptedJsonField(default=dict)

    date_added = models.DateTimeField(default=timezone.now)
    date_updated = models.DateTimeField(default=timezone.now)
    date_published = models.DateTimeField(null=True, blank=True)

    class Meta:
        app_label = "sentry"
        db_table = "sentry_sentryapp"

    @classmethod
    def visible_for_user(cls, request):
        from sentry.auth.superuser import is_active_superuser

        if is_active_superuser(request):
            return cls.objects.all()

        return cls.objects.filter(status=SentryAppStatus.PUBLISHED)

    @property
    def is_published(self):
        return self.status == SentryAppStatus.PUBLISHED

    @property
    def is_unpublished(self):
        return self.status == SentryAppStatus.UNPUBLISHED

    @property
    def is_internal(self):
        return self.status == SentryAppStatus.INTERNAL

    @property
    def slug_for_metrics(self):
        if self.is_internal:
            return "internal"
        if self.is_unpublished:
            return "unpublished"
        return self.slug

    def save(self, *args, **kwargs):
        self.date_updated = timezone.now()
        return super(SentryApp, self).save(*args, **kwargs)

    def is_installed_on(self, organization):
        return SentryAppInstallation.objects.filter(
            organization=organization).exists()

    def build_signature(self, body):
        secret = self.application.client_secret
        return hmac.new(key=secret.encode("utf-8"),
                        msg=body.encode("utf-8"),
                        digestmod=sha256).hexdigest()

    def show_auth_info(self, access):
        encoded_scopes = set({u"%s" % scope for scope in list(access.scopes)})
        return set(self.scope_list).issubset(encoded_scopes)
Exemplo n.º 9
0
class SentryApp(ParanoidModel, HasApiScopes):
    __core__ = True

    application = models.OneToOneField("sentry.ApiApplication",
                                       null=True,
                                       on_delete=models.SET_NULL,
                                       related_name="sentry_app")

    # Much of the OAuth system in place currently depends on a User existing.
    # This "proxy user" represents the SentryApp in those cases.
    proxy_user = models.OneToOneField("sentry.User",
                                      null=True,
                                      on_delete=models.SET_NULL,
                                      related_name="sentry_app")

    # The Organization the Sentry App was created in "owns" it. Members of that
    # Org have differing access, dependent on their role within the Org.
    owner = FlexibleForeignKey("sentry.Organization",
                               related_name="owned_sentry_apps")

    name = models.TextField()
    slug = models.CharField(max_length=SENTRY_APP_SLUG_MAX_LENGTH, unique=True)
    author = models.TextField(null=True)
    status = BoundedPositiveIntegerField(default=SentryAppStatus.UNPUBLISHED,
                                         choices=SentryAppStatus.as_choices(),
                                         db_index=True)
    uuid = models.CharField(max_length=64, default=default_uuid)

    redirect_url = models.URLField(null=True)
    webhook_url = models.URLField()
    # does the application subscribe to `event.alert`,
    # meaning can it be used in alert rules as a {service} ?
    is_alertable = models.BooleanField(default=False)

    # does the application need to wait for verification
    # on behalf of the external service to know if its installations
    # are successully installed ?
    verify_install = models.BooleanField(default=True)

    events = ArrayField(of=models.TextField, null=True)

    overview = models.TextField(null=True)
    schema = EncryptedJsonField(default=dict)

    date_added = models.DateTimeField(default=timezone.now)
    date_updated = models.DateTimeField(default=timezone.now)

    class Meta:
        app_label = "sentry"
        db_table = "sentry_sentryapp"

    @classmethod
    def visible_for_user(cls, request):
        from sentry.auth.superuser import is_active_superuser

        if is_active_superuser(request):
            return cls.objects.all()

        return cls.objects.filter(status=SentryAppStatus.PUBLISHED)

    @property
    def organizations(self):
        if not self.pk:
            return Organization.objects.none()

        return Organization.objects.select_related(
            "sentry_app_installations").filter(
                sentry_app_installations__sentry_app_id=self.id)

    @property
    def teams(self):
        from sentry.models import Team

        if not self.pk:
            return Team.objects.none()

        return Team.objects.filter(organization__in=self.organizations)

    @property
    def is_published(self):
        return self.status == SentryAppStatus.PUBLISHED

    @property
    def is_unpublished(self):
        return self.status == SentryAppStatus.UNPUBLISHED

    @property
    def is_internal(self):
        return self.status == SentryAppStatus.INTERNAL

    def save(self, *args, **kwargs):
        self._set_slug()
        self.date_updated = timezone.now()
        return super(SentryApp, self).save(*args, **kwargs)

    def is_installed_on(self, organization):
        return self.organizations.filter(pk=organization.pk).exists()

    def _set_slug(self):
        """
        Matches ``name``, but in lowercase, dash form.

        >>> self._set_slug('My Cool App')
        >>> self.slug
        my-cool-app
        """
        if not self.slug:
            self.slug = slugify(self.name)

        if self.is_internal and not self._has_internal_slug():
            self.slug = u"{}-{}".format(
                self.slug,
                hashlib.sha1(self.owner.slug).hexdigest()[0:6])

    def _has_internal_slug(self):
        return re.match(r"\w+-[0-9a-zA-Z]+", self.slug)

    def build_signature(self, body):
        secret = self.application.client_secret
        return hmac.new(key=secret.encode("utf-8"),
                        msg=body.encode("utf-8"),
                        digestmod=sha256).hexdigest()
Exemplo n.º 10
0
class SentryApp(ParanoidModel, HasApiScopes):
    __core__ = True

    application = models.OneToOneField(
        'sentry.ApiApplication',
        null=True,
        on_delete=models.SET_NULL,
        related_name='sentry_app',
    )

    # Much of the OAuth system in place currently depends on a User existing.
    # This "proxy user" represents the SentryApp in those cases.
    proxy_user = models.OneToOneField(
        'sentry.User',
        null=True,
        on_delete=models.SET_NULL,
        related_name='sentry_app'
    )

    # The Organization the Sentry App was created in "owns" it. Members of that
    # Org have differing access, dependent on their role within the Org.
    owner = FlexibleForeignKey('sentry.Organization',
                               related_name='owned_sentry_apps')

    name = models.TextField()
    slug = models.CharField(max_length=64, unique=True)
    status = BoundedPositiveIntegerField(
        default=SentryAppStatus.UNPUBLISHED,
        choices=SentryAppStatus.as_choices(),
        db_index=True,
    )
    uuid = models.CharField(max_length=64,
                            default=default_uuid)

    webhook_url = models.TextField()

    date_added = models.DateTimeField(default=timezone.now)
    date_updated = models.DateTimeField(default=timezone.now)

    class Meta:
        app_label = 'sentry'
        db_table = 'sentry_sentryapp'

    @property
    def organizations(self):
        if not self.pk:
            return Organization.objects.none()

        return Organization \
            .objects \
            .select_related('sentry_app_installations') \
            .filter(sentry_app_installations__sentry_app_id=self.id)

    @property
    def teams(self):
        from sentry.models import Team

        if not self.pk:
            return Team.objects.none()

        return Team.objects.filter(organization__in=self.organizations)

    def save(self, *args, **kwargs):
        self._set_slug()
        return super(SentryApp, self).save(*args, **kwargs)

    def is_installed_on(self, organization):
        return self.organizations.filter(pk=organization.pk).exists()

    def _set_slug(self):
        """
        Matches ``name``, but in lowercase, dash form.

        >>> self._set_slug('My Cool App')
        >>> self.slug
        my-cool-app
        """
        if not self.slug:
            self.slug = slugify(self.name)
Exemplo n.º 11
0
class SentryApp(ParanoidModel, HasApiScopes):
    __core__ = True

    application = models.OneToOneField(
        'sentry.ApiApplication',
        null=True,
        on_delete=models.SET_NULL,
        related_name='sentry_app',
    )

    # Much of the OAuth system in place currently depends on a User existing.
    # This "proxy user" represents the SentryApp in those cases.
    proxy_user = models.OneToOneField('sentry.User',
                                      null=True,
                                      on_delete=models.SET_NULL,
                                      related_name='sentry_app')

    # The Organization the Sentry App was created in "owns" it. Members of that
    # Org have differing access, dependent on their role within the Org.
    owner = FlexibleForeignKey('sentry.Organization',
                               related_name='owned_sentry_apps')

    name = models.TextField()
    slug = models.CharField(max_length=SENTRY_APP_SLUG_MAX_LENGTH, unique=True)
    status = BoundedPositiveIntegerField(
        default=SentryAppStatus.UNPUBLISHED,
        choices=SentryAppStatus.as_choices(),
        db_index=True,
    )
    uuid = models.CharField(max_length=64, default=default_uuid)

    redirect_url = models.URLField(null=True)
    webhook_url = models.URLField()
    # does the application subscribe to `event.alert`,
    # meaning can it be used in alert rules as a {service} ?
    is_alertable = models.BooleanField(default=False)

    events = ArrayField(of=models.TextField, null=True)

    overview = models.TextField(null=True)

    date_added = models.DateTimeField(default=timezone.now)
    date_updated = models.DateTimeField(default=timezone.now)

    class Meta:
        app_label = 'sentry'
        db_table = 'sentry_sentryapp'

    @classmethod
    def visible_for_user(cls, user):
        if user.is_superuser:
            return cls.objects.all()

        return cls.objects.filter(
            Q(status=SentryAppStatus.PUBLISHED)
            | Q(owner__in=user.get_orgs()), )

    @property
    def organizations(self):
        if not self.pk:
            return Organization.objects.none()

        return Organization \
            .objects \
            .select_related('sentry_app_installations') \
            .filter(sentry_app_installations__sentry_app_id=self.id)

    @property
    def teams(self):
        from sentry.models import Team

        if not self.pk:
            return Team.objects.none()

        return Team.objects.filter(organization__in=self.organizations)

    @property
    def is_published(self):
        return self.status == SentryAppStatus.PUBLISHED

    def save(self, *args, **kwargs):
        self._set_slug()
        return super(SentryApp, self).save(*args, **kwargs)

    def is_installed_on(self, organization):
        return self.organizations.filter(pk=organization.pk).exists()

    def _set_slug(self):
        """
        Matches ``name``, but in lowercase, dash form.

        >>> self._set_slug('My Cool App')
        >>> self.slug
        my-cool-app
        """
        if not self.slug:
            self.slug = slugify(self.name)

    def build_signature(self, body):
        secret = self.application.client_secret
        return hmac.new(
            key=secret.encode('utf-8'),
            msg=body.encode('utf-8'),
            digestmod=sha256,
        ).hexdigest()