diff --git a/opensearchpy/helpers/asyncsigner.py b/opensearchpy/helpers/asyncsigner.py index 52e0b092..930d0081 100644 --- a/opensearchpy/helpers/asyncsigner.py +++ b/opensearchpy/helpers/asyncsigner.py @@ -56,7 +56,7 @@ def _sign_request( from botocore.auth import SigV4Auth from botocore.awsrequest import AWSRequest - signature_host = self._fetch_url(url, headers or dict()) # type: ignore + signature_host = self._fetch_url(url, headers or dict()) # create an AWS request object and sign it using SigV4Auth aws_request = AWSRequest( @@ -86,25 +86,25 @@ def _sign_request( # copy the headers from AWS request object into the prepared_request return dict(aws_request.headers.items()) - def _fetch_url(self, url, headers): # type: ignore + def _fetch_url(self, url: str, headers: Optional[Dict[str, str]]) -> str: """ This is a util method that helps in reconstructing the request url. :param prepared_request: unsigned request :return: reconstructed url """ - url = urlparse(url) - path = url.path or "/" + parsed_url = urlparse(url) + path = parsed_url.path or "/" # fetch the query string if present in the request querystring = "" - if url.query: + if parsed_url.query: querystring = "?" + urlencode( - parse_qs(url.query, keep_blank_values=True), doseq=True + parse_qs(parsed_url.query, keep_blank_values=True), doseq=True ) # fetch the host information from headers - headers = {key.lower(): value for key, value in headers.items()} - location = headers.get("host") or url.netloc + headers = {key.lower(): value for key, value in (headers or dict()).items()} + location = headers.get("host") or parsed_url.netloc # construct the url and return - return url.scheme + "://" + location + path + querystring + return parsed_url.scheme + "://" + location + path + querystring diff --git a/opensearchpy/helpers/signer.py b/opensearchpy/helpers/signer.py index b435d276..0258a859 100644 --- a/opensearchpy/helpers/signer.py +++ b/opensearchpy/helpers/signer.py @@ -92,14 +92,14 @@ def _sign_request(self, prepared_request): # type: ignore prepared_request.headers.update( self.signer.sign( prepared_request.method, - self._fetch_url(prepared_request), # type: ignore + self._fetch_url(prepared_request), prepared_request.body, ) ) return prepared_request - def _fetch_url(self, prepared_request): # type: ignore + def _fetch_url(self, prepared_request: requests.PreparedRequest) -> str: """ This is a util method that helps in reconstructing the request url. :param prepared_request: unsigned request @@ -112,7 +112,7 @@ def _fetch_url(self, prepared_request): # type: ignore querystring = "" if url.query: querystring = "?" + urlencode( - parse_qs(url.query, keep_blank_values=True), doseq=True + parse_qs(url.query, keep_blank_values=True), doseq=True # type: ignore ) # fetch the host information from headers @@ -122,7 +122,7 @@ def _fetch_url(self, prepared_request): # type: ignore location = headers.get("host") or url.netloc # construct the url and return - return url.scheme + "://" + location + path + querystring + return url.scheme + "://" + location + path + querystring # type: ignore # Deprecated: use RequestsAWSV4SignerAuth diff --git a/test_opensearchpy/test_async/test_signer.py b/test_opensearchpy/test_async/test_signer.py index 7995797f..98109d7a 100644 --- a/test_opensearchpy/test_async/test_signer.py +++ b/test_opensearchpy/test_async/test_signer.py @@ -77,6 +77,20 @@ async def test_aws_signer_async_when_service_is_specified(self) -> None: assert "X-Amz-Security-Token" in headers assert "X-Amz-Content-SHA256" in headers + async def test_aws_signer_async_fetch_url_with_querystring(self) -> None: + region = "us-west-2" + service = "aoss" + + from opensearchpy.helpers.asyncsigner import AWSV4SignerAsyncAuth + + auth = AWSV4SignerAsyncAuth(self.mock_session(), region, service) + + signature_host = auth._fetch_url( + "http://localhost/?foo=bar", headers={"host": "otherhost"} + ) + + assert signature_host == "http://otherhost/?foo=bar" + class TestAsyncSignerWithFrozenCredentials(TestAsyncSigner): def mock_session(self, disable_get_frozen: bool = True) -> Mock: diff --git a/test_opensearchpy/test_connection/test_requests_http_connection.py b/test_opensearchpy/test_connection/test_requests_http_connection.py index 9e3014da..a1aee810 100644 --- a/test_opensearchpy/test_connection/test_requests_http_connection.py +++ b/test_opensearchpy/test_connection/test_requests_http_connection.py @@ -457,6 +457,23 @@ def mock_session(self) -> Any: return dummy_session + def test_aws_signer_fetch_url_with_querystring(self) -> None: + region = "us-west-2" + + import requests + + from opensearchpy.helpers.signer import RequestsAWSV4SignerAuth + + auth = RequestsAWSV4SignerAuth(self.mock_session(), region) + + prepared_request = requests.Request( + "GET", "http://localhost/?foo=bar", headers={"host": "otherhost:443"} + ).prepare() + + signature_host = auth._fetch_url(prepared_request) + + assert signature_host == "http://otherhost:443/?foo=bar" + def test_aws_signer_as_http_auth(self) -> None: region = "us-west-2"