Ejemplo n.º 1
0
class CommitSerializer(serializers.Serializer):
    id = serializers.CharField(max_length=64)
    repository = serializers.CharField(
        max_length=64,
        required=False,
        allow_null=True,
        allow_blank=True,
    )
    message = serializers.CharField(required=False,
                                    allow_null=True,
                                    allow_blank=True)
    author_name = serializers.CharField(
        max_length=128,
        required=False,
        allow_null=True,
        allow_blank=True,
    )
    author_email = serializers.EmailField(
        max_length=75,
        required=False,
        allow_null=True,
        allow_blank=True,
    )
    timestamp = serializers.DateTimeField(required=False, allow_null=True)
    patch_set = ListField(
        child=CommitPatchSetSerializer(required=False),
        required=False,
        allow_null=True,
    )
Ejemplo n.º 2
0
class ServiceHookValidator(serializers.Serializer):
    url = serializers.URLField(required=True)
    events = ListField(child=serializers.CharField(max_length=255),
                       required=False)
    version = serializers.ChoiceField(choices=((0, "0"), ),
                                      required=False,
                                      default=0)
    isActive = serializers.BooleanField(required=False, default=True)

    def validate_events(self, value):
        if value:
            for event in value:
                if event not in SERVICE_HOOK_EVENTS:
                    raise serializers.ValidationError(
                        f"Invalid event name: {event}")
        return value
Ejemplo n.º 3
0
class ServiceHookValidator(serializers.Serializer):
    url = serializers.URLField(required=True)
    events = ListField(
        child=serializers.CharField(max_length=255),
        required=True,
    )
    version = serializers.ChoiceField(choices=((0, '0'), ),
                                      required=False,
                                      default=0)

    def validate_events(self, attrs, source):
        value = attrs[source]
        if value:
            for event in value:
                if event not in SERVICE_HOOK_EVENTS:
                    raise serializers.ValidationError(
                        'Invalid event name: {}'.format(event))
        return attrs
Ejemplo n.º 4
0
class CommentSerializer(serializers.Serializer, MentionsMixin):
    comment = serializers.CharField(required=True)
    mentions = ListField(child=ActorField(), required=False)
    external_id = serializers.CharField(allow_null=True, required=False)
Ejemplo n.º 5
0
class ProjectAdminSerializer(ProjectMemberSerializer):
    name = serializers.CharField(max_length=200)
    slug = serializers.RegexField(r"^[a-z0-9_\-]+$", max_length=50)
    team = serializers.RegexField(r"^[a-z0-9_\-]+$", max_length=50)
    digestsMinDelay = serializers.IntegerField(min_value=60, max_value=3600)
    digestsMaxDelay = serializers.IntegerField(min_value=60, max_value=3600)
    subjectPrefix = serializers.CharField(max_length=200, allow_blank=True)
    subjectTemplate = serializers.CharField(max_length=200)
    securityToken = serializers.RegexField(
        r"^[-a-zA-Z0-9+/=\s]+$", max_length=255, allow_blank=True
    )
    securityTokenHeader = serializers.RegexField(
        r"^[a-zA-Z0-9_\-]+$", max_length=20, allow_blank=True
    )
    verifySSL = serializers.BooleanField(required=False)

    defaultEnvironment = serializers.CharField(required=False, allow_null=True, allow_blank=True)
    dataScrubber = serializers.BooleanField(required=False)
    dataScrubberDefaults = serializers.BooleanField(required=False)
    sensitiveFields = ListField(child=serializers.CharField(), required=False)
    safeFields = ListField(child=serializers.CharField(), required=False)
    storeCrashReports = serializers.IntegerField(
        min_value=-1, max_value=STORE_CRASH_REPORTS_MAX, required=False, allow_null=True
    )
    relayPiiConfig = serializers.CharField(required=False, allow_blank=True, allow_null=True)
    builtinSymbolSources = ListField(child=serializers.CharField(), required=False)
    symbolSources = serializers.CharField(required=False, allow_blank=True, allow_null=True)
    scrubIPAddresses = serializers.BooleanField(required=False)
    groupingConfig = serializers.CharField(required=False, allow_blank=True, allow_null=True)
    groupingEnhancements = serializers.CharField(required=False, allow_blank=True, allow_null=True)
    fingerprintingRules = serializers.CharField(required=False, allow_blank=True, allow_null=True)
    secondaryGroupingConfig = serializers.CharField(
        required=False, allow_blank=True, allow_null=True
    )
    secondaryGroupingExpiry = serializers.IntegerField(min_value=1, required=False, allow_null=True)
    scrapeJavaScript = serializers.BooleanField(required=False)
    allowedDomains = EmptyListField(child=OriginField(allow_blank=True), required=False)
    resolveAge = EmptyIntegerField(required=False, allow_null=True)
    platform = serializers.CharField(required=False, allow_null=True, allow_blank=True)
    copy_from_project = serializers.IntegerField(required=False)
    dynamicSampling = DynamicSamplingSerializer(required=False)

    def validate(self, data):
        max_delay = (
            data["digestsMaxDelay"]
            if "digestsMaxDelay" in data
            else self.context["project"].get_option("digests:mail:maximum_delay")
        )
        min_delay = (
            data["digestsMinDelay"]
            if "digestsMinDelay" in data
            else self.context["project"].get_option("digests:mail:minimum_delay")
        )

        if min_delay is not None and max_delay and max_delay is not None and min_delay > max_delay:
            raise serializers.ValidationError(
                {"digestsMinDelay": "The minimum delay on digests must be lower than the maximum."}
            )

        return data

    def validate_allowedDomains(self, value):
        value = filter(bool, value)
        if len(value) == 0:
            raise serializers.ValidationError(
                "Empty value will block all requests, use * to accept from all domains"
            )
        return value

    def validate_slug(self, slug):
        if slug in RESERVED_PROJECT_SLUGS:
            raise serializers.ValidationError(f'The slug "{slug}" is reserved and not allowed.')
        project = self.context["project"]
        other = (
            Project.objects.filter(slug=slug, organization=project.organization)
            .exclude(id=project.id)
            .first()
        )
        if other is not None:
            raise serializers.ValidationError(
                "Another project (%s) is already using that slug" % other.name
            )
        return slug

    def validate_relayPiiConfig(self, value):
        organization = self.context["project"].organization
        return validate_pii_config_update(organization, value)

    def validate_builtinSymbolSources(self, value):
        if not value:
            return value

        from sentry import features

        organization = self.context["project"].organization
        request = self.context["request"]
        has_sources = features.has("organizations:symbol-sources", organization, actor=request.user)

        if not has_sources:
            raise serializers.ValidationError("Organization is not allowed to set symbol sources")

        return value

    def validate_symbolSources(self, sources_json):
        if not sources_json:
            return sources_json

        from sentry import features

        organization = self.context["project"].organization
        request = self.context["request"]

        try:
            # We should really only grab and parse if there are sources in sources_json whose
            # secrets are set to {"hidden-secret":true}
            orig_sources = parse_sources(
                self.context["project"].get_option("sentry:symbol_sources")
            )
            sources = parse_backfill_sources(sources_json.strip(), orig_sources)
        except InvalidSourcesError as e:
            raise serializers.ValidationError(str(e))

        sources_json = json.dumps(sources) if sources else ""

        # If no sources are added or modified, we're either only deleting sources or doing nothing.
        # This is always allowed.
        added_or_modified_sources = [s for s in sources if s not in orig_sources]
        if not added_or_modified_sources:
            return sources_json

        # Adding sources is only allowed if custom symbol sources are enabled.
        has_sources = features.has(
            "organizations:custom-symbol-sources", organization, actor=request.user
        )

        if not has_sources:
            raise serializers.ValidationError(
                "Organization is not allowed to set custom symbol sources"
            )

        has_multiple_appconnect = features.has(
            "organizations:app-store-connect-multiple", organization, actor=request.user
        )
        appconnect_sources = [s for s in sources if s.get("type") == "appStoreConnect"]
        if not has_multiple_appconnect and len(appconnect_sources) > 1:
            raise serializers.ValidationError(
                "Only one Apple App Store Connect application is allowed in this project"
            )

        return sources_json

    def validate_groupingEnhancements(self, value):
        if not value:
            return value

        try:
            Enhancements.from_config_string(value)
        except InvalidEnhancerConfig as e:
            raise serializers.ValidationError(str(e))

        return value

    def validate_secondaryGroupingExpiry(self, value):
        if not isinstance(value, (int, float)) or math.isnan(value):
            raise serializers.ValidationError(
                f"Grouping expiry must be a numerical value, a UNIX timestamp with second resolution, found {type(value)}"
            )
        now = time.time()
        if value < now:
            raise serializers.ValidationError(
                "Grouping expiry must be sometime within the next 90 days and not in the past. Perhaps you specified the timestamp not in seconds?"
            )

        max_expiry_date = now + (91 * 24 * 3600)
        if value > max_expiry_date:
            value = max_expiry_date

        return value

    def validate_fingerprintingRules(self, value):
        if not value:
            return value

        try:
            FingerprintingRules.from_config_string(value)
        except InvalidFingerprintingConfig as e:
            raise serializers.ValidationError(str(e))

        return value

    def validate_copy_from_project(self, other_project_id):
        try:
            other_project = Project.objects.filter(
                id=other_project_id, organization_id=self.context["project"].organization_id
            ).prefetch_related("teams")[0]
        except IndexError:
            raise serializers.ValidationError("Project to copy settings from not found.")

        request = self.context["request"]
        if not request.access.has_project_access(other_project):
            raise serializers.ValidationError(
                "Project settings cannot be copied from a project you do not have access to."
            )

        for project_team in other_project.projectteam_set.all():
            if not request.access.has_team_scope(project_team.team, "team:write"):
                raise serializers.ValidationError(
                    "Project settings cannot be copied from a project with a team you do not have write access to."
                )

        return other_project_id

    def validate_platform(self, value):
        if Project.is_valid_platform(value):
            return value
        raise serializers.ValidationError("Invalid platform")
Ejemplo n.º 6
0
class ProjectAdminSerializer(ProjectMemberSerializer):
    name = serializers.CharField(max_length=200)
    slug = serializers.RegexField(r"^[a-z0-9_\-]+$", max_length=50)
    team = serializers.RegexField(r"^[a-z0-9_\-]+$", max_length=50)
    digestsMinDelay = serializers.IntegerField(min_value=60, max_value=3600)
    digestsMaxDelay = serializers.IntegerField(min_value=60, max_value=3600)
    subjectPrefix = serializers.CharField(max_length=200, allow_blank=True)
    subjectTemplate = serializers.CharField(max_length=200)
    securityToken = serializers.RegexField(r"^[-a-zA-Z0-9+/=\s]+$",
                                           max_length=255,
                                           allow_blank=True)
    securityTokenHeader = serializers.RegexField(r"^[a-zA-Z0-9_\-]+$",
                                                 max_length=20,
                                                 allow_blank=True)
    verifySSL = serializers.BooleanField(required=False)

    defaultEnvironment = serializers.CharField(required=False,
                                               allow_null=True,
                                               allow_blank=True)
    dataScrubber = serializers.BooleanField(required=False)
    dataScrubberDefaults = serializers.BooleanField(required=False)
    sensitiveFields = ListField(child=serializers.CharField(), required=False)
    safeFields = ListField(child=serializers.CharField(), required=False)
    storeCrashReports = serializers.IntegerField(min_value=-1,
                                                 max_value=20,
                                                 required=False,
                                                 allow_null=True)
    relayPiiConfig = serializers.CharField(required=False,
                                           allow_blank=True,
                                           allow_null=True)
    builtinSymbolSources = ListField(child=serializers.CharField(),
                                     required=False)
    symbolSources = serializers.CharField(required=False,
                                          allow_blank=True,
                                          allow_null=True)
    scrubIPAddresses = serializers.BooleanField(required=False)
    groupingConfig = serializers.CharField(required=False,
                                           allow_blank=True,
                                           allow_null=True)
    groupingEnhancements = serializers.CharField(required=False,
                                                 allow_blank=True,
                                                 allow_null=True)
    groupingEnhancementsBase = serializers.CharField(required=False,
                                                     allow_blank=True,
                                                     allow_null=True)
    fingerprintingRules = serializers.CharField(required=False,
                                                allow_blank=True,
                                                allow_null=True)
    scrapeJavaScript = serializers.BooleanField(required=False)
    allowedDomains = EmptyListField(child=OriginField(allow_blank=True),
                                    required=False)
    resolveAge = EmptyIntegerField(required=False, allow_null=True)
    platform = serializers.CharField(required=False,
                                     allow_null=True,
                                     allow_blank=True)
    copy_from_project = serializers.IntegerField(required=False)
    dynamicSampling = DynamicSamplingSerializer(required=False)

    def validate(self, data):
        max_delay = (
            data["digestsMaxDelay"] if "digestsMaxDelay" in data else
            self.context["project"].get_option("digests:mail:maximum_delay"))
        min_delay = (
            data["digestsMinDelay"] if "digestsMinDelay" in data else
            self.context["project"].get_option("digests:mail:minimum_delay"))

        if min_delay is not None and max_delay and max_delay is not None and min_delay > max_delay:
            raise serializers.ValidationError({
                "digestsMinDelay":
                "The minimum delay on digests must be lower than the maximum."
            })

        return data

    def validate_allowedDomains(self, value):
        value = filter(bool, value)
        if len(value) == 0:
            raise serializers.ValidationError(
                "Empty value will block all requests, use * to accept from all domains"
            )
        return value

    def validate_slug(self, slug):
        if slug in RESERVED_PROJECT_SLUGS:
            raise serializers.ValidationError(
                f'The slug "{slug}" is reserved and not allowed.')
        project = self.context["project"]
        other = (Project.objects.filter(
            slug=slug,
            organization=project.organization).exclude(id=project.id).first())
        if other is not None:
            raise serializers.ValidationError(
                "Another project (%s) is already using that slug" % other.name)
        return slug

    def validate_relayPiiConfig(self, value):
        organization = self.context["project"].organization
        return validate_pii_config_update(organization, value)

    def validate_builtinSymbolSources(self, value):
        if not value:
            return value

        from sentry import features

        organization = self.context["project"].organization
        request = self.context["request"]
        has_sources = features.has("organizations:symbol-sources",
                                   organization,
                                   actor=request.user)

        if not has_sources:
            raise serializers.ValidationError(
                "Organization is not allowed to set symbol sources")

        return value

    def validate_symbolSources(self, sources_json):
        if not sources_json:
            return sources_json

        from sentry import features

        organization = self.context["project"].organization
        request = self.context["request"]
        has_sources = features.has("organizations:custom-symbol-sources",
                                   organization,
                                   actor=request.user)

        if not has_sources:
            raise serializers.ValidationError(
                "Organization is not allowed to set custom symbol sources")

        try:
            sources = parse_sources(sources_json.strip())
            sources_json = json.dumps(sources) if sources else ""
        except InvalidSourcesError as e:
            raise serializers.ValidationError(str(e))

        return sources_json

    def validate_groupingEnhancements(self, value):
        if not value:
            return value

        try:
            Enhancements.from_config_string(value)
        except InvalidEnhancerConfig as e:
            raise serializers.ValidationError(str(e))

        return value

    def validate_fingerprintingRules(self, value):
        if not value:
            return value

        try:
            FingerprintingRules.from_config_string(value)
        except InvalidFingerprintingConfig as e:
            raise serializers.ValidationError(str(e))

        return value

    def validate_copy_from_project(self, other_project_id):
        try:
            other_project = Project.objects.filter(
                id=other_project_id,
                organization_id=self.context["project"].organization_id
            ).prefetch_related("teams")[0]
        except IndexError:
            raise serializers.ValidationError(
                "Project to copy settings from not found.")

        request = self.context["request"]
        if not request.access.has_project_access(other_project):
            raise serializers.ValidationError(
                "Project settings cannot be copied from a project you do not have access to."
            )

        for project_team in other_project.projectteam_set.all():
            if not request.access.has_team_scope(project_team.team,
                                                 "team:write"):
                raise serializers.ValidationError(
                    "Project settings cannot be copied from a project with a team you do not have write access to."
                )

        return other_project_id

    def validate_platform(self, value):
        if Project.is_valid_platform(value):
            return value
        raise serializers.ValidationError("Invalid platform")
Ejemplo n.º 7
0
class RuleSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=64)
    environment = serializers.CharField(max_length=64,
                                        required=False,
                                        allow_null=True)
    actionMatch = serializers.ChoiceField(choices=(("all", "all"),
                                                   ("any", "any"), ("none",
                                                                    "none")))
    filterMatch = serializers.ChoiceField(choices=(("all", "all"),
                                                   ("any", "any"), ("none",
                                                                    "none")),
                                          required=False)
    actions = ListField(child=RuleNodeField(type="action/event"),
                        required=False)
    conditions = ListField(child=RuleNodeField(type="condition/event"),
                           required=False)
    filters = ListField(child=RuleNodeField(type="filter/event"),
                        required=False)
    frequency = serializers.IntegerField(min_value=5, max_value=60 * 24 * 30)
    owner = ActorField(required=False, allow_null=True)

    def validate_environment(self, environment):
        if environment is None:
            return environment

        try:
            environment = Environment.get_for_organization_id(
                self.context["project"].organization_id, environment).id
        except Environment.DoesNotExist:
            raise serializers.ValidationError(
                "This environment has not been created.")

        return environment

    def validate(self, attrs):
        # XXX(meredith): For rules that have the Slack integration as an action
        # we need to check if the channel_id needs to be looked up via an async task.
        # If the "pending_save" attribute is set we want to bubble that up to the
        # project_rule(_details) endpoints by setting it on attrs
        actions = attrs.get("actions", tuple())
        for action in actions:
            # XXX(colleen): For ticket rules we need to ensure the user has
            # at least done minimal configuration
            if action["id"] in TICKET_ACTIONS:
                if not action.get("dynamic_form_fields"):
                    raise serializers.ValidationError(
                        {"actions": "Must configure issue link settings."})
            # remove this attribute because we don't want it to be saved in the rule
            if action.pop("pending_save", None):
                attrs["pending_save"] = True
                break

        # ensure that if filters are passed in that a filterMatch is also supplied
        filters = attrs.get("filters")
        if filters:
            filter_match = attrs.get("filterMatch")
            if not filter_match:
                raise serializers.ValidationError({
                    "filterMatch":
                    "Must select a filter match (all, any, none) if filters are supplied."
                })

        # ensure that if a user has alert-filters enabled, they do not use old conditions
        project = self.context["project"]
        conditions = attrs.get("conditions", tuple())
        project_has_filters = features.has("projects:alert-filters", project)
        if project_has_filters:
            old_conditions = [
                condition for condition in conditions
                if condition["id"] in MIGRATED_CONDITIONS
            ]
            if old_conditions:
                raise serializers.ValidationError({
                    "conditions":
                    "Conditions evaluating an event attribute, tag, or level are outdated please use an appropriate filter instead."
                })

        # ensure that if a user has alert-filters enabled, they do not use a 'none' match on conditions
        if project_has_filters and attrs.get("actionMatch") == "none":
            raise serializers.ValidationError({
                "conditions":
                "The 'none' match on conditions is outdated and no longer supported."
            })

        return attrs

    def save(self, rule):
        rule.project = self.context["project"]
        if "environment" in self.validated_data:
            environment = self.validated_data["environment"]
            rule.environment_id = int(
                environment) if environment else environment
        if self.validated_data.get("name"):
            rule.label = self.validated_data["name"]
        if self.validated_data.get("actionMatch"):
            rule.data["action_match"] = self.validated_data["actionMatch"]
        if self.validated_data.get("filterMatch"):
            rule.data["filter_match"] = self.validated_data["filterMatch"]
        if self.validated_data.get("actions") is not None:
            rule.data["actions"] = self.validated_data["actions"]
        if self.validated_data.get("conditions") is not None:
            rule.data["conditions"] = self.validated_data["conditions"]
        if self.validated_data.get("frequency"):
            rule.data["frequency"] = self.validated_data["frequency"]
        if self.validated_data.get("owner"):
            rule.owner = self.validated_data["owner"].resolve_to_actor()
        rule.save()
        return rule