diff --git a/HISTORY.md b/HISTORY.md index 6f43951c..aa1e3eb6 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [4.1.2](https://github.com/uploadcare/pyuploadcare/compare/v4.1.1...v4.1.2) - 2023-09-08 + +### Fixed + +- Incorrect expiration time was calculated for signed uploads causing the `AuthenticationError: Expired signature` exception. + ## [4.1.1](https://github.com/uploadcare/pyuploadcare/compare/v4.1.0...v4.1.1) - 2023-09-04 ### Fixed diff --git a/pyproject.toml b/pyproject.toml index efa8cf44..5afe6047 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pyuploadcare" -version = "4.1.1" +version = "4.1.2" description = "Python library for Uploadcare.com" authors = ["Uploadcare Inc "] readme = "README.md" diff --git a/pyuploadcare/__init__.py b/pyuploadcare/__init__.py index 1936c695..c269665d 100644 --- a/pyuploadcare/__init__.py +++ b/pyuploadcare/__init__.py @@ -1,5 +1,5 @@ # isort: skip_file -__version__ = "4.1.1" +__version__ = "4.1.2" from pyuploadcare.resources.file import File # noqa: F401 from pyuploadcare.resources.file_group import FileGroup # noqa: F401 diff --git a/pyuploadcare/client.py b/pyuploadcare/client.py index c9d4be93..84cb6d8e 100644 --- a/pyuploadcare/client.py +++ b/pyuploadcare/client.py @@ -1,5 +1,6 @@ import os import socket +from time import time from typing import ( IO, Any, @@ -373,7 +374,7 @@ def _file_name(file_object, index): files=files, store=self._format_store(store), secure_upload=self.signed_uploads, - expire=self.signed_uploads_ttl, + expire=int(time()) + self.signed_uploads_ttl, common_metadata=common_metadata, ) ucare_files = [self.file(response[file_name]) for file_name in files] @@ -425,7 +426,7 @@ def multipart_upload( # noqa: C901 content_type=mime_type, store=self._format_store(store), secure_upload=self.signed_uploads, - expire=self.signed_uploads_ttl, + expire=int(time()) + self.signed_uploads_ttl, metadata=metadata, ) @@ -495,7 +496,7 @@ def upload_from_url( filename=filename, metadata=metadata, secure_upload=self.signed_uploads, - expire=self.signed_uploads_ttl, + expire=int(time()) + self.signed_uploads_ttl, check_duplicates=check_duplicates, save_duplicates=save_duplicates, ) @@ -647,7 +648,7 @@ def create_file_group(self, files: List[File]) -> FileGroup: group_info = self.upload_api.create_group( files=file_urls, secure_upload=self.signed_uploads, - expire=self.signed_uploads_ttl, + expire=int(time()) + self.signed_uploads_ttl, ) group = self.file_group(group_info["id"], group_info) diff --git a/tests/functional/resources/test_resources.py b/tests/functional/resources/test_resources.py index cd2e64be..e6f947d4 100644 --- a/tests/functional/resources/test_resources.py +++ b/tests/functional/resources/test_resources.py @@ -1,4 +1,6 @@ import os +from datetime import datetime, timedelta +from unittest.mock import MagicMock, patch import pytest @@ -134,6 +136,31 @@ def test_file_upload_multiple(small_file, small_file2, uploadcare): ) +@pytest.mark.freeze_time("2021-10-12") +def test_file_upload_signature(small_file, uploadcare, signed_uploads): + assert uploadcare.signed_uploads + fake_response = MagicMock() + fake_response.json.return_value = { + "sample1.txt": "96e0a8f8-91f3-4162-907d-2d67d36ebae8" + } + with open(small_file.name, "rb") as fh: + with patch.object( + uploadcare.upload_api._client, "post", return_value=fake_response + ) as mocked_post: + response = uploadcare.upload(fh) + + assert mocked_post.called + data = mocked_post.call_args.kwargs["data"] + expected_expire = datetime.now() + timedelta( + seconds=uploadcare.signed_uploads_ttl + ) + assert data["expire"] == str(int(expected_expire.timestamp())) + assert ( + data["signature"] + == "f7d27174ad85736b9a4ace81a3cf420b99b2ddc78415b00adfc0c326a39cb490" + ) + + @pytest.mark.vcr def test_file_create_local_copy(uploadcare): file = uploadcare.file("35ea470d-216c-4752-91d2-5176b34c1225")