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)
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)
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)
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()
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()