Ejemplo n.º 1
0
    def get_one(self, ref_or_id, file_path, requester_user, **kwargs):
        """
            Outputs the content of a specific file in a pack.

            Handles requests:
                GET /packs/views/file/<pack_ref_or_id>/<file path>
        """
        pack_db = self._get_by_ref_or_id(ref_or_id=ref_or_id)

        if not pack_db:
            msg = 'Pack with ref_or_id "%s" does not exist' % (ref_or_id)
            raise StackStormDBObjectNotFoundError(msg)

        if not file_path:
            raise ValueError('Missing file path')

        pack_ref = pack_db.ref

        # Note: Until list filtering is in place we don't require RBAC check for icon file
        permission_type = PermissionType.PACK_VIEW
        if file_path not in WHITELISTED_FILE_PATHS:
            rbac_utils.assert_user_has_resource_db_permission(
                user_db=requester_user,
                resource_db=pack_db,
                permission_type=permission_type)

        normalized_file_path = get_pack_file_abs_path(pack_ref=pack_ref,
                                                      file_path=file_path)
        if not normalized_file_path or not os.path.isfile(
                normalized_file_path):
            # Ignore references to files which don't exist on disk
            raise StackStormDBObjectNotFoundError('File "%s" not found' %
                                                  (file_path))

        file_size, file_mtime = self._get_file_stats(
            file_path=normalized_file_path)

        response = Response()

        if not self._is_file_changed(file_mtime, **kwargs):
            response.status = http_client.NOT_MODIFIED
        else:
            if file_size is not None and file_size > MAX_FILE_SIZE:
                msg = ('File %s exceeds maximum allowed file size (%s bytes)' %
                       (file_path, MAX_FILE_SIZE))
                raise ValueError(msg)

            content_type = mimetypes.guess_type(normalized_file_path)[0] or \
                'application/octet-stream'

            response.headers['Content-Type'] = content_type
            response.body = self._get_file_content(
                file_path=normalized_file_path)

        response.headers['Last-Modified'] = format_date_time(file_mtime)
        response.headers['ETag'] = repr(file_mtime)

        return response
Ejemplo n.º 2
0
    def get_one(self, ref_or_id, file_path, requester_user, if_none_match=None,
                if_modified_since=None):
        """
            Outputs the content of a specific file in a pack.

            Handles requests:
                GET /packs/views/file/<pack_ref_or_id>/<file path>
        """
        pack_db = self._get_by_ref_or_id(ref_or_id=ref_or_id)

        if not pack_db:
            msg = 'Pack with ref_or_id "%s" does not exist' % (ref_or_id)
            raise StackStormDBObjectNotFoundError(msg)

        if not file_path:
            raise ValueError('Missing file path')

        pack_ref = pack_db.ref

        # Note: Until list filtering is in place we don't require RBAC check for icon file
        permission_type = PermissionType.PACK_VIEW
        if file_path not in WHITELISTED_FILE_PATHS:
            rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
                                                              resource_db=pack_db,
                                                              permission_type=permission_type)

        normalized_file_path = get_pack_file_abs_path(pack_ref=pack_ref, file_path=file_path)
        if not normalized_file_path or not os.path.isfile(normalized_file_path):
            # Ignore references to files which don't exist on disk
            raise StackStormDBObjectNotFoundError('File "%s" not found' % (file_path))

        file_size, file_mtime = self._get_file_stats(file_path=normalized_file_path)

        response = Response()

        if not self._is_file_changed(file_mtime,
                                     if_none_match=if_none_match,
                                     if_modified_since=if_modified_since):
            response.status = http_client.NOT_MODIFIED
        else:
            if file_size is not None and file_size > MAX_FILE_SIZE:
                msg = ('File %s exceeds maximum allowed file size (%s bytes)' %
                       (file_path, MAX_FILE_SIZE))
                raise ValueError(msg)

            content_type = mimetypes.guess_type(normalized_file_path)[0] or \
                'application/octet-stream'

            response.headers['Content-Type'] = content_type
            response.body = self._get_file_content(file_path=normalized_file_path)

        response.headers['Last-Modified'] = format_date_time(file_mtime)
        response.headers['ETag'] = repr(file_mtime)

        return response
Ejemplo n.º 3
0
    def get(self,
            id,
            requester_user,
            download=False,
            compress=False,
            pretty_format=False):
        """
        Retrieve raw action execution result object as a JSON string or optionally force result
        download as a (compressed) file.

        This is primarily to be used in scenarios where executions contain large results and JSON
        loading and parsing it can be slow (e.g. in the st2web) and we just want to display raw
        result.

        :param compress: True to compress the response using gzip (may come handy for executions
                         with large results).
        :param download: True to force downloading result to a file.
        :param pretty_format: True to pretty format returned JSON data - this adds quite some
                              overhead compared to the default behavior where we don't pretty
                              format the result.

        Handles requests:

            GET /executions/<id>/result[?download=1][&compress=1]

        TODO: Maybe we should also support pre-signed URLs for sharing externally with other
        people?

        It of course won't contain all the exection related data, but just sharing the result can
        come handy in many situations.

        :rtype: ``str``
        """
        # NOTE: Here we intentionally use as_pymongo() to avoid mongoengine layer even for old style
        # data
        try:
            result = (self.access.impl.model.objects.filter(
                id=id).only("result").as_pymongo()[0])
        except IndexError:
            raise NotFoundException("Execution with id %s not found" % (id))

        if isinstance(result["result"], dict):
            # For backward compatibility we also support old non JSON field storage format
            if pretty_format:
                response_body = orjson.dumps(result["result"],
                                             option=orjson.OPT_INDENT_2)
            else:
                response_body = orjson.dumps(result["result"])
        else:
            # For new JSON storage format we just use raw value since it's already JSON serialized
            # string
            response_body = result["result"]

            if pretty_format:
                # Pretty format is not a default behavior since it adds quite some overhead (e.g.
                # 10-30ms for non pretty format for 4 MB json vs ~120 ms for pretty formatted)
                response_body = orjson.dumps(orjson.loads(result["result"]),
                                             option=orjson.OPT_INDENT_2)

        response = Response()
        response.headers["Content-Type"] = "text/json"

        if download:
            filename = "execution_%s_result.json" % (id)

            if compress:
                filename += ".gz"

            response.headers[
                "Content-Disposition"] = "attachment; filename=%s" % (filename)

        if compress:
            response.headers["Content-Type"] = "application/x-gzip"
            response.headers["Content-Encoding"] = "gzip"
            response_body = gzip.compress(response_body)

        response.body = response_body
        return response