Example #1
0
    def get_download_auth(self,
                          file_path_or_prefix: str,
                          auth_duration: int,
                          bucket_id: str = '') -> str:
        """
        Gets an authorization token for downloading specified files.

        :param file_path:
        """
        # duration = Min duration is 1 second, max is 604800 (one week).
        self._ensure_auth()
        data = {
            'bucketId': bucket_id if bucket_id else self.bucket_id,
            'fileNamePrefix': file_path_or_prefix,
            'validDurationInSeconds': auth_duration,
        }
        response = self._http.post(self._make_url(
            Endpoints.download_auth.value),
                                   json=data,
                                   headers=self._headers())
        data = json_loads(response.text)
        check_b2_errors(
            data,
            'Failed to get download auth for prefix "{}" on bucket "{}": {}.'.
            format(file_path_or_prefix, data['bucketId'],
                   data.get('message', '')))
        return data['authorizationToken']
Example #2
0
 def create_key(self,
                account_id: str,
                capabilities: List[KeyCapabilities],
                key_name: str,
                bucket_id: str = '',
                prefix: str = '',
                duration: int = 0) -> Json:
     self._ensure_auth()
     params = {
         'accountId': account_id,
         'capabilities': [item.value for item in capabilities],
         'keyName': key_name,
     }
     if duration:
         params['validDurationInSeconds'] = duration
     if bucket_id:
         params['bucketId'] = bucket_id
     if prefix:
         params['namePrefix'] = prefix
     response = self._http.post(self._make_url(Endpoints.create_key.value),
                                json=params,
                                headers=self._headers())
     data = json_loads(response.text)
     check_b2_errors(
         data,
         'Failed to create key "{}": {}.'.format(key_name,
                                                 data.get('message', '')))
     return data
Example #3
0
    def upload_part(self, data: bytes, upload_url: str, part_number: int,
                    auth_token: str) -> Json:
        """
        Uploads part of a large file.

        :param data: The partial file data being uploaded.
        :param upload_url: The URL used to upload the partial file data.
        :param part_number: The number of the part. Be aware that part numbers start at 1, not 0!
        :param auth_token: The authorization token returned by BackBlazeB2.get_upload_part_url().
        :returns: A json-like 6-dict containing the keys:  fileId, partNumber, contentLength,
                  contentSha1, contentMd5, uploadTimestamp.
        .. seealso:: `Reference <https://www.backblaze.com/b2/docs/b2_upload_part.html>`_.
        """
        self._ensure_auth()
        headers = {
            'Authorization': auth_token,
            'X-Bz-Part-Number': str(part_number),
            'Content-Length': str(len(data)),
            'X-Bz-Content-Sha1': sha1(data).hexdigest(),
        }
        response = self._http.post(upload_url,
                                   data=data,
                                   headers=headers,
                                   timeout=None)
        result = json_loads(response.text)
        check_b2_errors(
            result,
            f'Failed to upload file part number #{part_number}: {result}')
        return result
Example #4
0
 def start_large_file(self,
                      bucket_id: str,
                      file_name: str,
                      content_type: str = 'b2/x-auto',
                      file_info: Optional[Json] = None) -> Json:
     """
     :returns: A json-like 11-dict containing the keys: accountId, action, bucketId, contentType,
               contentLength, contentSha1, contentMd5, fileId, fileInfo, fileName,
               uploadTimestamp.
     .. seealso:: `Reference <https://www.backblaze.com/b2/docs/b2_start_large_file.html>`_.
     """
     self._ensure_auth()
     params = {
         'bucketId': bucket_id,
         'fileName': quote(file_name),
         'contentType': content_type,
         'fileInfo': file_info,
     }
     response = self._http.post(self._make_url(
         Endpoints.start_large_file.value),
                                json=params,
                                headers=self._headers())
     result = json_loads(response.text)
     check_b2_errors(result, f'Failed to start large file upload: {result}')
     return result
Example #5
0
    def list_buckets(self,
                     bucket_id: Optional[str] = None,
                     bucket_name: Optional[str] = None,
                     bucket_types: Optional[str] = None) -> Json:
        """
        Lists all buckets associated with the authenticated account. This method can be used to
        get a single bucket information by passing the 'bucket_id' or 'bucket_name' parameters.

        :param bucket_id: If specified, will fetch only the bucket with that ID. If the bucket is
                          not found, the server will return an empty list.
        :param bucket_name: If specified, will fetch only the bucket with that name. If the bucket
                            is not found, the server will return an empty list.
        :param bucket_types: Used to filter private or public buckets. Valid values are
                             ('allPublic', 'allPrivate', 'snapshot', 'all').
        :returns: A dict with the json-encoded response data.
        :raises RequestError: If the user is not authenticated.
        :raises ResponseError: If the server returned an error.
        """
        self._ensure_auth()
        params = self._base_params()
        params.update({
            'bucketId': bucket_id,
            'bucketName': bucket_name,
            'bucketTypes': bucket_types,
        })
        response = self._http.post(self._make_url(
            Endpoints.list_buckets.value),
                                   json=params,
                                   headers=self._headers())
        data = json_loads(response.text)
        check_b2_errors(data, f'Failed to list buckets ({data}).')
        return data
Example #6
0
 def test_check_b2_errors__invalid_response__raises_response_error(self):
     """ The check_b2_errors() raises a RequestError if an error is found. """
     data = {'status': 400}
     message = 'error raised'
     try:
         utils.check_b2_errors(data, message)
     except ResponseError as error:
         self.assertEqual(str(error), message)
Example #7
0
 def delete_key(self, key_id: str) -> Json:
     self._ensure_auth()
     response = self._http.post(self._make_url(Endpoints.delete_key.value),
                                json={'applicationKeyId': key_id},
                                headers=self._headers())
     data = json_loads(response.text)
     check_b2_errors(
         data,
         'Failed to delete key "{}": {}.'.format(key_id,
                                                 data.get('message', '')))
     return data
Example #8
0
    def create_bucket(self,
                      bucket_name: str,
                      private: bool,
                      bucket_info: Optional[Json] = None,
                      cors_rules: Optional[Json] = None,
                      lifecycle_rules: Optional[Json] = None) -> Json:
        """
        Creates a bucket to which you can upload files.
        Bucket creation is slow, thus this method can take a reasonably long time (30s+) to return.

        :param bucket_name: The name of the bucket to be created.
        :param private: True to set the bucket to private, False to leave it public. Private files
                        can be downloaded by others if an authorization token is provided.
        :param bucket_info: User-defined information to be stored with the bucket.
        :param cors_rules: The initial CORS rules for the bucket.
        :param lifecycle_rules: The initial lifecycle rules for the bucket.
        :returns: A json-like 9-dict containing the keys: accountId, bucketId, bucketName,
                  bucketType, bucketInfo, corsRules, lifecycleRules, revision, options.
        :raises RequestError: If the user is not authenticated.
        :raises RequestError: If the bucket name is not valid.
        .. seealso:: `Reference <https://www.backblaze.com/b2/docs/b2_create_bucket.html>`_.
        .. seealso:: `Buckets <https://www.backblaze.com/b2/docs/buckets.html>`_.
        .. seealso:: `CORS Rules <https://www.backblaze.com/b2/docs/cors_rules.html>`_.
        .. seealso:: `Lifecycle Rules <https://www.backblaze.com/b2/docs/lifecycle_rules.html>`_.
        """
        self._ensure_auth()
        if not valid_bucket_name(bucket_name):
            raise RequestError(f'Invalid bucket name "{bucket_name}".')
        params = self._base_params()
        params.update({
            'bucketName':
            bucket_name,
            'bucketType':
            self.BUCKET_TYPE_PRIVATE if private else self.BUCKET_TYPE_PUBLIC,
            'bucketInfo':
            bucket_info,
            'corsRules':
            cors_rules,
            'lifecycleRules':
            lifecycle_rules,
        })
        # Endpoint /b2_create_bucket can take a long time to respond, a larger timeout is required
        response = self._http.post(self._make_url(
            Endpoints.create_bucket.value),
                                   json=params,
                                   headers=self._headers(),
                                   timeout=90.0)
        data = json_loads(response.text)
        check_b2_errors(data,
                        f'Failed to create bucket "{bucket_name}" ({data}).')
        return data
Example #9
0
    def list_files(self,
                   prefix: Optional[str] = None,
                   delimiter: Optional[str] = None,
                   max_files: int = 0,
                   start_name: str = '',
                   bucket_id: Optional[str] = None) -> Json:
        """
        Lists the files contained on a bucket and show their information.
        The file information shown for each file is the same as returned by "get_file_info()".
        See `b2_list_file_names <https://www.backblaze.com/b2/docs/b2_list_file_names.html>`_.

        :param prefix: Returns only files which names start with the specified prefix.
        :param delimiter: Used to list only files in a directory.
        :param max_files: Number of files to return in the response. Maximum is 10000, default 100.
        :param start_name: If a file matches this path, it will be the first file returned.
        :param bucket_id: The id of the bucket to have its files listed. If empty, will try to use
                          the bucket set with BackBlazeB2.set_bucket().
        :returns: A dict with the json-encoded response data.
        :raises RequestError: If the user is not authenticated.

        :example:

        >>> # List all files which name starts with 'foo'
        >>> BackBlazeB2().list_files(prefix='foo')
        >>> # List all files with the in the 'snafu' virtual folder
        >>> BackBlazeB2().list_files(prefix='snafu/', delimiter='/')

        """
        self._ensure_auth()
        if not prefix or self.limited_account:
            prefix = self.prefix()
        params = {
            'bucketId': bucket_id if bucket_id else self.bucket_id,
            'prefix': prefix,
            'delimiter':
            delimiter if delimiter is not None else self.delimiter,
            'maxFileCount': max_files,
            'startFileName': start_name,
        }
        response = self._http.post(self._make_url(Endpoints.list_files.value),
                                   json=params,
                                   headers=self._headers())
        data = json_loads(response.text)
        check_b2_errors(
            data,
            'Failed to get list files <prefix={}, delimiter={}, start_name={}, max_files={}, '
            'bucket_id={}> ({}).'.format(prefix, delimiter, start_name,
                                         max_files, bucket_id,
                                         data.get('message', '')))
        return data
Example #10
0
 def get_upload_part_url(self, file_id: str) -> Json:
     """
     :returns: A json-like 3-dict containing the keys: fileId, uploadUrl, authorizationToken.
     .. seealso:: `Reference <https://www.backblaze.com/b2/docs/b2_get_upload_part_url.html>`_.
     """
     self._ensure_auth()
     response = self._http.post(self._make_url(
         Endpoints.get_upload_part_url.value),
                                json={'fileId': file_id},
                                headers=self._headers())
     result = json_loads(response.text)
     check_b2_errors(
         result,
         f'Failed to get upload part url for file "{file_id}": {result}')
     return result
Example #11
0
 def list_keys(self,
               account_id: str,
               max_key_count: int = 0,
               start_app_key_id: str = ''):
     self._ensure_auth()
     params = {'accountId': account_id}
     if max_key_count:
         params['maxKeyCount'] = max_key_count
     if start_app_key_id:
         params['startApplicationKeyId'] = start_app_key_id
     response = self._http.post(self._make_url(Endpoints.list_keys.value),
                                json=params,
                                headers=self._headers())
     data = json_loads(response.text)
     check_b2_errors(
         data, 'Failed to list keys: {}.'.format(data.get('message', '')))
     return data
Example #12
0
    def cancel_large_file(self, file_id: str) -> Json:
        """
        Cancels a large file upload and deletes the parts that were already uploaded.

        :param file_id: The file id returned by BackBlazeB2.start_large_file().
        :returns: A json-like 4-dict containing the keys: fileId, accountId, bucketId, fileName.
        .. seealso:: `Reference <https://www.backblaze.com/b2/docs/b2_cancel_large_file.html>`_.
        """
        self._ensure_auth()
        response = self._http.post(self._make_url(
            Endpoints.cancel_large_file.value),
                                   json={'fileId': file_id},
                                   headers=self._headers())
        result = json_loads(response.text)
        check_b2_errors(
            result,
            f'Failed to cancel large file with id "{file_id}": {result}')
        return result
Example #13
0
    def authenticate(self,
                     account_id: Optional[str] = None,
                     app_key: Optional[str] = None) -> Json:
        """
        Authenticates (or re-authenticates, if already authenticated) an account.
        If the "account_id" and "app_key" attributes were already set in the constructor, it's not
        necessary to specify them again. Specifying them will re-authenticate the account.

        IMPORTANT: When authenticating with a non-master application key, use the key id as the
        account id, otherwise the response will produce a 401 Unauthorized error.

        :param account_id: The account id to be used for authentication, or the key id if
                           authenticating with a non-master application key.
        :param app_key: The secret value of the application key to be used for authentication.
        :returns: A dict with the json-encoded response data.
        :raises BlazeError: If failed to make the request to the server.
        :raises ResponseError: If the server returned an error.
        """
        self.account_id = account_id or self.account_id
        self.app_key = app_key or self.app_key
        response = self._http.get(self.BASE_URL + Endpoints.auth.value,
                                  auth=HTTPBasicAuth(self.account_id,
                                                     self.app_key))
        data = json_loads(response.text)
        check_b2_errors(
            data,
            'Failed to authenticate with BackBlaze (account_id={}, app_key={}) ({})'
            .format(self.account_id, self.app_key, data))
        self.api_url = data['apiUrl']
        self.auth_token = data['authorizationToken']
        self.download_url = data['downloadUrl']
        allowed = data.get('allowed', {})
        self._capabilities = allowed.get('capabilities', [])
        allowed_bucket_id = allowed.get('bucketId', '')
        allowed_bucket_name = allowed.get('bucketName', '')
        if allowed_bucket_id:
            self._bucket_id = allowed_bucket_id
            self._limited_account = True
        if allowed_bucket_name:
            self._bucket_name = allowed_bucket_name
            self._limited_account = True
        if self.limited_account:
            self.set_prefix(allowed.get('namePrefix', ''))
        return data
Example #14
0
    def delete_file(self, file_id: str, file_path: str):
        """
        Deletes a file from the bucket.
        Both the ID and the full path of the file on the server must be provided to delete the file.

        :param file_id: The id of the file in the backblaze service.
        :param file_path: The absolute path to the file in the backblaze service.
        """
        self._ensure_auth()
        data = {'fileName': file_path, 'fileId': file_id}
        response = self._http.post(self._make_url(Endpoints.delete_file.value),
                                   json=data,
                                   headers=self._headers())
        data = json_loads(response.text)
        check_b2_errors(
            data,
            'Failed to delete file with id "{}" and path "{}" ({}).'.format(
                file_id, file_path, data.get('message', '')))
        return data
Example #15
0
 def upload_file(self,
                 data: bytes,
                 upload_url: str,
                 auth_token: str,
                 file_name: str,
                 content_type: str = '',
                 last_modified_ms: Optional[int] = None,
                 content_disposition: Optional[str] = None,
                 language: Optional[str] = None,
                 expires: Optional[str] = None,
                 cache_control: Optional[str] = None,
                 encoding: Optional[str] = None,
                 content_type_header: Optional[str] = None,
                 info: Optional[Dict[str, str]] = None) -> Json:
     self._ensure_auth()
     headers = {
         'Authorization': auth_token,
         'X-Bz-File-Name': quote(file_name),
         'Content-Type': content_type if content_type else 'b2/x-auto',
         'Content-Length': str(len(data)),
         'X-Bz-Content-Sha1': sha1(data).hexdigest(),
         'X-Bz-Info-src_last_modified_millis': last_modified_ms,
         'X-Bz-Info-b2-content-disposition': content_disposition,
         'X-Bz-Info-b2-content-language': language,
         'X-Bz-Info-b2-expires': expires,
         'X-Bz-Info-b2-cache-control': cache_control,
         'X-Bz-Info-b2-content-encoding': encoding,
         'X-Bz-Info-b2-content-type': content_type_header,
     }
     if info:
         headers.update({
             f'X-Bz-Info-{key}': quote(value)
             for key, value in info.items()
         })
     response = self._http.post(upload_url,
                                data=data,
                                headers=headers,
                                timeout=None)
     result = json_loads(response.text)
     check_b2_errors(result,
                     f'Failed to upload file "{file_name}": {result}')
     return result
Example #16
0
    def get_upload_url(self, bucket_id: str) -> Json:
        """
        Gets the URL to upload to a bucket.

        :param bucket_id: The ID of the bucket to which files will be uploaded to.
        :returns: A json-like 3-dict containing the keys: bucketId, uploadUrl, authorizationToken.
        :raises ResponseError: If failed to obtain the upload information.
        """
        self._ensure_auth()
        response = self._http.post(self._make_url(
            Endpoints.get_upload_url.value),
                                   json={'bucketId': bucket_id},
                                   headers=self._headers())
        result = json_loads(response.text)
        check_b2_errors(result,
                        f'Failed to get uploading authorization: {result}.')
        self.upload_url = result['uploadUrl']
        self.upload_token = result['authorizationToken']
        self._bucket_id = bucket_id
        return result
Example #17
0
    def get_file_info(self, file_id: str) -> Json:
        """
        Gets the information for a file on the server.
        The content of this response is the same as the one in each file listed by "list_files()".

        :param file_id: The ID given to the file when it was uploaded.
        :returns: A dict with the json-encoded response data.
        :raises RequestError: If the user is not authenticated.
        :raises ResponseError: If the server returned an error (e.g.: file does not exist).
        """
        self._ensure_auth()
        response = self._http.post(self._make_url(Endpoints.file_info.value),
                                   json={'fileId': quote(file_id)},
                                   headers=self._headers())

        data = json_loads(response.text)
        check_b2_errors(
            data, 'Failed to get files info  <file_id={}> ({}).'.format(
                file_id, data.get('message', '')))
        return data
Example #18
0
    def delete_bucket(self, bucket_id: str) -> Json:
        """
        Deletes a bucket.

        :param bucket_id: The id of the bucket to be deleted.
        :returns: A dict with the json-encoded response data.
        :raises RequestError: If the user is not authenticated.
        """
        self._ensure_auth()
        params = self._base_params()
        params.update({'bucketId': bucket_id})
        # Endpoint /b2_delete_bucket takes a long time to respond, so a larger timeout is warranted
        response = self._http.post(self._make_url(
            Endpoints.delete_bucket.value),
                                   json=params,
                                   headers=self._headers(),
                                   timeout=90.0)
        data = json_loads(response.text)
        check_b2_errors(
            data, f'Failed to delete bucket with id "{bucket_id}" ({data}).')
        return data
Example #19
0
 def finish_large_file(self, file_id: str, parts_sha1: List[str]) -> Json:
     """
     :param file_id: The ID returned by b2_start_large_file.
     :param parts_sha1: A list of hex SHA1 checksums of the parts of the large file.
     :returns: A json-like 11-dict containing the keys: accountId, action, bucketId, fileName,
               fileId, contentLength, contentSha1, contentMd5, contentType, fileInfo,
               uploadTimestamp.
     .. seealso:: `Reference <https://www.backblaze.com/b2/docs/b2_finish_large_file.html>`_.
     """
     self._ensure_auth()
     params = {
         'fileId': file_id,
         'partSha1Array': parts_sha1,
     }
     response = self._http.post(self._make_url(
         Endpoints.finish_large_file.value),
                                json=params,
                                headers=self._headers())
     result = json_loads(response.text)
     check_b2_errors(
         result,
         f'Failed to finish large file with id "{file_id}": {result}')
     return result
Example #20
0
 def test_check_b2_errors__valid_response__nothing_happens(self):
     """ The check_b2_errors() does nothing if no error is found. """
     data = {'status': 200}
     self.assertIsNone(utils.check_b2_errors(data, ''))