Ejemplo n.º 1
0
class TranslatedModel(ModelBase):
    name = TranslatedField()
    description = TranslatedField()
    default_locale = models.CharField(max_length=10)
    no_locale = TranslatedField(require_locale=False)

    objects = ManagerBase()
Ejemplo n.º 2
0
class TranslatedModel(ModelBase):
    name = TranslatedField()
    description = TranslatedField()
    default_locale = models.CharField(max_length=10)
    no_locale = TranslatedField(require_locale=False)

    translated_through_fk = models.ForeignKey(
        'TranslatedModelLinkedAsForeignKey', null=True, default=None)
    objects = ManagerBase()
Ejemplo n.º 3
0
class L10nEventlog(caching.base.CachingMixin, models.Model):
    locale = models.CharField(max_length=30, default='')
    type = models.CharField(max_length=20, default='')
    action = models.CharField(max_length=40, default='')
    field = models.CharField(max_length=20, default='', null=True)
    user = models.ForeignKey('users.UserProfile')
    changed_id = models.PositiveIntegerField(
        default=0, help_text='id of the object being affected by the change')
    added = models.CharField(max_length=255, default='', null=True)
    removed = models.CharField(max_length=255, default='', null=True)
    notes = models.TextField()

    created = models.DateTimeField(auto_now_add=True)

    objects = ManagerBase()

    class Meta:
        db_table = 'l10n_eventlog'
        get_latest_by = 'created'
Ejemplo n.º 4
0
 def __init__(self, include_deleted=False):
     ManagerBase.__init__(self)
     self.include_deleted = include_deleted
Ejemplo n.º 5
0
 def __init__(self, include_deleted=False):
     # DO NOT change the default value of include_deleted unless you've read
     # through the comment just above the Addon managers
     # declaration/instanciation and understand the consequences.
     ManagerBase.__init__(self)
     self.include_deleted = include_deleted
Ejemplo n.º 6
0
 def __init__(self, include_deleted=False):
     ManagerBase.__init__(self)
     self.include_deleted = include_deleted
Ejemplo n.º 7
0
class FileUpload(ModelBase):
    """Created when a file is uploaded for validation/submission."""
    uuid = models.UUIDField(default=uuid.uuid4, editable=False)
    path = models.CharField(max_length=255, default='')
    name = models.CharField(max_length=255,
                            default='',
                            help_text="The user's original filename")
    hash = models.CharField(max_length=255, default='')
    user = models.ForeignKey('users.UserProfile',
                             null=True,
                             on_delete=models.CASCADE)
    valid = models.BooleanField(default=False)
    validation = models.TextField(null=True)
    automated_signing = models.BooleanField(default=False)
    compat_with_app = models.PositiveIntegerField(
        choices=amo.APPS_CHOICES, db_column="compat_with_app_id", null=True)
    compat_with_appver = models.ForeignKey(
        AppVersion,
        null=True,
        related_name='uploads_compat_for_appver',
        on_delete=models.CASCADE)
    # Not all FileUploads will have a version and addon but it will be set
    # if the file was uploaded using the new API.
    version = models.CharField(max_length=255, null=True)
    addon = models.ForeignKey('addons.Addon',
                              null=True,
                              on_delete=models.CASCADE)

    objects = ManagerBase()

    class Meta(ModelBase.Meta):
        db_table = 'file_uploads'

    def __str__(self):
        return six.text_type(self.uuid.hex)

    def save(self, *args, **kw):
        if self.validation:
            if self.load_validation()['errors'] == 0:
                self.valid = True
        super(FileUpload, self).save(*args, **kw)

    def add_file(self, chunks, filename, size):
        if not self.uuid:
            self.uuid = self._meta.get_field('uuid')._create_uuid()

        filename = force_text(u'{0}_{1}'.format(self.uuid.hex, filename))
        loc = os.path.join(user_media_path('addons'), 'temp', uuid.uuid4().hex)
        base, ext = os.path.splitext(filename)
        is_crx = False

        # Change a ZIP to an XPI, to maintain backward compatibility
        # with older versions of Firefox and to keep the rest of the XPI code
        # path as consistent as possible for ZIP uploads.
        # See: https://github.com/mozilla/addons-server/pull/2785
        if ext == '.zip':
            ext = '.xpi'

        # If the extension is a CRX, we need to do some actual work to it
        # before we just convert it to an XPI. We strip the header from the
        # CRX, then it's good; see more about the CRX file format here:
        # https://developer.chrome.com/extensions/crx
        if ext == '.crx':
            ext = '.xpi'
            is_crx = True

        if ext in amo.VALID_ADDON_FILE_EXTENSIONS:
            loc += ext

        log.info('UPLOAD: %r (%s bytes) to %r' % (filename, size, loc))
        if is_crx:
            hash_func = write_crx_as_xpi(chunks, loc)
        else:
            hash_func = hashlib.sha256()
            with storage.open(loc, 'wb') as file_destination:
                for chunk in chunks:
                    hash_func.update(chunk)
                    file_destination.write(chunk)
        self.path = loc
        self.name = filename
        self.hash = 'sha256:%s' % hash_func.hexdigest()
        self.save()

    @classmethod
    def from_post(cls, chunks, filename, size, **params):
        upload = FileUpload(**params)
        upload.add_file(chunks, filename, size)
        return upload

    @property
    def processed(self):
        return bool(self.valid or self.validation)

    @property
    def validation_timeout(self):
        if self.processed:
            validation = self.load_validation()
            messages = validation['messages']
            timeout_id = [
                'validator', 'unexpected_exception', 'validation_timeout'
            ]
            return any(msg['id'] == timeout_id for msg in messages)
        else:
            return False

    @property
    def processed_validation(self):
        """Return processed validation results as expected by the frontend."""
        if self.validation:
            # Import loop.
            from olympia.devhub.utils import process_validation

            validation = self.load_validation()
            is_compatibility = self.compat_with_app is not None

            return process_validation(validation,
                                      is_compatibility=is_compatibility,
                                      file_hash=self.hash)

    @property
    def passed_all_validations(self):
        return self.processed and self.valid

    def load_validation(self):
        return json.loads(self.validation)

    @property
    def pretty_name(self):
        parts = self.name.split('_', 1)
        if len(parts) > 1:
            return parts[1]
        return self.name
Ejemplo n.º 8
0
class FileUpload(ModelBase):
    """Created when a file is uploaded for validation/submission."""

    uuid = models.UUIDField(default=uuid.uuid4, editable=False)
    path = models.CharField(max_length=255, default='')
    name = models.CharField(max_length=255,
                            default='',
                            help_text="The user's original filename")
    hash = models.CharField(max_length=255, default='')
    user = models.ForeignKey('users.UserProfile',
                             null=True,
                             on_delete=models.CASCADE)
    valid = models.BooleanField(default=False)
    validation = models.TextField(null=True)
    automated_signing = models.BooleanField(default=False)
    # Not all FileUploads will have a version and addon but it will be set
    # if the file was uploaded using the new API.
    version = models.CharField(max_length=255, null=True)
    addon = models.ForeignKey('addons.Addon',
                              null=True,
                              on_delete=models.CASCADE)
    access_token = models.CharField(max_length=40, null=True)
    ip_address = models.CharField(max_length=45, null=True, default=None)
    source = models.PositiveSmallIntegerField(
        choices=amo.UPLOAD_SOURCE_CHOICES, default=None, null=True)

    objects = ManagerBase()

    class Meta(ModelBase.Meta):
        db_table = 'file_uploads'
        constraints = [
            models.UniqueConstraint(fields=('uuid', ), name='uuid'),
        ]

    def __str__(self):
        return str(self.uuid.hex)

    def save(self, *args, **kw):
        if self.validation:
            if self.load_validation()['errors'] == 0:
                self.valid = True
        if not self.access_token:
            self.access_token = self.generate_access_token()
        super(FileUpload, self).save(*args, **kw)

    def write_data_to_path(self, chunks):
        hash_obj = hashlib.sha256()
        with storage.open(self.path, 'wb') as file_destination:
            for chunk in chunks:
                hash_obj.update(chunk)
                file_destination.write(chunk)
        return hash_obj

    def add_file(self, chunks, filename, size):
        if not self.uuid:
            self.uuid = self._meta.get_field('uuid')._create_uuid()

        _base, ext = os.path.splitext(filename)
        was_crx = ext == '.crx'
        # Filename we'll expose (but not use for storage).
        self.name = force_str('{0}_{1}'.format(self.uuid.hex, filename))

        # Final path on our filesystem. If it had a valid extension we change
        # it to .xpi (CRX files are converted before validation, so they will
        # be treated as normal .xpi for validation). If somehow this is
        # not a valid archive or the extension is invalid parse_addon() will
        # eventually complain at validation time or before even reaching the
        # linter.
        if ext in amo.VALID_ADDON_FILE_EXTENSIONS:
            ext = '.xpi'
        self.path = os.path.join(user_media_path('addons'), 'temp',
                                 '{0}{1}'.format(uuid.uuid4().hex, ext))

        hash_obj = None
        if was_crx:
            try:
                hash_obj = write_crx_as_xpi(chunks, self.path)
            except InvalidOrUnsupportedCrx:
                # We couldn't convert the crx file. Write it to the filesystem
                # normally, the validation process should reject this with a
                # proper error later.
                pass
        if hash_obj is None:
            hash_obj = self.write_data_to_path(chunks)
        self.hash = 'sha256:%s' % hash_obj.hexdigest()

        # The following log statement is used by foxsec-pipeline.
        log.info(
            'UPLOAD: %r (%s bytes) to %r' % (self.name, size, self.path),
            extra={
                'email':
                (self.user.email if self.user and self.user.email else ''),
                'upload_hash': self.hash,
            },
        )
        self.save()

    def generate_access_token(self):
        """
        Returns an access token used to secure download URLs.
        """
        return get_random_string(40)

    def get_authenticated_download_url(self):
        """
        Returns a download URL containing an access token bound to this file.
        """
        absolute_url = urljoin(
            settings.EXTERNAL_SITE_URL,
            reverse('files.serve_file_upload', kwargs={'uuid': self.uuid.hex}),
        )
        return '{}?access_token={}'.format(absolute_url, self.access_token)

    @classmethod
    def from_post(cls, chunks, filename, size, **params):
        upload = FileUpload(**params)
        upload.add_file(chunks, filename, size)
        return upload

    @property
    def processed(self):
        return bool(self.valid or self.validation)

    @property
    def validation_timeout(self):
        if self.processed:
            validation = self.load_validation()
            messages = validation['messages']
            timeout_id = [
                'validator', 'unexpected_exception', 'validation_timeout'
            ]
            return any(msg['id'] == timeout_id for msg in messages)
        else:
            return False

    @property
    def processed_validation(self):
        """Return processed validation results as expected by the frontend."""
        if self.validation:
            # Import loop.
            from olympia.devhub.utils import process_validation

            validation = self.load_validation()

            return process_validation(validation, file_hash=self.hash)

    @property
    def passed_all_validations(self):
        return self.processed and self.valid

    def load_validation(self):
        return json.loads(self.validation)

    @property
    def pretty_name(self):
        parts = self.name.split('_', 1)
        if len(parts) > 1:
            return parts[1]
        return self.name
Ejemplo n.º 9
0
 def __init__(self, include_deleted=False):
     # DO NOT change the default value of include_deleted unless you've read
     # through the comment just above the Addon managers
     # declaration/instanciation and understand the consequences.
     ManagerBase.__init__(self)
     self.include_deleted = include_deleted
Ejemplo n.º 10
0
class TranslatedModelWithDefaultNull(ModelBase):
    name = TranslatedField(default=None)

    objects = ManagerBase()