コード例 #1
0
class AvatarSerializer(serializers.Serializer):
    avatar_photo = AvatarField(required=False)
    avatar_type = serializers.ChoiceField(
        choices=(("upload", "upload"), ("gravatar", "gravatar"), ("letter_avatar", "letter_avatar"))
    )

    def validate(self, attrs):
        attrs = super().validate(attrs)
        if attrs.get("avatar_type") == "upload":
            model_type = self.context["type"]
            has_existing_file = model_type.objects.filter(
                file__isnull=False, **self.context["kwargs"]
            ).exists()
            if not has_existing_file and not attrs.get("avatar_photo"):
                raise serializers.ValidationError(
                    {"avatar_type": "Cannot set avatar_type to upload without avatar_photo"}
                )
        return attrs
コード例 #2
0
class AvatarSerializer(serializers.Serializer):
    avatar_photo = AvatarField(required=False)
    avatar_type = serializers.ChoiceField(choices=(
        ('upload', 'upload'),
        ('gravatar', 'gravatar'),
        ('letter_avatar', 'letter_avatar'),
    ))

    def validate(self, attrs):
        attrs = super(AvatarSerializer, self).validate(attrs)
        if attrs.get('avatar_type') == 'upload':
            model_type = self.context['type']
            has_existing_file = model_type.objects.filter(
                file__isnull=False, **self.context['kwargs']).exists()
            if not has_existing_file and not attrs.get('avatar_photo'):
                raise serializers.ValidationError({
                    'avatar_type':
                    'Cannot set avatar_type to upload without avatar_photo',
                })
        return attrs
コード例 #3
0
class OrganizationSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=64)
    slug = serializers.RegexField(r'^[a-z0-9_\-]+$', max_length=50)
    accountRateLimit = serializers.IntegerField(
        min_value=0, max_value=1000000, required=False)
    projectRateLimit = serializers.IntegerField(
        min_value=50, max_value=100, required=False)
    avatar = AvatarField(required=False)
    avatarType = serializers.ChoiceField(
        choices=(('upload', 'upload'), ('letter_avatar', 'letter_avatar'), ), required=False
    )

    openMembership = serializers.BooleanField(required=False)
    allowSharedIssues = serializers.BooleanField(required=False)
    enhancedPrivacy = serializers.BooleanField(required=False)
    dataScrubber = serializers.BooleanField(required=False)
    dataScrubberDefaults = serializers.BooleanField(required=False)
    sensitiveFields = ListField(child=serializers.CharField(), required=False)
    safeFields = ListField(child=serializers.CharField(), required=False)
    scrubIPAddresses = serializers.BooleanField(required=False)
    isEarlyAdopter = serializers.BooleanField(required=False)
    require2FA = serializers.BooleanField(required=False)

    def validate_slug(self, attrs, source):
        value = attrs[source]
        # Historically, the only check just made sure there was more than 1
        # character for the slug, but since then, there are many slugs that
        # fit within this new imposed limit. We're not fixing existing, but
        # just preventing new bad values.
        if len(value) < 3:
            raise serializers.ValidationError(
                'This slug "%s" is too short. Minimum of 3 characters.' %
                (value, ))
        if value in RESERVED_ORGANIZATION_SLUGS:
            raise serializers.ValidationError(
                'This slug "%s" is reserved and not allowed.' %
                (value, ))
        qs = Organization.objects.filter(
            slug=value,
        ).exclude(id=self.context['organization'].id)
        if qs.exists():
            raise serializers.ValidationError('The slug "%s" is already in use.' % (value, ))
        return attrs

    def validate_sensitiveFields(self, attrs, source):
        value = attrs[source]
        if value and not all(value):
            raise serializers.ValidationError('Empty values are not allowed.')
        return attrs

    def validate_safeFields(self, attrs, source):
        value = attrs[source]
        if value and not all(value):
            raise serializers.ValidationError('Empty values are not allowed.')
        return attrs

    def validate_require2FA(self, attrs, source):
        value = attrs[source]
        user = self.context['user']
        has_2fa = Authenticator.objects.user_has_2fa(user)
        if value and not has_2fa:
            raise serializers.ValidationError(
                'Cannot require two-factor authentication without personal two-factor enabled.')
        return attrs

    def validate(self, attrs):
        attrs = super(OrganizationSerializer, self).validate(attrs)
        if attrs.get('avatarType') == 'upload':
            has_existing_file = OrganizationAvatar.objects.filter(
                organization=self.context['organization'],
                file__isnull=False,
            ).exists()
            if not has_existing_file and not attrs.get('avatar'):
                raise serializers.ValidationError(
                    {
                        'avatarType': 'Cannot set avatarType to upload without avatar',
                    }
                )
        return attrs

    def save(self):
        org = self.context['organization']
        changed_data = {}

        for key, option, type_ in ORG_OPTIONS:
            if key not in self.init_data:
                continue
            try:
                option_inst = OrganizationOption.objects.get(
                    organization=org, key=option)
            except OrganizationOption.DoesNotExist:
                OrganizationOption.objects.set_value(
                    organization=org,
                    key=option,
                    value=type_(self.init_data[key]),
                )
                # TODO(kelly): This will not work if new ORG_OPTIONS are added and their
                # default value evaluates as truthy, but this should work for now with the
                # current ORG_OPTIONS (assumes ORG_OPTIONS are falsy)
                if type_(self.init_data[key]):
                    changed_data[key] = self.init_data[key]
            else:
                option_inst.value = self.init_data[key]
                # check if ORG_OPTIONS changed
                if option_inst.has_changed('value'):
                    old_val = option_inst.old_value('value')
                    changed_data[key] = u'from {} to {}'.format(old_val, option_inst.value)
                option_inst.save()

        if 'openMembership' in self.init_data:
            org.flags.allow_joinleave = self.init_data['openMembership']
        if 'allowSharedIssues' in self.init_data:
            org.flags.disable_shared_issues = not self.init_data['allowSharedIssues']
        if 'enhancedPrivacy' in self.init_data:
            org.flags.enhanced_privacy = self.init_data['enhancedPrivacy']
        if 'isEarlyAdopter' in self.init_data:
            org.flags.early_adopter = self.init_data['isEarlyAdopter']
        if 'require2FA' in self.init_data:
            org.flags.require_2fa = self.init_data['require2FA']
        if 'name' in self.init_data:
            org.name = self.init_data['name']
        if 'slug' in self.init_data:
            org.slug = self.init_data['slug']

        org_tracked_field = {
            'name': org.name,
            'slug': org.slug,
            'default_role': org.default_role,
            'flag_field': {
                'allow_joinleave': org.flags.allow_joinleave.is_set,
                'enhanced_privacy': org.flags.enhanced_privacy.is_set,
                'disable_shared_issues': org.flags.disable_shared_issues.is_set,
                'early_adopter': org.flags.early_adopter.is_set,
                'require_2fa': org.flags.require_2fa.is_set,
            }
        }

        # check if fields changed
        for f, v in six.iteritems(org_tracked_field):
            if f is not 'flag_field':
                if org.has_changed(f):
                    old_val = org.old_value(f)
                    changed_data[f] = u'from {} to {}'.format(old_val, v)
            else:
                # check if flag fields changed
                for f, v in six.iteritems(org_tracked_field['flag_field']):
                    if org.flag_has_changed(f):
                        changed_data[f] = u'to {}'.format(v)

        org.save()

        if 'avatar' in self.init_data or 'avatarType' in self.init_data:
            OrganizationAvatar.save_avatar(
                relation={'organization': org},
                type=self.init_data.get('avatarType', 'upload'),
                avatar=self.init_data.get('avatar'),
                filename='{}.png'.format(org.slug),
            )
        if 'require2FA' in self.init_data and self.init_data['require2FA'] is True:
            org.send_setup_2fa_emails()
        return org, changed_data
コード例 #4
0
class OrganizationSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=64)
    slug = serializers.RegexField(r'^[a-z0-9_\-]+$', max_length=50)
    accountRateLimit = EmptyIntegerField(
        min_value=0,
        max_value=1000000,
        required=False,
        allow_null=True,
    )
    projectRateLimit = EmptyIntegerField(
        min_value=50,
        max_value=100,
        required=False,
        allow_null=True,
    )
    avatar = AvatarField(required=False, allow_null=True)
    avatarType = serializers.ChoiceField(
        choices=(
            ('upload', 'upload'),
            ('letter_avatar', 'letter_avatar'),
        ),
        required=False,
        allow_null=True,
    )

    openMembership = serializers.BooleanField(required=False)
    allowSharedIssues = serializers.BooleanField(required=False)
    enhancedPrivacy = serializers.BooleanField(required=False)
    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.BooleanField(required=False)
    attachmentsRole = serializers.CharField(required=True)
    scrubIPAddresses = serializers.BooleanField(required=False)
    scrapeJavaScript = serializers.BooleanField(required=False)
    isEarlyAdopter = serializers.BooleanField(required=False)
    require2FA = serializers.BooleanField(required=False)
    trustedRelays = ListField(child=serializers.CharField(), required=False)

    @memoize
    def _has_legacy_rate_limits(self):
        org = self.context['organization']
        return OrganizationOption.objects.filter(
            organization=org,
            key__in=LEGACY_RATE_LIMIT_OPTIONS,
        ).exists()

    def _has_sso_enabled(self):
        org = self.context['organization']
        return AuthProvider.objects.filter(organization=org).exists()

    def validate_slug(self, value):
        # Historically, the only check just made sure there was more than 1
        # character for the slug, but since then, there are many slugs that
        # fit within this new imposed limit. We're not fixing existing, but
        # just preventing new bad values.
        if len(value) < 3:
            raise serializers.ValidationError(
                'This slug "%s" is too short. Minimum of 3 characters.' %
                (value, ))
        if value in RESERVED_ORGANIZATION_SLUGS:
            raise serializers.ValidationError(
                'This slug "%s" is reserved and not allowed.' % (value, ))
        qs = Organization.objects.filter(
            slug=value, ).exclude(id=self.context['organization'].id)
        if qs.exists():
            raise serializers.ValidationError(
                'The slug "%s" is already in use.' % (value, ))
        return value

    def validate_sensitiveFields(self, value):
        if value and not all(value):
            raise serializers.ValidationError('Empty values are not allowed.')
        return value

    def validate_safeFields(self, value):
        if value and not all(value):
            raise serializers.ValidationError('Empty values are not allowed.')
        return value

    def validate_attachmentsRole(self, value):
        try:
            roles.get(value)
        except KeyError:
            raise serializers.ValidationError('Invalid role')
        return value

    def validate_require2FA(self, value):
        user = self.context['user']
        has_2fa = Authenticator.objects.user_has_2fa(user)
        if value and not has_2fa:
            raise serializers.ValidationError(ERR_NO_2FA)

        if value and self._has_sso_enabled():
            raise serializers.ValidationError(ERR_SSO_ENABLED)
        return value

    def validate_trustedRelays(self, value):
        from sentry import features

        organization = self.context['organization']
        request = self.context["request"]
        has_relays = features.has('organizations:relay',
                                  organization,
                                  actor=request.user)
        if not has_relays:
            raise serializers.ValidationError(
                'Organization does not have the relay feature enabled')
        return value

    def validate_accountRateLimit(self, value):
        if not self._has_legacy_rate_limits:
            raise serializers.ValidationError(
                'The accountRateLimit option cannot be configured for this organization'
            )
        return value

    def validate_projectRateLimit(self, value):
        if not self._has_legacy_rate_limits:
            raise serializers.ValidationError(
                'The accountRateLimit option cannot be configured for this organization'
            )
        return value

    def validate(self, attrs):
        attrs = super(OrganizationSerializer, self).validate(attrs)
        if attrs.get('avatarType') == 'upload':
            has_existing_file = OrganizationAvatar.objects.filter(
                organization=self.context['organization'],
                file__isnull=False,
            ).exists()
            if not has_existing_file and not attrs.get('avatar'):
                raise serializers.ValidationError({
                    'avatarType':
                    'Cannot set avatarType to upload without avatar',
                })
        return attrs

    def save(self):
        org = self.context['organization']
        changed_data = {}

        for key, option, type_, default_value in ORG_OPTIONS:
            if key not in self.initial_data:
                continue
            try:
                option_inst = OrganizationOption.objects.get(organization=org,
                                                             key=option)
            except OrganizationOption.DoesNotExist:
                OrganizationOption.objects.set_value(
                    organization=org,
                    key=option,
                    value=type_(self.initial_data[key]),
                )

                if self.initial_data[key] != default_value:
                    changed_data[key] = u'to {}'.format(self.initial_data[key])
            else:
                option_inst.value = self.initial_data[key]
                # check if ORG_OPTIONS changed
                if option_inst.has_changed('value'):
                    old_val = option_inst.old_value('value')
                    changed_data[key] = u'from {} to {}'.format(
                        old_val, option_inst.value)
                option_inst.save()

        if 'openMembership' in self.initial_data:
            org.flags.allow_joinleave = self.initial_data['openMembership']
        if 'allowSharedIssues' in self.initial_data:
            org.flags.disable_shared_issues = not self.initial_data[
                'allowSharedIssues']
        if 'enhancedPrivacy' in self.initial_data:
            org.flags.enhanced_privacy = self.initial_data['enhancedPrivacy']
        if 'isEarlyAdopter' in self.initial_data:
            org.flags.early_adopter = self.initial_data['isEarlyAdopter']
        if 'require2FA' in self.initial_data:
            org.flags.require_2fa = self.initial_data['require2FA']
        if 'name' in self.initial_data:
            org.name = self.initial_data['name']
        if 'slug' in self.initial_data:
            org.slug = self.initial_data['slug']

        org_tracked_field = {
            'name': org.name,
            'slug': org.slug,
            'default_role': org.default_role,
            'flag_field': {
                'allow_joinleave': org.flags.allow_joinleave.is_set,
                'enhanced_privacy': org.flags.enhanced_privacy.is_set,
                'disable_shared_issues':
                org.flags.disable_shared_issues.is_set,
                'early_adopter': org.flags.early_adopter.is_set,
                'require_2fa': org.flags.require_2fa.is_set,
            }
        }

        # check if fields changed
        for f, v in six.iteritems(org_tracked_field):
            if f is not 'flag_field':
                if org.has_changed(f):
                    old_val = org.old_value(f)
                    changed_data[f] = u'from {} to {}'.format(old_val, v)
            else:
                # check if flag fields changed
                for f, v in six.iteritems(org_tracked_field['flag_field']):
                    if org.flag_has_changed(f):
                        changed_data[f] = u'to {}'.format(v)

        org.save()

        if 'avatar' in self.initial_data or 'avatarType' in self.initial_data:
            OrganizationAvatar.save_avatar(
                relation={'organization': org},
                type=self.initial_data.get('avatarType', 'upload'),
                avatar=self.initial_data.get('avatar'),
                filename=u'{}.png'.format(org.slug),
            )
        if 'require2FA' in self.initial_data and self.initial_data[
                'require2FA'] is True:
            org.handle_2fa_required(self.context['request'])
        return org, changed_data
コード例 #5
0
class OrganizationSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=64)
    slug = serializers.RegexField(r"^[a-z0-9_\-]+$", max_length=50)
    accountRateLimit = EmptyIntegerField(min_value=0,
                                         max_value=1000000,
                                         required=False,
                                         allow_null=True)
    projectRateLimit = EmptyIntegerField(min_value=50,
                                         max_value=100,
                                         required=False,
                                         allow_null=True)
    avatar = AvatarField(required=False, allow_null=True)
    avatarType = serializers.ChoiceField(
        choices=(("upload", "upload"), ("letter_avatar", "letter_avatar")),
        required=False,
        allow_null=True,
    )

    openMembership = serializers.BooleanField(required=False)
    allowSharedIssues = serializers.BooleanField(required=False)
    enhancedPrivacy = serializers.BooleanField(required=False)
    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)
    attachmentsRole = serializers.CharField(required=True)
    debugFilesRole = serializers.CharField(required=True)
    eventsMemberAdmin = serializers.BooleanField(required=False)
    alertsMemberWrite = serializers.BooleanField(required=False)
    scrubIPAddresses = serializers.BooleanField(required=False)
    scrapeJavaScript = serializers.BooleanField(required=False)
    isEarlyAdopter = serializers.BooleanField(required=False)
    require2FA = serializers.BooleanField(required=False)
    requireEmailVerification = serializers.BooleanField(required=False)
    trustedRelays = ListField(child=TrustedRelaySerializer(), required=False)
    allowJoinRequests = serializers.BooleanField(required=False)
    relayPiiConfig = serializers.CharField(required=False,
                                           allow_blank=True,
                                           allow_null=True)
    apdexThreshold = serializers.IntegerField(min_value=1, required=False)

    @memoize
    def _has_legacy_rate_limits(self):
        org = self.context["organization"]
        return OrganizationOption.objects.filter(
            organization=org, key__in=LEGACY_RATE_LIMIT_OPTIONS).exists()

    def _has_sso_enabled(self):
        org = self.context["organization"]
        return AuthProvider.objects.filter(organization=org).exists()

    def validate_slug(self, value):
        # Historically, the only check just made sure there was more than 1
        # character for the slug, but since then, there are many slugs that
        # fit within this new imposed limit. We're not fixing existing, but
        # just preventing new bad values.
        if len(value) < 3:
            raise serializers.ValidationError(
                f'This slug "{value}" is too short. Minimum of 3 characters.')
        if value in RESERVED_ORGANIZATION_SLUGS:
            raise serializers.ValidationError(
                f'This slug "{value}" is reserved and not allowed.')
        qs = Organization.objects.filter(slug=value).exclude(
            id=self.context["organization"].id)
        if qs.exists():
            raise serializers.ValidationError(
                f'The slug "{value}" is already in use.')
        return value

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

    def validate_sensitiveFields(self, value):
        if value and not all(value):
            raise serializers.ValidationError("Empty values are not allowed.")
        return value

    def validate_safeFields(self, value):
        if value and not all(value):
            raise serializers.ValidationError("Empty values are not allowed.")
        return value

    def validate_attachmentsRole(self, value):
        try:
            roles.get(value)
        except KeyError:
            raise serializers.ValidationError("Invalid role")
        return value

    def validate_debugFilesRole(self, value):
        try:
            roles.get(value)
        except KeyError:
            raise serializers.ValidationError("Invalid role")
        return value

    def validate_require2FA(self, value):
        user = self.context["user"]
        has_2fa = Authenticator.objects.user_has_2fa(user)
        if value and not has_2fa:
            raise serializers.ValidationError(ERR_NO_2FA)

        if value and self._has_sso_enabled():
            raise serializers.ValidationError(ERR_SSO_ENABLED)
        return value

    def validate_requireEmailVerification(self, value):
        user = self.context["user"]
        has_verified = UserEmail.get_primary_email(user).is_verified
        if value and not has_verified:
            raise serializers.ValidationError(ERR_EMAIL_VERIFICATION)
        return value

    def validate_trustedRelays(self, value):
        from sentry import features

        organization = self.context["organization"]
        request = self.context["request"]
        has_relays = features.has("organizations:relay",
                                  organization,
                                  actor=request.user)
        if not has_relays:
            raise serializers.ValidationError(
                "Organization does not have the relay feature enabled")

        # make sure we don't have multiple instances of one public key
        public_keys = set()
        if value is not None:
            for key_info in value:
                key = key_info.get("public_key")
                if key in public_keys:
                    raise serializers.ValidationError(
                        f"Duplicated key in Trusted Relays: '{key}'")
                public_keys.add(key)

        return value

    def validate_accountRateLimit(self, value):
        if not self._has_legacy_rate_limits:
            raise serializers.ValidationError(
                "The accountRateLimit option cannot be configured for this organization"
            )
        return value

    def validate_projectRateLimit(self, value):
        if not self._has_legacy_rate_limits:
            raise serializers.ValidationError(
                "The accountRateLimit option cannot be configured for this organization"
            )
        return value

    def validate(self, attrs):
        attrs = super().validate(attrs)
        if attrs.get("avatarType") == "upload":
            has_existing_file = OrganizationAvatar.objects.filter(
                organization=self.context["organization"],
                file__isnull=False).exists()
            if not has_existing_file and not attrs.get("avatar"):
                raise serializers.ValidationError({
                    "avatarType":
                    "Cannot set avatarType to upload without avatar"
                })
        return attrs

    def save_trusted_relays(self, incoming, changed_data, organization):
        timestamp_now = datetime.utcnow().replace(tzinfo=UTC).isoformat()
        option_key = "sentry:trusted-relays"
        try:
            # get what we already have
            existing = OrganizationOption.objects.get(
                organization=organization, key=option_key)

            key_dict = {val.get("public_key"): val for val in existing.value}
            original_number_of_keys = len(existing.value)
        except OrganizationOption.DoesNotExist:
            key_dict = {}  # we don't have anything set
            original_number_of_keys = 0
            existing = None

        modified = False
        for option in incoming:
            public_key = option.get("public_key")
            existing_info = key_dict.get(public_key, {})

            option["created"] = existing_info.get("created", timestamp_now)
            option["last_modified"] = existing_info.get("last_modified")

            # check if we modified the current public_key info and update last_modified if we did
            if (not existing_info
                    or existing_info.get("name") != option.get("name")
                    or existing_info.get("description") !=
                    option.get("description")):
                option["last_modified"] = timestamp_now
                modified = True

        # check to see if the only modifications were some deletions (which are not captured in the loop above)
        if len(incoming) != original_number_of_keys:
            modified = True

        if modified:
            # we have some modifications create a log message
            if existing is not None:
                # generate an update log message
                changed_data[
                    "trustedRelays"] = f"from {existing} to {incoming}"
                existing.value = incoming
                existing.save()
            else:
                # first time we set trusted relays, generate a create log message
                changed_data["trustedRelays"] = f"to {incoming}"
                OrganizationOption.objects.set_value(organization=organization,
                                                     key=option_key,
                                                     value=incoming)

        return incoming

    def save(self):
        from sentry import features

        org = self.context["organization"]
        changed_data = {}
        if not hasattr(org, "__data"):
            update_tracked_data(org)

        for key, option, type_, default_value in ORG_OPTIONS:
            if key not in self.initial_data:
                continue
            try:
                option_inst = OrganizationOption.objects.get(organization=org,
                                                             key=option)
                update_tracked_data(option_inst)
            except OrganizationOption.DoesNotExist:
                OrganizationOption.objects.set_value(
                    organization=org,
                    key=option,
                    value=type_(self.initial_data[key]))

                if self.initial_data[key] != default_value:
                    changed_data[key] = f"to {self.initial_data[key]}"
            else:
                option_inst.value = self.initial_data[key]
                # check if ORG_OPTIONS changed
                if has_changed(option_inst, "value"):
                    old_val = old_value(option_inst, "value")
                    changed_data[
                        key] = f"from {old_val} to {option_inst.value}"
                option_inst.save()

        trusted_realy_info = self.validated_data.get("trustedRelays")
        if trusted_realy_info is not None:
            self.save_trusted_relays(trusted_realy_info, changed_data, org)

        if "openMembership" in self.initial_data:
            org.flags.allow_joinleave = self.initial_data["openMembership"]
        if "allowSharedIssues" in self.initial_data:
            org.flags.disable_shared_issues = not self.initial_data[
                "allowSharedIssues"]
        if "enhancedPrivacy" in self.initial_data:
            org.flags.enhanced_privacy = self.initial_data["enhancedPrivacy"]
        if "isEarlyAdopter" in self.initial_data:
            org.flags.early_adopter = self.initial_data["isEarlyAdopter"]
        if "require2FA" in self.initial_data:
            org.flags.require_2fa = self.initial_data["require2FA"]
        if (features.has("organizations:required-email-verification", org)
                and "requireEmailVerification" in self.initial_data):
            org.flags.require_email_verification = self.initial_data[
                "requireEmailVerification"]
        if "name" in self.initial_data:
            org.name = self.initial_data["name"]
        if "slug" in self.initial_data:
            org.slug = self.initial_data["slug"]

        org_tracked_field = {
            "name": org.name,
            "slug": org.slug,
            "default_role": org.default_role,
            "flag_field": {
                "allow_joinleave": org.flags.allow_joinleave.is_set,
                "enhanced_privacy": org.flags.enhanced_privacy.is_set,
                "disable_shared_issues":
                org.flags.disable_shared_issues.is_set,
                "early_adopter": org.flags.early_adopter.is_set,
                "require_2fa": org.flags.require_2fa.is_set,
            },
        }

        # check if fields changed
        for f, v in org_tracked_field.items():
            if f != "flag_field":
                if has_changed(org, f):
                    old_val = old_value(org, f)
                    changed_data[f] = f"from {old_val} to {v}"
            else:
                # check if flag fields changed
                for f, v in org_tracked_field["flag_field"].items():
                    if flag_has_changed(org, f):
                        changed_data[f] = f"to {v}"

        org.save()

        if "avatar" in self.initial_data or "avatarType" in self.initial_data:
            OrganizationAvatar.save_avatar(
                relation={"organization": org},
                type=self.initial_data.get("avatarType", "upload"),
                avatar=self.initial_data.get("avatar"),
                filename=f"{org.slug}.png",
            )
        if self.initial_data.get("require2FA") is True:
            org.handle_2fa_required(self.context["request"])
        if (features.has("organizations:required-email-verification", org)
                and self.initial_data.get("requireEmailVerification") is True):
            org.handle_email_verification_required(self.context["request"])
        return org, changed_data
コード例 #6
0
class OrganizationSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=64)
    slug = serializers.RegexField(r'^[a-z0-9_\-]+$', max_length=50)
    accountRateLimit = serializers.IntegerField(min_value=0,
                                                max_value=1000000,
                                                required=False)
    projectRateLimit = serializers.IntegerField(min_value=50,
                                                max_value=100,
                                                required=False)
    avatar = AvatarField(required=False)
    avatarType = serializers.ChoiceField(choices=(
        ('upload', 'upload'),
        ('letter_avatar', 'letter_avatar'),
    ),
                                         required=False)

    openMembership = serializers.BooleanField(required=False)
    allowSharedIssues = serializers.BooleanField(required=False)
    enhancedPrivacy = serializers.BooleanField(required=False)
    dataScrubber = serializers.BooleanField(required=False)
    dataScrubberDefaults = serializers.BooleanField(required=False)
    sensitiveFields = ListField(child=serializers.CharField(), required=False)
    safeFields = ListField(child=serializers.CharField(), required=False)
    scrubIPAddresses = serializers.BooleanField(required=False)
    isEarlyAdopter = serializers.BooleanField(required=False)

    def validate_slug(self, attrs, source):
        value = attrs[source]
        if Organization.objects.filter(slug=value).exclude(
                id=self.context['organization'].id):
            raise serializers.ValidationError(
                'The slug "%s" is already in use.' % (value, ))
        return attrs

    def validate(self, attrs):
        attrs = super(OrganizationSerializer, self).validate(attrs)
        if attrs.get('avatarType') == 'upload':
            has_existing_file = OrganizationAvatar.objects.filter(
                organization=self.context['organization'],
                file__isnull=False,
            ).exists()
            if not has_existing_file and not attrs.get('avatar'):
                raise serializers.ValidationError({
                    'avatarType':
                    'Cannot set avatarType to upload without avatar',
                })
        return attrs

    def save(self):
        org = self.context['organization']
        if 'openMembership' in self.init_data:
            org.flags.allow_joinleave = self.init_data['openMembership']
        if 'allowSharedIssues' in self.init_data:
            org.flags.disable_shared_issues = not self.init_data[
                'allowSharedIssues']
        if 'enhancedPrivacy' in self.init_data:
            org.flags.enhanced_privacy = self.init_data['enhancedPrivacy']
        if 'isEarlyAdopter' in self.init_data:
            org.flags.early_adopter = self.init_data['isEarlyAdopter']
        if 'name' in self.init_data:
            org.name = self.init_data['name']
        if 'slug' in self.init_data:
            org.slug = self.init_data['slug']
        org.save()
        for key, option, type_ in ORG_OPTIONS:
            if key in self.init_data:
                OrganizationOption.objects.set_value(
                    organization=org,
                    key=option,
                    value=type_(self.init_data[key]),
                )
        if 'avatar' in self.init_data or 'avatarType' in self.init_data:
            OrganizationAvatar.save_avatar(
                relation={'organization': org},
                type=self.init_data.get('avatarType', 'upload'),
                avatar=self.init_data.get('avatar'),
                filename='{}.png'.format(org.slug),
            )
        return org
コード例 #7
0
class OrganizationSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=64)
    slug = serializers.RegexField(r'^[a-z0-9_\-]+$', max_length=50)
    accountRateLimit = serializers.IntegerField(min_value=0,
                                                max_value=1000000,
                                                required=False)
    projectRateLimit = serializers.IntegerField(min_value=50,
                                                max_value=100,
                                                required=False)
    avatar = AvatarField(required=False)
    avatarType = serializers.ChoiceField(choices=(
        ('upload', 'upload'),
        ('letter_avatar', 'letter_avatar'),
    ),
                                         required=False)

    openMembership = serializers.BooleanField(required=False)
    allowSharedIssues = serializers.BooleanField(required=False)
    enhancedPrivacy = serializers.BooleanField(required=False)
    dataScrubber = serializers.BooleanField(required=False)
    dataScrubberDefaults = serializers.BooleanField(required=False)
    sensitiveFields = ListField(child=serializers.CharField(), required=False)
    safeFields = ListField(child=serializers.CharField(), required=False)
    scrubIPAddresses = serializers.BooleanField(required=False)
    isEarlyAdopter = serializers.BooleanField(required=False)
    require2FA = serializers.BooleanField(required=False)

    def validate_slug(self, attrs, source):
        value = attrs[source]
        # Historically, the only check just made sure there was more than 1
        # character for the slug, but since then, there are many slugs that
        # fit within this new imposed limit. We're not fixing existing, but
        # just preventing new bad values.
        if len(value) < 3:
            raise serializers.ValidationError(
                'This slug "%s" is too short. Minimum of 3 characters.' %
                (value, ))
        if value in RESERVED_ORGANIZATION_SLUGS:
            raise serializers.ValidationError(
                'This slug "%s" is reserved and not allowed.' % (value, ))
        qs = Organization.objects.filter(
            slug=value, ).exclude(id=self.context['organization'].id)
        if qs.exists():
            raise serializers.ValidationError(
                'The slug "%s" is already in use.' % (value, ))
        return attrs

    def validate_sensitiveFields(self, attrs, source):
        value = attrs[source]
        if value and not all(value):
            raise serializers.ValidationError('Empty values are not allowed.')
        return attrs

    def validate_safeFields(self, attrs, source):
        value = attrs[source]
        if value and not all(value):
            raise serializers.ValidationError('Empty values are not allowed.')
        return attrs

    def validate_require2FA(self, attrs, source):
        value = attrs[source]
        user = self.context['user']
        has_2fa = Authenticator.objects.user_has_2fa(user)
        if value and not has_2fa:
            raise serializers.ValidationError(
                'User setting two-factor authentication enforcement without two-factor authentication enabled.'
            )
        return attrs

    def validate(self, attrs):
        attrs = super(OrganizationSerializer, self).validate(attrs)
        if attrs.get('avatarType') == 'upload':
            has_existing_file = OrganizationAvatar.objects.filter(
                organization=self.context['organization'],
                file__isnull=False,
            ).exists()
            if not has_existing_file and not attrs.get('avatar'):
                raise serializers.ValidationError({
                    'avatarType':
                    'Cannot set avatarType to upload without avatar',
                })
        return attrs

    def save(self):
        org = self.context['organization']
        if 'openMembership' in self.init_data:
            org.flags.allow_joinleave = self.init_data['openMembership']
        if 'allowSharedIssues' in self.init_data:
            org.flags.disable_shared_issues = not self.init_data[
                'allowSharedIssues']
        if 'enhancedPrivacy' in self.init_data:
            org.flags.enhanced_privacy = self.init_data['enhancedPrivacy']
        if 'isEarlyAdopter' in self.init_data:
            org.flags.early_adopter = self.init_data['isEarlyAdopter']
        if 'require2FA' in self.init_data:
            org.flags.require_2fa = self.init_data['require2FA']
        if 'name' in self.init_data:
            org.name = self.init_data['name']
        if 'slug' in self.init_data:
            org.slug = self.init_data['slug']
        org.save()
        for key, option, type_ in ORG_OPTIONS:
            if key in self.init_data:
                OrganizationOption.objects.set_value(
                    organization=org,
                    key=option,
                    value=type_(self.init_data[key]),
                )
        if 'avatar' in self.init_data or 'avatarType' in self.init_data:
            OrganizationAvatar.save_avatar(
                relation={'organization': org},
                type=self.init_data.get('avatarType', 'upload'),
                avatar=self.init_data.get('avatar'),
                filename='{}.png'.format(org.slug),
            )
        if 'require2FA' in self.init_data and self.init_data[
                'require2FA'] is True:
            org.send_setup_2fa_emails()
        return org
コード例 #8
0
class OrganizationSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=64)
    slug = serializers.RegexField(r"^[a-z0-9_\-]+$", max_length=50)
    accountRateLimit = EmptyIntegerField(
        min_value=0, max_value=1000000, required=False, allow_null=True
    )
    projectRateLimit = EmptyIntegerField(
        min_value=50, max_value=100, required=False, allow_null=True
    )
    avatar = AvatarField(required=False, allow_null=True)
    avatarType = serializers.ChoiceField(
        choices=(("upload", "upload"), ("letter_avatar", "letter_avatar")),
        required=False,
        allow_null=True,
    )

    openMembership = serializers.BooleanField(required=False)
    allowSharedIssues = serializers.BooleanField(required=False)
    enhancedPrivacy = serializers.BooleanField(required=False)
    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)
    attachmentsRole = serializers.CharField(required=True)
    scrubIPAddresses = serializers.BooleanField(required=False)
    scrapeJavaScript = serializers.BooleanField(required=False)
    isEarlyAdopter = serializers.BooleanField(required=False)
    require2FA = serializers.BooleanField(required=False)
    trustedRelays = ListField(child=serializers.CharField(), required=False)
    allowJoinRequests = serializers.BooleanField(required=False)

    @memoize
    def _has_legacy_rate_limits(self):
        org = self.context["organization"]
        return OrganizationOption.objects.filter(
            organization=org, key__in=LEGACY_RATE_LIMIT_OPTIONS
        ).exists()

    def _has_sso_enabled(self):
        org = self.context["organization"]
        return AuthProvider.objects.filter(organization=org).exists()

    def validate_slug(self, value):
        # Historically, the only check just made sure there was more than 1
        # character for the slug, but since then, there are many slugs that
        # fit within this new imposed limit. We're not fixing existing, but
        # just preventing new bad values.
        if len(value) < 3:
            raise serializers.ValidationError(
                'This slug "%s" is too short. Minimum of 3 characters.' % (value,)
            )
        if value in RESERVED_ORGANIZATION_SLUGS:
            raise serializers.ValidationError(
                'This slug "%s" is reserved and not allowed.' % (value,)
            )
        qs = Organization.objects.filter(slug=value).exclude(id=self.context["organization"].id)
        if qs.exists():
            raise serializers.ValidationError('The slug "%s" is already in use.' % (value,))
        return value

    def validate_sensitiveFields(self, value):
        if value and not all(value):
            raise serializers.ValidationError("Empty values are not allowed.")
        return value

    def validate_safeFields(self, value):
        if value and not all(value):
            raise serializers.ValidationError("Empty values are not allowed.")
        return value

    def validate_attachmentsRole(self, value):
        try:
            roles.get(value)
        except KeyError:
            raise serializers.ValidationError("Invalid role")
        return value

    def validate_require2FA(self, value):
        user = self.context["user"]
        has_2fa = Authenticator.objects.user_has_2fa(user)
        if value and not has_2fa:
            raise serializers.ValidationError(ERR_NO_2FA)

        if value and self._has_sso_enabled():
            raise serializers.ValidationError(ERR_SSO_ENABLED)
        return value

    def validate_trustedRelays(self, value):
        from sentry import features

        organization = self.context["organization"]
        request = self.context["request"]
        has_relays = features.has("organizations:relay", organization, actor=request.user)
        if not has_relays:
            raise serializers.ValidationError(
                "Organization does not have the relay feature enabled"
            )
        return value

    def validate_accountRateLimit(self, value):
        if not self._has_legacy_rate_limits:
            raise serializers.ValidationError(
                "The accountRateLimit option cannot be configured for this organization"
            )
        return value

    def validate_projectRateLimit(self, value):
        if not self._has_legacy_rate_limits:
            raise serializers.ValidationError(
                "The accountRateLimit option cannot be configured for this organization"
            )
        return value

    def validate(self, attrs):
        attrs = super(OrganizationSerializer, self).validate(attrs)
        if attrs.get("avatarType") == "upload":
            has_existing_file = OrganizationAvatar.objects.filter(
                organization=self.context["organization"], file__isnull=False
            ).exists()
            if not has_existing_file and not attrs.get("avatar"):
                raise serializers.ValidationError(
                    {"avatarType": "Cannot set avatarType to upload without avatar"}
                )
        return attrs

    def save(self):
        org = self.context["organization"]
        changed_data = {}

        for key, option, type_, default_value in ORG_OPTIONS:
            if key not in self.initial_data:
                continue
            try:
                option_inst = OrganizationOption.objects.get(organization=org, key=option)
            except OrganizationOption.DoesNotExist:
                OrganizationOption.objects.set_value(
                    organization=org, key=option, value=type_(self.initial_data[key])
                )

                if self.initial_data[key] != default_value:
                    changed_data[key] = u"to {}".format(self.initial_data[key])
            else:
                option_inst.value = self.initial_data[key]
                # check if ORG_OPTIONS changed
                if option_inst.has_changed("value"):
                    old_val = option_inst.old_value("value")
                    changed_data[key] = u"from {} to {}".format(old_val, option_inst.value)
                option_inst.save()

        if "openMembership" in self.initial_data:
            org.flags.allow_joinleave = self.initial_data["openMembership"]
        if "allowSharedIssues" in self.initial_data:
            org.flags.disable_shared_issues = not self.initial_data["allowSharedIssues"]
        if "enhancedPrivacy" in self.initial_data:
            org.flags.enhanced_privacy = self.initial_data["enhancedPrivacy"]
        if "isEarlyAdopter" in self.initial_data:
            org.flags.early_adopter = self.initial_data["isEarlyAdopter"]
        if "require2FA" in self.initial_data:
            org.flags.require_2fa = self.initial_data["require2FA"]
        if "name" in self.initial_data:
            org.name = self.initial_data["name"]
        if "slug" in self.initial_data:
            org.slug = self.initial_data["slug"]

        org_tracked_field = {
            "name": org.name,
            "slug": org.slug,
            "default_role": org.default_role,
            "flag_field": {
                "allow_joinleave": org.flags.allow_joinleave.is_set,
                "enhanced_privacy": org.flags.enhanced_privacy.is_set,
                "disable_shared_issues": org.flags.disable_shared_issues.is_set,
                "early_adopter": org.flags.early_adopter.is_set,
                "require_2fa": org.flags.require_2fa.is_set,
            },
        }

        # check if fields changed
        for f, v in six.iteritems(org_tracked_field):
            if f != "flag_field":
                if org.has_changed(f):
                    old_val = org.old_value(f)
                    changed_data[f] = u"from {} to {}".format(old_val, v)
            else:
                # check if flag fields changed
                for f, v in six.iteritems(org_tracked_field["flag_field"]):
                    if org.flag_has_changed(f):
                        changed_data[f] = u"to {}".format(v)

        org.save()

        if "avatar" in self.initial_data or "avatarType" in self.initial_data:
            OrganizationAvatar.save_avatar(
                relation={"organization": org},
                type=self.initial_data.get("avatarType", "upload"),
                avatar=self.initial_data.get("avatar"),
                filename=u"{}.png".format(org.slug),
            )
        if "require2FA" in self.initial_data and self.initial_data["require2FA"] is True:
            org.handle_2fa_required(self.context["request"])
        return org, changed_data
コード例 #9
0
class OrganizationSerializer(serializers.ModelSerializer):
    accountRateLimit = serializers.IntegerField(min_value=0, max_value=1000000)
    projectRateLimit = serializers.IntegerField(min_value=50, max_value=100)
    slug = serializers.RegexField(r'^[a-z0-9_\-]+$',
                                  max_length=50,
                                  required=False)
    avatar = AvatarField()
    avatarType = serializers.ChoiceField(choices=(
        ('upload', 'upload'),
        ('letter_avatar', 'letter_avatar'),
    ))

    class Meta:
        model = Organization
        fields = ('name', 'slug')

    def validate_slug(self, attrs, source):
        value = attrs[source]
        if Organization.objects.filter(slug=value).exclude(id=self.object.id):
            raise serializers.ValidationError(
                'The slug "%s" is already in use.' % (value, ))
        return attrs

    def validate(self, attrs):
        attrs = super(OrganizationSerializer, self).validate(attrs)
        if attrs.get('avatarType') == 'upload':
            has_existing_file = OrganizationAvatar.objects.filter(
                organization=self.object,
                file__isnull=False,
            ).exists()
            if not has_existing_file and not attrs.get('avatar'):
                raise serializers.ValidationError({
                    'avatarType':
                    'Cannot set avatarType to upload without avatar',
                })
        return attrs

    def save(self):
        rv = super(OrganizationSerializer, self).save()
        # XXX(dcramer): this seems wrong, but cant find documentation on how to
        # actually access this data
        if 'projectRateLimit' in self.init_data:
            OrganizationOption.objects.set_value(
                organization=self.object,
                key='sentry:project-rate-limit',
                value=int(self.init_data['projectRateLimit']),
            )
        if 'accountRateLimit' in self.init_data:
            OrganizationOption.objects.set_value(
                organization=self.object,
                key='sentry:account-rate-limit',
                value=int(self.init_data['accountRateLimit']),
            )
        if 'avatar' in self.init_data or 'avatarType' in self.init_data:
            OrganizationAvatar.save_avatar(
                relation={'organization': self.object},
                type=self.init_data.get('avatarType', 'upload'),
                avatar=self.init_data.get('avatar'),
                filename='{}.png'.format(self.object.slug),
            )
        return rv