diff --git a/packages/aws-sdk-signers/MANIFEST.in b/packages/aws-sdk-signers/MANIFEST.in new file mode 100644 index 000000000..f365756d9 --- /dev/null +++ b/packages/aws-sdk-signers/MANIFEST.in @@ -0,0 +1 @@ +include aws_sdk_signers/py.typed diff --git a/packages/aws-sdk-signers/NOTICE b/packages/aws-sdk-signers/NOTICE new file mode 100644 index 000000000..616fc5889 --- /dev/null +++ b/packages/aws-sdk-signers/NOTICE @@ -0,0 +1 @@ +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/aws-sdk-signers/README.md b/packages/aws-sdk-signers/README.md new file mode 100644 index 000000000..e69de29bb diff --git a/packages/aws-sdk-signers/pyproject.toml b/packages/aws-sdk-signers/pyproject.toml new file mode 100644 index 000000000..4af4c5f4e --- /dev/null +++ b/packages/aws-sdk-signers/pyproject.toml @@ -0,0 +1,55 @@ +[build-system] +requires = ["setuptools", "setuptools-scm", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "aws_sdk_signers" +requires-python = ">=3.12" +authors = [ + {name = "Amazon Web Services"}, +] +description = "Standalone HTTP Request Signers for Amazon Web Services" +readme = "README.md" +license = {text = "Apache License 2.0"} +keywords = ["aws", "sdk", "amazon", "signing", "sigv4", "http"] +classifiers = [ + "Development Status :: 2 - Pre-Alpha", + "Intended Audience :: Developers", + "Intended Audience :: System Administrators", + "Natural Language :: English", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: Implementation :: CPython", + "Topic :: Software Development :: Libraries" +] +dynamic = ["version"] + +[tool.setuptools.dynamic] +version = {attr = "aws_sdk_signers._version.__version__"} + +[tool.setuptools] +include-package-data = false + +[project.optional-dependencies] +test = [ + "freezegun", + "pytest", + "pytest-asyncio", + "mypy", + "ruff", +] + +[tool.mypy] +python_version = "3.12" +strict = true + +[tool.pytest.ini_options] +asyncio_mode = "auto" +addopts = "-W error" + +[tool.ruff] +src = ["src"] diff --git a/packages/aws-sdk-signers/src/aws_sdk_signers/__init__.py b/packages/aws-sdk-signers/src/aws_sdk_signers/__init__.py new file mode 100644 index 000000000..0fc1cc81c --- /dev/null +++ b/packages/aws-sdk-signers/src/aws_sdk_signers/__init__.py @@ -0,0 +1,27 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +"""AWS SDK Signers provides stand-alone signing functionality for use with HTTP tools +such as AioHTTP, Curl, Postman, Requests, urllib3, etc.""" + +from __future__ import annotations + +from ._http import URI, AWSRequest, Field, Fields +from ._identity import AWSCredentialIdentity +from ._io import AsyncBytesReader +from ._version import __version__ +from .signers import AsyncSigV4Signer, SigV4Signer, SigV4SigningProperties + +__license__ = "Apache-2.0" +__version__ = __version__ + +__all__ = ( + "AsyncBytesReader", + "AsyncSigV4Signer", + "AWSCredentialIdentity", + "AWSRequest", + "Field", + "Fields", + "SigV4Signer", + "SigV4SigningProperties", + "URI", +) diff --git a/packages/aws-sdk-signers/src/aws_sdk_signers/_http.py b/packages/aws-sdk-signers/src/aws_sdk_signers/_http.py new file mode 100644 index 000000000..5c304213a --- /dev/null +++ b/packages/aws-sdk-signers/src/aws_sdk_signers/_http.py @@ -0,0 +1,378 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 +""" +NOTE TO THE READER: + +This file is _strictly_ temporary and subject to abrupt breaking changes +including unannounced removal. For typing information, please rely on the +__all__ attributes provided in the types/__init__.py file. +""" + +from __future__ import annotations + +from collections import Counter, OrderedDict +from collections.abc import AsyncIterable, Iterable, Iterator +from copy import deepcopy +from dataclasses import dataclass +from functools import cached_property +from typing import TypedDict +from urllib.parse import urlunparse + +import aws_sdk_signers.interfaces.http as interfaces_http + + +class Field(interfaces_http.Field): + """A name-value pair representing a single field in an HTTP Request or Response. + + The kind will dictate metadata placement within an HTTP message. + + All field names are case insensitive and case-variance must be treated as + equivalent. Names may be normalized but should be preserved for accuracy during + transmission. + """ + + def __init__( + self, + *, + name: str, + values: Iterable[str] | None = None, + kind: interfaces_http.FieldPosition = interfaces_http.FieldPosition.HEADER, + ): + self.name = name + self.values: list[str] = [val for val in values] if values is not None else [] + self.kind = kind + + def add(self, value: str) -> None: + """Append a value to a field.""" + self.values.append(value) + + def set(self, values: list[str]) -> None: + """Overwrite existing field values.""" + self.values = values + + def remove(self, value: str) -> None: + """Remove all matching entries from list.""" + try: + while True: + self.values.remove(value) + except ValueError: + return + + def as_string(self, delimiter: str = ",") -> str: + """Get delimited string of all values. A comma followed by a space is used by + default. + + If the ``Field`` has zero values, the empty string is returned. If the ``Field`` + has exactly one value, the value is returned unmodified. + + For ``Field``s with more than one value, the values are joined by a comma and a + space. For such multi-valued ``Field``s, any values that already contain + commas or double quotes will be surrounded by double quotes. Within any values + that get quoted, pre-existing double quotes and backslashes are escaped with a + backslash. + """ + value_count = len(self.values) + if value_count == 0: + return "" + if value_count == 1: + return self.values[0] + return delimiter.join(quote_and_escape_field_value(val) for val in self.values) + + def as_tuples(self) -> list[tuple[str, str]]: + """Get list of ``name``, ``value`` tuples where each tuple represents one + value.""" + return [(self.name, val) for val in self.values] + + def __eq__(self, other: object) -> bool: + """Name, values, and kind must match. + + Values order must match. + """ + if not isinstance(other, Field): + return False + return ( + self.name == other.name + and self.kind is other.kind + and self.values == other.values + ) + + def __repr__(self) -> str: + return f"Field(name={self.name!r}, value={self.values!r}, kind={self.kind!r})" + + +class Fields(interfaces_http.Fields): + def __init__( + self, + initial: Iterable[interfaces_http.Field] | None = None, + *, + encoding: str = "utf-8", + ): + """Collection of header and trailer entries mapped by name. + + :param initial: Initial list of ``Field`` objects. ``Field``s can also be added + and later removed. + :param encoding: The string encoding to be used when converting the ``Field`` + name and value from ``str`` to ``bytes`` for transmission. + """ + init_fields = [fld for fld in initial] if initial is not None else [] + init_field_names = [self._normalize_field_name(fld.name) for fld in init_fields] + fname_counter = Counter(init_field_names) + repeated_names_exist = ( + len(init_fields) > 0 and fname_counter.most_common(1)[0][1] > 1 + ) + if repeated_names_exist: + non_unique_names = [name for name, num in fname_counter.items() if num > 1] + raise ValueError( + "Field names of the initial list of fields must be unique. The " + "following normalized field names appear more than once: " + f"{', '.join(non_unique_names)}." + ) + init_tuples = zip(init_field_names, init_fields) + self.entries: OrderedDict[str, interfaces_http.Field] = OrderedDict(init_tuples) + self.encoding: str = encoding + + def set_field(self, field: interfaces_http.Field) -> None: + """Alias for __setitem__ to utilize the field.name for the entry key.""" + self.__setitem__(field.name, field) + + def __setitem__(self, name: str, field: interfaces_http.Field) -> None: + """Set or override entry for a Field name.""" + normalized_name = self._normalize_field_name(name) + normalized_field_name = self._normalize_field_name(field.name) + if normalized_name != normalized_field_name: + raise ValueError( + f"Supplied key {name} does not match Field.name " + f"provided: {normalized_field_name}" + ) + self.entries[normalized_name] = field + + def get( + self, key: str, default: interfaces_http.Field | None = None + ) -> interfaces_http.Field | None: + return self[key] if key in self else default + + def __getitem__(self, name: str) -> interfaces_http.Field: + """Retrieve Field entry.""" + normalized_name = self._normalize_field_name(name) + return self.entries[normalized_name] + + def __delitem__(self, name: str) -> None: + """Delete entry from collection.""" + normalized_name = self._normalize_field_name(name) + del self.entries[normalized_name] + + def get_by_type( + self, kind: interfaces_http.FieldPosition + ) -> list[interfaces_http.Field]: + """Helper function for retrieving specific types of fields. + + Used to grab all headers or all trailers. + """ + return [entry for entry in self.entries.values() if entry.kind is kind] + + def extend(self, other: interfaces_http.Fields) -> None: + """Merges ``entries`` of ``other`` into the current ``entries``. + + For every `Field` in the ``entries`` of ``other``: If the normalized name + already exists in the current ``entries``, the values from ``other`` are + appended. Otherwise, the ``Field`` is added to the list of ``entries``. + """ + for other_field in other: + try: + cur_field = self.__getitem__(other_field.name) + for other_value in other_field.values: + cur_field.add(other_value) + except KeyError: + self.__setitem__(other_field.name, other_field) + + def _normalize_field_name(self, name: str) -> str: + """Normalize field names. + + For use as key in ``entries``. + """ + return name.lower() + + def __eq__(self, other: object) -> bool: + """Encoding must match. + + Entries must match in values and order. + """ + if not isinstance(other, Fields): + return False + return self.encoding == other.encoding and self.entries == other.entries + + def __iter__(self) -> Iterator[interfaces_http.Field]: + yield from self.entries.values() + + def __len__(self) -> int: + return len(self.entries) + + def __repr__(self) -> str: + return f"Fields({self.entries})" + + def __contains__(self, key: str) -> bool: + return self._normalize_field_name(key) in self.entries + + +@dataclass(kw_only=True, frozen=True) +class URI(interfaces_http.URI): + """Universal Resource Identifier, target location for a :py:class:`HTTPRequest`.""" + + scheme: str = "https" + """For example ``http`` or ``https``.""" + + username: str | None = None + """Username part of the userinfo URI component.""" + + password: str | None = None + """Password part of the userinfo URI component.""" + + host: str + """The hostname, for example ``amazonaws.com``.""" + + port: int | None = None + """An explicit port number.""" + + path: str | None = None + """Path component of the URI.""" + + query: str | None = None + """Query component of the URI as string.""" + + fragment: str | None = None + """Part of the URI specification, but may not be transmitted by a client.""" + + @property + def netloc(self) -> str: + """Construct netloc string in format ``{username}:{password}@{host}:{port}`` + + ``username``, ``password``, and ``port`` are only included if set. ``password`` + is ignored, unless ``username`` is also set. + """ + return self._netloc + + # cached_property does NOT behave like property, it actually allows for setting. + # Therefore we need a layer of indirection. + @cached_property + def _netloc(self) -> str: + if self.username is not None: + password = "" if self.password is None else f":{self.password}" + userinfo = f"{self.username}{password}@" + else: + userinfo = "" + + if self.port is not None: + port = f":{self.port}" + else: + port = "" + + host = self.host + + return f"{userinfo}{host}{port}" + + def build(self) -> str: + """Construct URI string representation. + + Validate host. Returns a string of the form + ``{scheme}://{username}:{password}@{host}:{port}{path}?{query}#{fragment}`` + """ + components = ( + self.scheme, + self.netloc, + self.path or "", + "", # params + self.query, + self.fragment, + ) + return urlunparse(components) + + def to_dict(self) -> URIParameters: + return { + "scheme": self.scheme, + "host": self.host, + "port": self.port, + "path": self.path, + "query": self.query, + "username": self.username, + "password": self.password, + "fragment": self.fragment, + } + + def __eq__(self, other: object) -> bool: + if not isinstance(other, URI): + return False + return ( + self.scheme == other.scheme + and self.host == other.host + and self.port == other.port + and self.path == other.path + and self.query == other.query + and self.username == other.username + and self.password == other.password + and self.fragment == other.fragment + ) + + +class URIParameters(TypedDict): + """TypedDict representing the parameters for the URI class. + + These need to be kept in sync for the `to_dict` method. + """ + + # TODO: Unpack doesn't seem to do what we want, so we need a way to represent + # returning a class' parameters as a dict. There must be a better way to do this. + + scheme: str + username: str | None + password: str | None + host: str + port: int | None + path: str | None + query: str | None + fragment: str | None + + +class AWSRequest(interfaces_http.Request): + def __init__( + self, + *, + destination: URI, + method: str, + body: AsyncIterable[bytes] | Iterable[bytes] | None, + fields: Fields, + ): + self.destination = destination + self.method = method + self.body = body + self.fields = fields + + def __deepcopy__(self, memo: dict[int, AWSRequest] | None = None) -> AWSRequest: + if memo is None: + memo = {} + + if id(self) in memo: + return memo[id(self)] + + # the destination doesn't need to be copied because it's immutable + # the body can't be copied because it's an iterator + new_instance = self.__class__( + destination=self.destination, # pyright: ignore [reportArgumentType] + body=self.body, + method=self.method, + fields=deepcopy(self.fields, memo), + ) + memo[id(new_instance)] = new_instance + return new_instance + + +def quote_and_escape_field_value(value: str) -> str: + """Escapes and quotes a single :class:`Field` value if necessary. + + See :func:`Field.as_string` for quoting and escaping logic. + """ + chars_to_quote = (",", '"') + if any(char in chars_to_quote for char in value): + escaped = value.replace("\\", "\\\\").replace('"', '\\"') + return f'"{escaped}"' + else: + return value diff --git a/packages/aws-sdk-signers/src/aws_sdk_signers/_identity.py b/packages/aws-sdk-signers/src/aws_sdk_signers/_identity.py new file mode 100644 index 000000000..4e32ea52c --- /dev/null +++ b/packages/aws-sdk-signers/src/aws_sdk_signers/_identity.py @@ -0,0 +1,22 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +from dataclasses import dataclass +from datetime import UTC, datetime + +from .interfaces.identity import Identity + + +@dataclass(kw_only=True) +class AWSCredentialIdentity(Identity): + access_key_id: str + secret_access_key: str + session_token: str | None = None + expiration: datetime | None = None + + @property + def is_expired(self) -> bool: + """Whether the identity is expired.""" + if self.expiration is None: + return False + return self.expiration < datetime.now(UTC) diff --git a/packages/aws-sdk-signers/src/aws_sdk_signers/_io.py b/packages/aws-sdk-signers/src/aws_sdk_signers/_io.py new file mode 100644 index 000000000..ba50f3c4d --- /dev/null +++ b/packages/aws-sdk-signers/src/aws_sdk_signers/_io.py @@ -0,0 +1,283 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +from asyncio import iscoroutinefunction +from collections.abc import AsyncIterable, AsyncIterator, Awaitable, Callable +from io import BytesIO +from typing import ( + Self, + cast, +) + +from aws_sdk_signers.interfaces.io import AsyncByteStream, ByteStream + +# The default chunk size for iterating streams. +_DEFAULT_CHUNK_SIZE = 1024 + + +StreamingBlob = ByteStream | AsyncByteStream | bytes | bytearray | AsyncIterable[bytes] + + +class AsyncBytesReader: + """A file-like object with an async read method.""" + + # BytesIO *is* a ByteStream, but mypy complains if it isn't here. + _data: ByteStream | AsyncByteStream | AsyncIterable[bytes] | BytesIO | None + _closed = False + + def __init__(self, data: StreamingBlob): + """Initializes self. + + Data is read from the source on an as-needed basis and is not buffered. + + :param data: The source data to read from. + """ + self._remainder = b"" + # pylint: disable-next=isinstance-second-argument-not-valid-type + if isinstance(data, bytes | bytearray): + self._data = BytesIO(data) + else: + self._data = data + + async def read(self, size: int | None = -1) -> bytes: + """Read a number of bytes from the stream. + + :param size: The maximum number of bytes to read. If less than 0, all bytes will + be read. + """ + if self._closed or not self._data: + raise ValueError("I/O operation on closed file.") + + if size is None: + size = -1 + + if isinstance(self._data, ByteStream) and not iscoroutinefunction( # type: ignore - TODO(pyright) + self._data.read + ): + # Python's runtime_checkable can't actually tell the difference between + # sync and async, so we have to check ourselves. + return self._data.read(size) + + if isinstance(self._data, AsyncByteStream): # type: ignore - TODO(pyright) + return await self._data.read(size) + + return await self._read_from_iterable( + cast(AsyncIterable[bytes], self._data), size + ) + + async def _read_from_iterable( + self, iterator: AsyncIterable[bytes], size: int + ) -> bytes: + # This takes the iterator as an arg here just to avoid mypy complaints, since + # we know it's an iterator where this is called. + result = self._remainder + if size < 0: + async for element in iterator: + result += element + self._remainder = b"" + return result + + async for element in iterator: + result += element + if len(result) >= size: + break + + self._remainder = result[size:] + return result[:size] + + def __aiter__(self) -> AsyncIterator[bytes]: + return self.iter_chunks() + + def iter_chunks( + self, chunk_size: int = _DEFAULT_CHUNK_SIZE + ) -> AsyncIterator[bytes]: + """Iterate over the reader in chunks of a given size. + + :param chunk_size: The maximum size of each chunk. If less than 0, the entire + reader will be read into one chunk. + """ + return _AsyncByteStreamIterator(self.read, chunk_size) + + def readable(self) -> bool: + """Returns whether the stream is readable.""" + return True + + def writeable(self) -> bool: + """Returns whether the stream is writeable.""" + return False + + def seekable(self) -> bool: + """Returns whether the stream is seekable.""" + return False + + @property + def closed(self) -> bool: + """Returns whether the stream is closed.""" + return self._closed + + def close(self) -> None: + """Closes the stream, as well as the underlying stream where possible.""" + if (close := getattr(self._data, "close", None)) is not None: + close() + self._data = None + self._closed = True + + +class SeekableAsyncBytesReader: + """A file-like object with async read and seek methods.""" + + def __init__(self, data: StreamingBlob): + """Initializes self. + + Data is read from the source on an as-needed basis and buffered internally so + that it can be rewound safely. + + :param data: The source data to read from. + """ + # pylint: disable-next=isinstance-second-argument-not-valid-type + if isinstance(data, bytes | bytearray): + self._buffer = BytesIO(data) + self._data_source = None + elif isinstance(data, AsyncByteStream) and iscoroutinefunction(data.read): # type: ignore - TODO(pyright) + # Note that we need that iscoroutine check because python won't actually check + # whether or not the read function is async. + self._buffer = BytesIO() + self._data_source = data + else: + self._buffer = BytesIO() + self._data_source = AsyncBytesReader(data) + + async def read(self, size: int | None = -1) -> bytes: + """Read a number of bytes from the stream. + + :param size: The maximum number of bytes to read. If less than 0, all bytes will + be read. + """ + if size is None: + size = -1 + + if self._data_source is None or size == 0: + return self._buffer.read(size) + + start = self._buffer.tell() + current_buffer_size = self._buffer.seek(0, 2) + + if size < 0: + await self._read_into_buffer(size) + elif (target := start + size) > current_buffer_size: + amount_to_read = target - current_buffer_size + await self._read_into_buffer(amount_to_read) + + self._buffer.seek(start, 0) + return self._buffer.read(size) + + async def seek(self, offset: int, whence: int = 0) -> int: + """Moves the cursor to a position relative to the position indicated by whence. + + Whence can have one of three values: + + * 0 => The offset is relative to the start of the stream. + + * 1 => The offset is relative to the current location of the cursor. + + * 2 => The offset is relative to the end of the stream. + + :param offset: The amount of movement to be done relative to whence. + :param whence: The location the offset is relative to. + :returns: Returns the new position of the cursor. + """ + if self._data_source is None: + return self._buffer.seek(offset, whence) + + if whence >= 2: + # If the seek is relative to the end of the stream, we need to read the + # whole thing in from the source. + self._buffer.seek(0, 2) + self._buffer.write(await self._data_source.read()) + return self._buffer.seek(offset, whence) + + start = self.tell() + target = offset + if whence == 1: + target += start + + current_buffer_size = self._buffer.seek(0, 2) + if current_buffer_size < target: + await self._read_into_buffer(target - current_buffer_size) + + return self._buffer.seek(target, 0) + + async def _read_into_buffer(self, size: int) -> None: + if self._data_source is None: + return + + read_bytes = await self._data_source.read(size) + if len(read_bytes) < size or size < 0: + self._data_source = None + + self._buffer.seek(0, 2) + self._buffer.write(read_bytes) + + def tell(self) -> int: + """Returns the position of the cursor.""" + return self._buffer.tell() + + def __aiter__(self) -> AsyncIterator[bytes]: + return self.iter_chunks() + + def iter_chunks( + self, chunk_size: int = _DEFAULT_CHUNK_SIZE + ) -> AsyncIterator[bytes]: + """Iterate over the reader in chunks of a given size. + + :param chunk_size: The maximum size of each chunk. If less than 0, the entire + reader will be read into one chunk. + """ + return _AsyncByteStreamIterator(self.read, chunk_size) + + def readable(self) -> bool: + """Returns whether the stream is readable.""" + return True + + def writeable(self) -> bool: + """Returns whether the stream is writeable.""" + return False + + def seekable(self) -> bool: + """Returns whether the stream is seekable.""" + return True + + @property + def closed(self) -> bool: + """Returns whether the stream is closed.""" + return self._buffer.closed + + def close(self) -> None: + """Closes the stream, as well as the underlying stream where possible.""" + if callable(close_fn := getattr(self._data_source, "close", None)): + close_fn() # pylint: disable=not-callable + self._data_source = None + self._buffer.close() + + +class _AsyncByteStreamIterator: + """An async bytes iterator that operates over an async read method.""" + + def __init__(self, read: Callable[[int], Awaitable[bytes]], chunk_size: int): + """Initializes self. + + :param read: An async callable that reads a given number of bytes from some + source. + :param chunk_size: The number of bytes to read in each iteration. + """ + self._read = read + self._chunk_size = chunk_size + + def __aiter__(self) -> Self: + return self + + async def __anext__(self) -> bytes: + data = await self._read(self._chunk_size) + if data: + return data + raise StopAsyncIteration diff --git a/packages/aws-sdk-signers/src/aws_sdk_signers/_version.py b/packages/aws-sdk-signers/src/aws_sdk_signers/_version.py new file mode 100644 index 000000000..cf2d598e9 --- /dev/null +++ b/packages/aws-sdk-signers/src/aws_sdk_signers/_version.py @@ -0,0 +1,7 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +# This file is protected via CODEOWNERS +from __future__ import annotations + +__version__ = "0.0.2" diff --git a/packages/aws-sdk-signers/src/aws_sdk_signers/exceptions.py b/packages/aws-sdk-signers/src/aws_sdk_signers/exceptions.py new file mode 100644 index 000000000..b29c23d1b --- /dev/null +++ b/packages/aws-sdk-signers/src/aws_sdk_signers/exceptions.py @@ -0,0 +1,17 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + + +class AWSSDKWarning(UserWarning): ... + + +class BaseAWSSDKException(Exception): + """Top-level exception to capture SDK-related errors.""" + + ... + + +class MissingExpectedParameterException(BaseAWSSDKException, ValueError): + """Some APIs require specific signing properties to be present.""" + + ... diff --git a/packages/aws-sdk-signers/src/aws_sdk_signers/interfaces/__init__.py b/packages/aws-sdk-signers/src/aws_sdk_signers/interfaces/__init__.py new file mode 100644 index 000000000..04f8b7b76 --- /dev/null +++ b/packages/aws-sdk-signers/src/aws_sdk_signers/interfaces/__init__.py @@ -0,0 +1,2 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 diff --git a/packages/aws-sdk-signers/src/aws_sdk_signers/interfaces/http.py b/packages/aws-sdk-signers/src/aws_sdk_signers/interfaces/http.py new file mode 100644 index 000000000..8d450ceb8 --- /dev/null +++ b/packages/aws-sdk-signers/src/aws_sdk_signers/interfaces/http.py @@ -0,0 +1,165 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from collections import OrderedDict +from collections.abc import AsyncIterable, Iterable, Iterator +from enum import Enum +from typing import Protocol, runtime_checkable + + +class FieldPosition(Enum): + """The type of a field. + + Defines its placement in a request or response. + """ + + HEADER = 0 + """Header field. + + In HTTP this is a header as defined in RFC 9110 Section 6.3. Implementations of + other protocols may use this FieldPosition for similar types of metadata. + """ + + TRAILER = 1 + """Trailer field. + + In HTTP this is a trailer as defined in RFC 9110 Section 6.5. Implementations of + other protocols may use this FieldPosition for similar types of metadata. + """ + + +class Field(Protocol): + """A name-value pair representing a single field in a request or response. + + The kind will dictate metadata placement within a message, for example as a header + or trailer field in an HTTP request as defined in RFC 9110 Section 5. + + All field names are case insensitive and case-variance must be treated as + equivalent. Names may be normalized but should be preserved for accuracy during + transmission. + """ + + name: str + values: list[str] + kind: FieldPosition = FieldPosition.HEADER + + def add(self, value: str) -> None: + """Append a value to a field.""" + ... + + def set(self, values: list[str]) -> None: + """Overwrite existing field values.""" + ... + + def remove(self, value: str) -> None: + """Remove all matching entries from list.""" + ... + + def as_string(self, delimiter: str = ", ") -> str: + """Serialize the ``Field``'s values into a single line string.""" + ... + + def as_tuples(self) -> list[tuple[str, str]]: + """Get list of ``name``, ``value`` tuples where each tuple represents one + value.""" + ... + + +class Fields(Protocol): + """Protocol agnostic mapping of key-value pair request metadata, such as HTTP + fields.""" + + # Entries are keyed off the name of a provided Field + entries: OrderedDict[str, Field] + encoding: str = "utf-8" + + def set_field(self, field: Field) -> None: + """Alias for __setitem__ to utilize the field.name for the entry key.""" + ... + + def __setitem__(self, name: str, field: Field) -> None: + """Set entry for a Field name.""" + ... + + def __getitem__(self, name: str) -> Field: + """Retrieve Field entry.""" + ... + + def __delitem__(self, name: str) -> None: + """Delete entry from collection.""" + ... + + def __iter__(self) -> Iterator[Field]: + """Allow iteration over entries.""" + ... + + def __len__(self) -> int: + """Get total number of Field entries.""" + ... + + def get_by_type(self, kind: FieldPosition) -> list[Field]: + """Helper function for retrieving specific types of fields. + + Used to grab all headers or all trailers. + """ + ... + + def extend(self, other: Fields) -> None: + """Merges ``entries`` of ``other`` into the current ``entries``. + + For every `Field` in the ``entries`` of ``other``: If the normalized name + already exists in the current ``entries``, the values from ``other`` are + appended. Otherwise, the ``Field`` is added to the list of ``entries``. + """ + ... + + +class Request(Protocol): + """Protocol-agnostic representation of a request.""" + + destination: URI + body: AsyncIterable[bytes] | Iterable[bytes] | None + + +@runtime_checkable +class URI(Protocol): + """Universal Resource Identifier, target location for a :py:class:`Request`.""" + + scheme: str + """For example ``http`` or ``mqtts``.""" + + username: str | None + """Username part of the userinfo URI component.""" + + password: str | None + """Password part of the userinfo URI component.""" + + host: str + """The hostname, for example ``amazonaws.com``.""" + + port: int | None + """An explicit port number.""" + + path: str | None + """Path component of the URI.""" + + query: str | None + """Query component of the URI as string.""" + + fragment: str | None + """Part of the URI specification, but may not be transmitted by a client.""" + + def build(self) -> str: + """Construct URI string representation. + + Returns a string of the form + ``{scheme}://{username}:{password}@{host}:{port}{path}?{query}#{fragment}`` + """ + ... + + @property + def netloc(self) -> str: + """Construct netloc string in format ``{username}:{password}@{host}:{port}``""" + ... diff --git a/packages/aws-sdk-signers/src/aws_sdk_signers/interfaces/identity.py b/packages/aws-sdk-signers/src/aws_sdk_signers/interfaces/identity.py new file mode 100644 index 000000000..4ab4a9551 --- /dev/null +++ b/packages/aws-sdk-signers/src/aws_sdk_signers/interfaces/identity.py @@ -0,0 +1,20 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +from datetime import datetime +from typing import Protocol + + +class Identity(Protocol): + """An entity available to the client representing who the user is.""" + + # The expiration time of the identity. If time zone is provided, + # it is updated to UTC. The value must always be in UTC. + expiration: datetime | None = None + + @property + def is_expired(self) -> bool: + """Whether the identity is expired.""" + ... diff --git a/packages/aws-sdk-signers/src/aws_sdk_signers/interfaces/io.py b/packages/aws-sdk-signers/src/aws_sdk_signers/interfaces/io.py new file mode 100644 index 000000000..491d21a50 --- /dev/null +++ b/packages/aws-sdk-signers/src/aws_sdk_signers/interfaces/io.py @@ -0,0 +1,27 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +from typing import Protocol, runtime_checkable + + +@runtime_checkable +class ByteStream(Protocol): + """A file-like object with a read method that returns bytes.""" + + def read(self, size: int | None = -1, /) -> bytes: ... + + +@runtime_checkable +class AsyncByteStream(Protocol): + """A file-like object with an async read method.""" + + async def read(self, size: int | None = -1, /) -> bytes: ... + + +@runtime_checkable +class Seekable(Protocol): + """A file-like object with seek and tell implemented.""" + + def seek(self, offset: int, whence: int = 0, /) -> int: ... + + def tell(self) -> int: ... diff --git a/packages/aws-sdk-signers/src/aws_sdk_signers/py.typed b/packages/aws-sdk-signers/src/aws_sdk_signers/py.typed new file mode 100644 index 000000000..5f3ea3d91 --- /dev/null +++ b/packages/aws-sdk-signers/src/aws_sdk_signers/py.typed @@ -0,0 +1,2 @@ +# Instruct type checkers to look for inline type annotations in this package. +# See PEP 561. diff --git a/packages/aws-sdk-signers/src/aws_sdk_signers/signers.py b/packages/aws-sdk-signers/src/aws_sdk_signers/signers.py new file mode 100644 index 000000000..8e96d04ea --- /dev/null +++ b/packages/aws-sdk-signers/src/aws_sdk_signers/signers.py @@ -0,0 +1,798 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +import datetime +import hmac +import io +import warnings +from collections.abc import AsyncIterable, Iterable +from copy import deepcopy +from hashlib import sha256 +from typing import Required, TypedDict +from urllib.parse import parse_qsl, quote + +from .interfaces.io import Seekable +from ._http import URI, AWSRequest, Field +from ._identity import AWSCredentialIdentity +from ._io import AsyncBytesReader +from .exceptions import AWSSDKWarning, MissingExpectedParameterException + +HEADERS_EXCLUDED_FROM_SIGNING: tuple[str, ...] = ( + "accept", + "accept-encoding", + "authorization", + "connection", + "expect", + "user-agent", + "x-amzn-trace-id", +) +DEFAULT_PORTS: dict[str, int] = {"http": 80, "https": 443} + +SIGV4_TIMESTAMP_FORMAT: str = "%Y%m%dT%H%M%SZ" +UNSIGNED_PAYLOAD: str = "UNSIGNED-PAYLOAD" +EMPTY_SHA256_HASH = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + + +class SigV4SigningProperties(TypedDict, total=False): + region: Required[str] + service: Required[str] + date: str + payload_signing_enabled: bool + content_checksum_enabled: bool + + +class SigV4Signer: + """Request signer for applying the AWS Signature Version 4 algorithm.""" + + def sign( + self, + *, + signing_properties: SigV4SigningProperties, + request: AWSRequest, + identity: AWSCredentialIdentity, + ) -> AWSRequest: + """Generate and apply a SigV4 Signature to a copy of the supplied request. + + :param signing_properties: SigV4SigningProperties to define signing primitives + such as the target service, region, and date. + :param request: An AWSRequest to sign prior to sending to the service. + :param identity: A set of credentials representing an AWS Identity or role + capacity. + """ + # Copy and prepopulate any missing values in the + # supplied request and signing properties. + self._validate_identity(identity=identity) + new_signing_properties = self._normalize_signing_properties( + signing_properties=signing_properties + ) + assert "date" in new_signing_properties + + new_request = self._generate_new_request(request=request) + self._apply_required_fields( + request=new_request, + signing_properties=new_signing_properties, + identity=identity, + ) + + # Construct core signing components + canonical_request = self.canonical_request( + signing_properties=new_signing_properties, + request=new_request, + ) + string_to_sign = self.string_to_sign( + canonical_request=canonical_request, + signing_properties=new_signing_properties, + ) + signature = self._signature( + string_to_sign=string_to_sign, + secret_key=identity.secret_access_key, + signing_properties=new_signing_properties, + ) + + signing_fields = self._normalize_signing_fields(request=new_request) + credential_scope = self._scope(signing_properties=new_signing_properties) + credential = f"{identity.access_key_id}/{credential_scope}" + authorization = self.generate_authorization_field( + credential=credential, + signed_headers=list(signing_fields.keys()), + signature=signature, + ) + new_request.fields.set_field(authorization) + + return new_request + + def generate_authorization_field( + self, *, credential: str, signed_headers: list[str], signature: str + ) -> Field: + """Generate the `Authorization` field. + + :param credential: + Credential scope string for generating the Authorization header. + Defined as: + //// + :param signed_headers: + A list of the field names used in signing. + :param signature: + Final hash of the SigV4 signing algorithm generated from the + canonical request and string to sign. + """ + signed_headers_str = ";".join(signed_headers) + auth_str = ( + f"AWS4-HMAC-SHA256 Credential={credential}, " + f"SignedHeaders={signed_headers_str}, Signature={signature}" + ) + return Field(name="Authorization", values=[auth_str]) + + def _signature( + self, + *, + string_to_sign: str, + secret_key: str, + signing_properties: SigV4SigningProperties, + ) -> str: + """Sign the string to sign. + + In SigV4, a signing key is created that is scoped to a specific region and + service. The date, region, service and resulting signing key are individually + hashed, then the composite hash is used to sign the string to sign. + """ + + # Components of Signing Key Calculation + # + # DateKey = HMAC-SHA256("AWS4"+"", "") + # DateRegionKey = HMAC-SHA256(, "") + # DateRegionServiceKey = HMAC-SHA256(, "") + # SigningKey = HMAC-SHA256(, "aws4_request") + assert "date" in signing_properties + k_date = self._hash( + key=f"AWS4{secret_key}".encode(), value=signing_properties["date"][0:8] + ) + k_region = self._hash(key=k_date, value=signing_properties["region"]) + k_service = self._hash(key=k_region, value=signing_properties["service"]) + k_signing = self._hash(key=k_service, value="aws4_request") + + return self._hash(key=k_signing, value=string_to_sign).hex() + + def _hash(self, key: bytes, value: str) -> bytes: + return hmac.new(key=key, msg=value.encode(), digestmod=sha256).digest() + + def _validate_identity(self, *, identity: AWSCredentialIdentity) -> None: + """Perform runtime and expiration checks before attempting signing.""" + if not isinstance(identity, AWSCredentialIdentity): # pyright: ignore + raise ValueError( + "Received unexpected value for identity parameter. Expected " + f"AWSCredentialIdentity but received {type(identity)}." + ) + elif identity.is_expired: + raise ValueError( + f"Provided identity expired at {identity.expiration}. Please " + "refresh the credentials or update the expiration parameter." + ) + + def _normalize_signing_properties( + self, *, signing_properties: SigV4SigningProperties + ) -> SigV4SigningProperties: + # Create copy of signing properties to avoid mutating the original + new_signing_properties = SigV4SigningProperties(**signing_properties) + if "date" not in new_signing_properties: + date_obj = datetime.datetime.now(datetime.UTC) + new_signing_properties["date"] = date_obj.strftime(SIGV4_TIMESTAMP_FORMAT) + return new_signing_properties + + def _generate_new_request(self, *, request: AWSRequest) -> AWSRequest: + return deepcopy(request) + + def _apply_required_fields( + self, + *, + request: AWSRequest, + signing_properties: SigV4SigningProperties, + identity: AWSCredentialIdentity, + ) -> None: + # Apply required X-Amz-Date if neither X-Amz-Date nor Date are present. + if "Date" not in request.fields and "X-Amz-Date" not in request.fields: + assert "date" in signing_properties + request.fields.set_field( + Field(name="X-Amz-Date", values=[signing_properties["date"]]) + ) + # Apply required X-Amz-Security-Token if token present on identity + if ( + "X-Amz-Security-Token" not in request.fields + and identity.session_token is not None + ): + request.fields.set_field( + Field(name="X-Amz-Security-Token", values=[identity.session_token]) + ) + + def canonical_request( + self, *, signing_properties: SigV4SigningProperties, request: AWSRequest + ) -> str: + """The canonical request is a standardized string laying out the components used + in the SigV4 signing algorithm. This is useful to quickly compare inputs to find + signature mismatches and unintended variances. + + The SigV4 specification defines the canonical request to be: + \n + \n + \n + \n + \n + + + :param signing_properties: + SigV4SigningProperties to define signing primitives such as + the target service, region, and date. + :param request: + An AWSRequest to use for generating a SigV4 signature. + """ + # We generate the payload first to ensure any field modifications + # are in place before choosing the canonical fields. + canonical_payload = self._format_canonical_payload( + request=request, signing_properties=signing_properties + ) + canonical_path = self._format_canonical_path(path=request.destination.path) + canonical_query = self._format_canonical_query(query=request.destination.query) + normalized_fields = self._normalize_signing_fields(request=request) + canonical_fields = self._format_canonical_fields(fields=normalized_fields) + return ( + f"{request.method.upper()}\n" + f"{canonical_path}\n" + f"{canonical_query}\n" + f"{canonical_fields}\n" + f"{';'.join(normalized_fields)}\n" + f"{canonical_payload}" + ) + + def string_to_sign( + self, + *, + canonical_request: str, + signing_properties: SigV4SigningProperties, + ) -> str: + """The string to sign is the second step of our signing algorithm which + concatenates the formal identifier of our signing algorithm, the signing + DateTime, the scope of our credentials, and a hash of our previously generated + canonical request. This is another checkpoint that can be used to ensure we're + constructing our signature as intended. + + The SigV4 specification defines the string to sign as: + Algorithm \n + RequestDateTime \n + CredentialScope \n + HashedCanonicalRequest + + :param canonical_request: + String generated from the `canonical_request` method. + :param signing_properties: + SigV4SigningProperties to define signing primitives such as + the target service, region, and date. + """ + date = signing_properties.get("date") + if date is None: + raise MissingExpectedParameterException( + "Cannot generate string_to_sign without a valid date " + f"in your signing_properties. Current value: {date}" + ) + return ( + "AWS4-HMAC-SHA256\n" + f"{date}\n" + f"{self._scope(signing_properties=signing_properties)}\n" + f"{sha256(canonical_request.encode()).hexdigest()}" + ) + + def _scope(self, signing_properties: SigV4SigningProperties) -> str: + assert "date" in signing_properties + formatted_date = signing_properties["date"][0:8] + region = signing_properties["region"] + service = signing_properties["service"] + # Scope format: ///aws4_request + return f"{formatted_date}/{region}/{service}/aws4_request" + + def _format_canonical_path(self, *, path: str | None) -> str: + if path is None: + path = "/" + normalized_path = _remove_dot_segments(path) + return quote(string=normalized_path, safe="/%") + + def _format_canonical_query(self, *, query: str | None) -> str: + if query is None: + return "" + + query_params = parse_qsl(qs=query) + query_parts = ( + (quote(string=key, safe=""), quote(string=value, safe="")) + for key, value in query_params + ) + # key-value pairs must be in sorted order for their encoded forms. + return "&".join(f"{key}={value}" for key, value in sorted(query_parts)) + + def _normalize_signing_fields(self, *, request: AWSRequest) -> dict[str, str]: + normalized_fields = { + field.name.lower(): field.as_string() + for field in request.fields + if self._is_signable_header(field.name.lower()) + } + if "host" not in normalized_fields: + normalized_fields["host"] = self._normalize_host_field( + uri=request.destination # type: ignore - TODO(pyright) + ) + + return dict(sorted(normalized_fields.items())) + + def _is_signable_header(self, field_name: str): + if field_name in HEADERS_EXCLUDED_FROM_SIGNING: + return False + return True + + def _normalize_host_field(self, *, uri: URI) -> str: + if uri.port is not None and DEFAULT_PORTS.get(uri.scheme) == uri.port: + uri_dict = uri.to_dict() + uri_dict.update({"port": None}) + uri = URI(**uri_dict) + return uri.netloc + + def _format_canonical_fields(self, *, fields: dict[str, str]) -> str: + return "".join( + f"{key}:{' '.join(value.split())}\n" for key, value in fields.items() + ) + + def _should_sha256_sign_payload( + self, + *, + request: AWSRequest, + signing_properties: SigV4SigningProperties, + ) -> bool: + # All insecure connections should be signed + if request.destination.scheme != "https": + return True + + return signing_properties.get("payload_signing_enabled", True) + + def _format_canonical_payload( + self, + *, + request: AWSRequest, + signing_properties: SigV4SigningProperties, + ) -> str: + payload_hash = self._compute_payload_hash( + request=request, signing_properties=signing_properties + ) + if signing_properties.get("content_checksum_enabled", False): + request.fields.set_field( + Field(name="X-Amz-Content-SHA256", values=[payload_hash]) + ) + return payload_hash + + def _compute_payload_hash( + self, *, request: AWSRequest, signing_properties: SigV4SigningProperties + ) -> str: + if not self._should_sha256_sign_payload( + request=request, signing_properties=signing_properties + ): + return UNSIGNED_PAYLOAD + + body = request.body + + if body is None: + return EMPTY_SHA256_HASH + + if not isinstance(body, Iterable): + raise TypeError( + "An async body was attached to a synchronous signer. Please use " + "AsyncSigV4Signer for async AWSRequests or ensure your body is " + "of type Iterable[bytes]." + ) + + warnings.warn( + "Payload signing is enabled. This may result in " + "decreased performance for large request bodies.", + AWSSDKWarning, + ) + + checksum = sha256() + if isinstance(body, Seekable): + position = body.tell() + for chunk in body: + checksum.update(chunk) + body.seek(position) + else: + buffer = io.BytesIO() + for chunk in body: + buffer.write(chunk) + checksum.update(chunk) + buffer.seek(0) + request.body = buffer + return checksum.hexdigest() + + +class AsyncSigV4Signer: + """Request signer for applying the AWS Signature Version 4 algorithm.""" + + async def sign( + self, + *, + signing_properties: SigV4SigningProperties, + request: AWSRequest, + identity: AWSCredentialIdentity, + ) -> AWSRequest: + """Generate and apply a SigV4 Signature to a copy of the supplied request. + + :param signing_properties: SigV4SigningProperties to define signing primitives + such as the target service, region, and date. + :param request: An AWSRequest to sign prior to sending to the service. + :param identity: A set of credentials representing an AWS Identity or role + capacity. + """ + # Copy and prepopulate any missing values in the + # supplied request and signing properties. + + await self._validate_identity(identity=identity) + new_signing_properties = await self._normalize_signing_properties( + signing_properties=signing_properties + ) + new_request = await self._generate_new_request(request=request) + await self._apply_required_fields( + request=new_request, + signing_properties=new_signing_properties, + identity=identity, + ) + + # Construct core signing components + canonical_request = await self.canonical_request( + signing_properties=signing_properties, + request=request, + ) + string_to_sign = await self.string_to_sign( + canonical_request=canonical_request, + signing_properties=new_signing_properties, + ) + signature = await self._signature( + string_to_sign=string_to_sign, + secret_key=identity.secret_access_key, + signing_properties=new_signing_properties, + ) + + signing_fields = await self._normalize_signing_fields(request=request) + credential_scope = await self._scope(signing_properties=new_signing_properties) + credential = f"{identity.access_key_id}/{credential_scope}" + authorization = await self.generate_authorization_field( + credential=credential, + signed_headers=list(signing_fields.keys()), + signature=signature, + ) + new_request.fields.set_field(authorization) + return new_request + + async def generate_authorization_field( + self, *, credential: str, signed_headers: list[str], signature: str + ) -> Field: + """Generate the `Authorization` field. + + :param credential: + Credential scope string for generating the Authorization header. + Defined as: + //// + :param signed_headers: + A list of the field names used in signing. + :param signature: + Final hash of the SigV4 signing algorithm generated from the + canonical request and string to sign. + """ + signed_headers_str = ";".join(signed_headers) + auth_str = ( + f"AWS4-HMAC-SHA256 Credential={credential}, " + f"SignedHeaders={signed_headers_str}, Signature={signature}" + ) + return Field(name="Authorization", values=[auth_str]) + + async def _signature( + self, + *, + string_to_sign: str, + secret_key: str, + signing_properties: SigV4SigningProperties, + ) -> str: + """Sign the string to sign. + + In SigV4, a signing key is created that is scoped to a specific region and + service. The date, region, service and resulting signing key are individually + hashed, then the composite hash is used to sign the string to sign. + """ + + # Components of Signing Key Calculation + # + # DateKey = HMAC-SHA256("AWS4"+"", "") + # DateRegionKey = HMAC-SHA256(, "") + # DateRegionServiceKey = HMAC-SHA256(, "") + # SigningKey = HMAC-SHA256(, "aws4_request") + assert "date" in signing_properties + k_date = await self._hash( + key=f"AWS4{secret_key}".encode(), value=signing_properties["date"][0:8] + ) + k_region = await self._hash(key=k_date, value=signing_properties["region"]) + k_service = await self._hash(key=k_region, value=signing_properties["service"]) + k_signing = await self._hash(key=k_service, value="aws4_request") + final_hash = await self._hash(key=k_signing, value=string_to_sign) + + return final_hash.hex() + + async def _hash(self, key: bytes, value: str) -> bytes: + return hmac.new(key=key, msg=value.encode(), digestmod=sha256).digest() + + async def _validate_identity(self, *, identity: AWSCredentialIdentity) -> None: + """Perform runtime and expiration checks before attempting signing.""" + if not isinstance(identity, AWSCredentialIdentity): # pyright: ignore + raise ValueError( + "Received unexpected value for identity parameter. Expected " + f"AWSCredentialIdentity but received {type(identity)}." + ) + elif identity.is_expired: + raise ValueError( + f"Provided identity expired at {identity.expiration}. Please " + "refresh the credentials or update the expiration parameter." + ) + + async def _normalize_signing_properties( + self, *, signing_properties: SigV4SigningProperties + ) -> SigV4SigningProperties: + # Create copy of signing properties to avoid mutating the original + new_signing_properties = SigV4SigningProperties(**signing_properties) + if "date" not in new_signing_properties: + date_obj = datetime.datetime.now(datetime.UTC) + new_signing_properties["date"] = date_obj.strftime(SIGV4_TIMESTAMP_FORMAT) + return new_signing_properties + + async def _generate_new_request(self, *, request: AWSRequest) -> AWSRequest: + return deepcopy(request) + + async def _apply_required_fields( + self, + *, + request: AWSRequest, + signing_properties: SigV4SigningProperties, + identity: AWSCredentialIdentity, + ) -> None: + # Apply required X-Amz-Date if neither X-Amz-Date nor Date are present. + if "Date" not in request.fields and "X-Amz-Date" not in request.fields: + assert "date" in signing_properties + request.fields.set_field( + Field(name="X-Amz-Date", values=[signing_properties["date"]]) + ) + # Apply required X-Amz-Security-Token if token present on identity + if ( + "X-Amz-Security-Token" not in request.fields + and identity.session_token is not None + ): + request.fields.set_field( + Field(name="X-Amz-Security-Token", values=[identity.session_token]) + ) + + async def canonical_request( + self, *, signing_properties: SigV4SigningProperties, request: AWSRequest + ) -> str: + """The canonical request is a standardized string laying out the components used + in the SigV4 signing algorithm. This is useful to quickly compare inputs to find + signature mismatches and unintended variances. + + The SigV4 specification defines the canonical request to be: + \n + \n + \n + \n + \n + + + :param signing_properties: + SigV4SigningProperties to define signing primitives such as + the target service, region, and date. + :param request: + An AWSRequest to use for generating a SigV4 signature. + """ + # We generate the payload first to ensure any field modifications + # are in place before choosing the canonical fields. + canonical_payload = await self._format_canonical_payload( + request=request, signing_properties=signing_properties + ) + canonical_path = await self._format_canonical_path( + path=request.destination.path + ) + canonical_query = await self._format_canonical_query( + query=request.destination.query + ) + normalized_fields = await self._normalize_signing_fields(request=request) + canonical_fields = await self._format_canonical_fields(fields=normalized_fields) + return ( + f"{request.method.upper()}\n" + f"{canonical_path}\n" + f"{canonical_query}\n" + f"{canonical_fields}\n" + f"{';'.join(normalized_fields)}\n" + f"{canonical_payload}" + ) + + async def string_to_sign( + self, + *, + canonical_request: str, + signing_properties: SigV4SigningProperties, + ) -> str: + """The string to sign is the second step of our signing algorithm which + concatenates the formal identifier of our signing algorithm, the signing + DateTime, the scope of our credentials, and a hash of our previously generated + canonical request. This is another checkpoint that can be used to ensure we're + constructing our signature as intended. + + The SigV4 specification defines the string to sign as: + Algorithm \n + RequestDateTime \n + CredentialScope \n + HashedCanonicalRequest + + :param canonical_request: + String generated from the `canonical_request` method. + :param signing_properties: + SigV4SigningProperties to define signing primitives such as + the target service, region, and date. + """ + date = signing_properties.get("date") + if date is None: + raise MissingExpectedParameterException( + "Cannot generate string_to_sign without a valid date " + f"in your signing_properties. Current value: {date}" + ) + scope = await self._scope(signing_properties=signing_properties) + return ( + "AWS4-HMAC-SHA256\n" + f"{date}\n" + f"{scope}\n" + f"{sha256(canonical_request.encode()).hexdigest()}" + ) + + async def _scope(self, signing_properties: SigV4SigningProperties) -> str: + assert "date" in signing_properties + formatted_date = signing_properties["date"][0:8] + region = signing_properties["region"] + service = signing_properties["service"] + # Scope format: ///aws4_request + return f"{formatted_date}/{region}/{service}/aws4_request" + + async def _format_canonical_path(self, *, path: str | None) -> str: + if path is None: + path = "/" + normalized_path = _remove_dot_segments(path) + return quote(string=normalized_path, safe="/%") + + async def _format_canonical_query(self, *, query: str | None) -> str: + if query is None: + return "" + + query_params = parse_qsl(qs=query) + query_parts = ( + (quote(string=key, safe=""), quote(string=value, safe="")) + for key, value in query_params + ) + # key-value pairs must be in sorted order for their encoded forms. + return "&".join(f"{key}={value}" for key, value in sorted(query_parts)) + + async def _normalize_signing_fields(self, *, request: AWSRequest) -> dict[str, str]: + normalized_fields = { + field.name.lower(): field.as_string() + for field in request.fields + if self._is_signable_header(field.name.lower()) + } + if "host" not in normalized_fields: + normalized_fields["host"] = await self._normalize_host_field( + uri=request.destination # type: ignore - TODO(pyright) + ) + + return dict(sorted(normalized_fields.items())) + + def _is_signable_header(self, field_name: str): + if field_name in HEADERS_EXCLUDED_FROM_SIGNING: + return False + return True + + async def _normalize_host_field(self, *, uri: URI) -> str: + if uri.port is not None and DEFAULT_PORTS.get(uri.scheme) == uri.port: + uri_dict = uri.to_dict() + uri_dict.update({"port": None}) + uri = URI(**uri_dict) + return uri.netloc + + async def _format_canonical_fields(self, *, fields: dict[str, str]) -> str: + return "".join( + f"{key}:{' '.join(value.split())}\n" for key, value in fields.items() + ) + + async def _should_sha256_sign_payload( + self, + *, + request: AWSRequest, + signing_properties: SigV4SigningProperties, + ) -> bool: + # All insecure connections should be signed + if request.destination.scheme != "https": + return True + + return signing_properties.get("payload_signing_enabled", True) + + async def _format_canonical_payload( + self, + *, + request: AWSRequest, + signing_properties: SigV4SigningProperties, + ) -> str: + payload_hash = await self._compute_payload_hash( + request=request, signing_properties=signing_properties + ) + if signing_properties.get("content_checksum_enabled", False): + request.fields.set_field( + Field(name="X-Amz-Content-SHA256", values=[payload_hash]) + ) + return payload_hash + + async def _compute_payload_hash( + self, *, request: AWSRequest, signing_properties: SigV4SigningProperties + ) -> str: + if not await self._should_sha256_sign_payload( + request=request, signing_properties=signing_properties + ): + return UNSIGNED_PAYLOAD + + body = request.body + + if body is None: + return EMPTY_SHA256_HASH + + if not isinstance(body, AsyncIterable): + raise TypeError( + "A sync body was attached to an asynchronous signer. Please use " + "SigV4Signer for sync AWSRequests or ensure your body is " + "of type AsyncIterable[bytes]." + ) + warnings.warn( + "Payload signing is enabled. This may result in " + "decreased performance for large request bodies.", + AWSSDKWarning, + ) + + checksum = sha256() + if isinstance(body, Seekable): + position = body.tell() + async for chunk in body: + checksum.update(chunk) + body.seek(position) + else: + buffer = io.BytesIO() + async for chunk in body: + buffer.write(chunk) + checksum.update(chunk) + buffer.seek(0) + request.body = AsyncBytesReader(buffer) + return checksum.hexdigest() + + +def _remove_dot_segments(path: str, remove_consecutive_slashes: bool = True) -> str: + """Removes dot segments from a path per :rfc:`3986#section-5.2.4`. + + Optionally removes consecutive slashes, true by default. + :param path: The path to modify. + :param remove_consecutive_slashes: Whether to remove consecutive slashes. + :returns: The path with dot segments removed. + """ + output: list[str] = [] + for segment in path.split("/"): + if segment == ".": + continue + elif segment != "..": + output.append(segment) + elif output: + output.pop() + if path.startswith("/") and (not output or output[0]): + output.insert(0, "") + if output and path.endswith(("/.", "/..")): + output.append("") + result = "/".join(output) + if remove_consecutive_slashes: + result = result.replace("//", "/") + return result diff --git a/packages/aws-sdk-signers/tests/__init__.py b/packages/aws-sdk-signers/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/packages/aws-sdk-signers/tests/unit/__init__.py b/packages/aws-sdk-signers/tests/unit/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/packages/aws-sdk-signers/tests/unit/auth/__init__.py b/packages/aws-sdk-signers/tests/unit/auth/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/LICENSE b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/NOTICE b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/NOTICE new file mode 100644 index 000000000..0ceb2983d --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/NOTICE @@ -0,0 +1,2 @@ +AWS Signature Version 4 Test Suite +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-key-duplicate/get-header-key-duplicate.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-key-duplicate/get-header-key-duplicate.authz new file mode 100644 index 000000000..ade3ec753 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-key-duplicate/get-header-key-duplicate.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c9d5ea9f3f72853aea855b47ea873832890dbdd183b4468f858259531a5138ea \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-key-duplicate/get-header-key-duplicate.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-key-duplicate/get-header-key-duplicate.creq new file mode 100644 index 000000000..fa8f49a1c --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-key-duplicate/get-header-key-duplicate.creq @@ -0,0 +1,9 @@ +GET +/ + +host:example.amazonaws.com +my-header1:value2,value2,value1 +x-amz-date:20150830T123600Z + +host;my-header1;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-key-duplicate/get-header-key-duplicate.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-key-duplicate/get-header-key-duplicate.req new file mode 100644 index 000000000..08a0364c8 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-key-duplicate/get-header-key-duplicate.req @@ -0,0 +1,6 @@ +GET / HTTP/1.1 +Host:example.amazonaws.com +My-Header1:value2 +My-Header1:value2 +My-Header1:value1 +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-key-duplicate/get-header-key-duplicate.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-key-duplicate/get-header-key-duplicate.sreq new file mode 100644 index 000000000..f0166e18c --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-key-duplicate/get-header-key-duplicate.sreq @@ -0,0 +1,7 @@ +GET / HTTP/1.1 +Host:example.amazonaws.com +My-Header1:value2 +My-Header1:value2 +My-Header1:value1 +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c9d5ea9f3f72853aea855b47ea873832890dbdd183b4468f858259531a5138ea \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-key-duplicate/get-header-key-duplicate.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-key-duplicate/get-header-key-duplicate.sts new file mode 100644 index 000000000..48a135ece --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-key-duplicate/get-header-key-duplicate.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +dc7f04a3abfde8d472b0ab1a418b741b7c67174dad1551b4117b15527fbe966c \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-multiline/get-header-value-multiline.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-multiline/get-header-value-multiline.authz new file mode 100644 index 000000000..9f455693b --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-multiline/get-header-value-multiline.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=cfd34249e4b1c8d6b91ef74165d41a32e5fab3306300901bb65a51a73575eefd \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-multiline/get-header-value-multiline.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-multiline/get-header-value-multiline.creq new file mode 100644 index 000000000..8cb54769d --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-multiline/get-header-value-multiline.creq @@ -0,0 +1,9 @@ +GET +/ + +host:example.amazonaws.com +my-header1:value1 value2 value3 +x-amz-date:20150830T123600Z + +host;my-header1;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-multiline/get-header-value-multiline.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-multiline/get-header-value-multiline.req new file mode 100644 index 000000000..7caa6acc2 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-multiline/get-header-value-multiline.req @@ -0,0 +1,6 @@ +GET / HTTP/1.1 +Host:example.amazonaws.com +My-Header1:value1 + value2 + value3 +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-multiline/get-header-value-multiline.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-multiline/get-header-value-multiline.sreq new file mode 100644 index 000000000..49513e3bd --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-multiline/get-header-value-multiline.sreq @@ -0,0 +1,7 @@ +GET / HTTP/1.1 +Host:example.amazonaws.com +My-Header1:value1 + value2 + value3 +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=cfd34249e4b1c8d6b91ef74165d41a32e5fab3306300901bb65a51a73575eefd \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-multiline/get-header-value-multiline.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-multiline/get-header-value-multiline.sts new file mode 100644 index 000000000..97c743099 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-multiline/get-header-value-multiline.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +e99419459a677bc11de234014be3c4e72c1ea5b454ceb58b613061f5d7a162e8 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-order/get-header-value-order.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-order/get-header-value-order.authz new file mode 100644 index 000000000..c0409ab2a --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-order/get-header-value-order.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=08c7e5a9acfcfeb3ab6b2185e75ce8b1deb5e634ec47601a50643f830c755c01 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-order/get-header-value-order.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-order/get-header-value-order.creq new file mode 100644 index 000000000..e336bc94b --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-order/get-header-value-order.creq @@ -0,0 +1,9 @@ +GET +/ + +host:example.amazonaws.com +my-header1:value4,value1,value3,value2 +x-amz-date:20150830T123600Z + +host;my-header1;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-order/get-header-value-order.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-order/get-header-value-order.req new file mode 100644 index 000000000..f7bd9e668 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-order/get-header-value-order.req @@ -0,0 +1,7 @@ +GET / HTTP/1.1 +Host:example.amazonaws.com +My-Header1:value4 +My-Header1:value1 +My-Header1:value3 +My-Header1:value2 +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-order/get-header-value-order.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-order/get-header-value-order.sreq new file mode 100644 index 000000000..79e16a953 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-order/get-header-value-order.sreq @@ -0,0 +1,8 @@ +GET / HTTP/1.1 +Host:example.amazonaws.com +My-Header1:value4 +My-Header1:value1 +My-Header1:value3 +My-Header1:value2 +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=08c7e5a9acfcfeb3ab6b2185e75ce8b1deb5e634ec47601a50643f830c755c01 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-order/get-header-value-order.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-order/get-header-value-order.sts new file mode 100644 index 000000000..711a8d4d6 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-order/get-header-value-order.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +31ce73cd3f3d9f66977ad3dd957dc47af14df92fcd8509f59b349e9137c58b86 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-trim/get-header-value-trim.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-trim/get-header-value-trim.authz new file mode 100644 index 000000000..4874ac0b1 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-trim/get-header-value-trim.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;my-header2;x-amz-date, Signature=acc3ed3afb60bb290fc8d2dd0098b9911fcaa05412b367055dee359757a9c736 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-trim/get-header-value-trim.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-trim/get-header-value-trim.creq new file mode 100644 index 000000000..a59087c9a --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-trim/get-header-value-trim.creq @@ -0,0 +1,10 @@ +GET +/ + +host:example.amazonaws.com +my-header1:value1 +my-header2:"a b c" +x-amz-date:20150830T123600Z + +host;my-header1;my-header2;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-trim/get-header-value-trim.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-trim/get-header-value-trim.req new file mode 100644 index 000000000..901f36c35 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-trim/get-header-value-trim.req @@ -0,0 +1,5 @@ +GET / HTTP/1.1 +Host:example.amazonaws.com +My-Header1: value1 +My-Header2: "a b c" +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-trim/get-header-value-trim.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-trim/get-header-value-trim.sreq new file mode 100644 index 000000000..98224c9bd --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-trim/get-header-value-trim.sreq @@ -0,0 +1,6 @@ +GET / HTTP/1.1 +Host:example.amazonaws.com +My-Header1: value1 +My-Header2: "a b c" +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;my-header2;x-amz-date, Signature=acc3ed3afb60bb290fc8d2dd0098b9911fcaa05412b367055dee359757a9c736 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-trim/get-header-value-trim.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-trim/get-header-value-trim.sts new file mode 100644 index 000000000..a0b15cc70 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-header-value-trim/get-header-value-trim.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +a726db9b0df21c14f559d0a978e563112acb1b9e05476f0a6a1c7d68f28605c7 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-unreserved/get-unreserved.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-unreserved/get-unreserved.authz new file mode 100644 index 000000000..2943ec89d --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-unreserved/get-unreserved.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=07ef7494c76fa4850883e2b006601f940f8a34d404d0cfa977f52a65bbf5f24f \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-unreserved/get-unreserved.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-unreserved/get-unreserved.creq new file mode 100644 index 000000000..8af54df27 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-unreserved/get-unreserved.creq @@ -0,0 +1,8 @@ +GET +/-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz + +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-unreserved/get-unreserved.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-unreserved/get-unreserved.req new file mode 100644 index 000000000..da760cdb3 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-unreserved/get-unreserved.req @@ -0,0 +1,3 @@ +GET /-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-unreserved/get-unreserved.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-unreserved/get-unreserved.sreq new file mode 100644 index 000000000..8001b3d6b --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-unreserved/get-unreserved.sreq @@ -0,0 +1,4 @@ +GET /-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=07ef7494c76fa4850883e2b006601f940f8a34d404d0cfa977f52a65bbf5f24f \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-unreserved/get-unreserved.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-unreserved/get-unreserved.sts new file mode 100644 index 000000000..e9dc54146 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-unreserved/get-unreserved.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +6a968768eefaa713e2a6b16b589a8ea192661f098f37349f4e2c0082757446f9 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-utf8/get-utf8.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-utf8/get-utf8.authz new file mode 100644 index 000000000..738b3fbd8 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-utf8/get-utf8.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=8318018e0b0f223aa2bbf98705b62bb787dc9c0e678f255a891fd03141be5d85 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-utf8/get-utf8.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-utf8/get-utf8.creq new file mode 100644 index 000000000..5d4b9f619 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-utf8/get-utf8.creq @@ -0,0 +1,8 @@ +GET +/%E1%88%B4 + +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-utf8/get-utf8.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-utf8/get-utf8.req new file mode 100644 index 000000000..da4808d0b --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-utf8/get-utf8.req @@ -0,0 +1,3 @@ +GET /ሴ HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-utf8/get-utf8.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-utf8/get-utf8.sreq new file mode 100644 index 000000000..94eadb6d2 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-utf8/get-utf8.sreq @@ -0,0 +1,4 @@ +GET /ሴ HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=8318018e0b0f223aa2bbf98705b62bb787dc9c0e678f255a891fd03141be5d85 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-utf8/get-utf8.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-utf8/get-utf8.sts new file mode 100644 index 000000000..5edc8f456 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-utf8/get-utf8.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +2a0a97d02205e45ce2e994789806b19270cfbbb0921b278ccf58f5249ac42102 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.authz new file mode 100644 index 000000000..65b5c7ce4 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=a67d582fa61cc504c4bae71f336f98b97f1ea3c7a6bfe1b6e45aec72011b9aeb \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.creq new file mode 100644 index 000000000..c6cdceda1 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.creq @@ -0,0 +1,8 @@ +GET +/ +Param1=value1 +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.req new file mode 100644 index 000000000..970d0a050 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.req @@ -0,0 +1,3 @@ +GET /?Param1=value1 HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sreq new file mode 100644 index 000000000..f0815913f --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sreq @@ -0,0 +1,4 @@ +GET /?Param1=value1 HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=a67d582fa61cc504c4bae71f336f98b97f1ea3c7a6bfe1b6e45aec72011b9aeb \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sts new file mode 100644 index 000000000..c4ed216c1 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +1e24db194ed7d0eec2de28d7369675a243488e08526e8c1c73571282f7c517ab \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-encoded/get-vanilla-query-order-encoded.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-encoded/get-vanilla-query-order-encoded.authz new file mode 100644 index 000000000..99e972571 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-encoded/get-vanilla-query-order-encoded.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=371d3713e185cc334048618a97f809c9ffe339c62934c032af5a0e595648fcac \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-encoded/get-vanilla-query-order-encoded.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-encoded/get-vanilla-query-order-encoded.creq new file mode 100644 index 000000000..0c8ba21f3 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-encoded/get-vanilla-query-order-encoded.creq @@ -0,0 +1,8 @@ +GET +/ +%E1%88%B4=Value1&Param=Value2&Param-3=Value3 +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-encoded/get-vanilla-query-order-encoded.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-encoded/get-vanilla-query-order-encoded.req new file mode 100644 index 000000000..c539437dd --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-encoded/get-vanilla-query-order-encoded.req @@ -0,0 +1,3 @@ +GET /?Param-3=Value3&Param=Value2&%E1%88%B4=Value1 HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-encoded/get-vanilla-query-order-encoded.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-encoded/get-vanilla-query-order-encoded.sreq new file mode 100644 index 000000000..7d4361644 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-encoded/get-vanilla-query-order-encoded.sreq @@ -0,0 +1,4 @@ +GET /?Param-3=Value3&Param=Value2&%E1%88%B4=Value1 HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=371d3713e185cc334048618a97f809c9ffe339c62934c032af5a0e595648fcac \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-encoded/get-vanilla-query-order-encoded.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-encoded/get-vanilla-query-order-encoded.sts new file mode 100644 index 000000000..bf674ad63 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-encoded/get-vanilla-query-order-encoded.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +868294f5c38bd141c4972a373a76654f1418a8e4fc18b2e7903ae45e8ae0ec71 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.authz new file mode 100644 index 000000000..c781fe665 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=b97d918cfa904a5beff61c982a1b6f458b799221646efd99d3219ec94cdf2500 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.creq new file mode 100644 index 000000000..8ae02cd60 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.creq @@ -0,0 +1,8 @@ +GET +/ +Param1=value1&Param2=value2 +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.req new file mode 100644 index 000000000..8a56f15f7 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.req @@ -0,0 +1,3 @@ +GET /?Param2=value2&Param1=value1 HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sreq new file mode 100644 index 000000000..aa3162d8e --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sreq @@ -0,0 +1,4 @@ +GET /?Param2=value2&Param1=value1 HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=b97d918cfa904a5beff61c982a1b6f458b799221646efd99d3219ec94cdf2500 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sts new file mode 100644 index 000000000..f773de594 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +816cd5b414d056048ba4f7c5386d6e0533120fb1fcfa93762cf0fc39e2cf19e0 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key/get-vanilla-query-order-key.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key/get-vanilla-query-order-key.authz new file mode 100644 index 000000000..812cd3fdf --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key/get-vanilla-query-order-key.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=eedbc4e291e521cf13422ffca22be7d2eb8146eecf653089df300a15b2382bd1 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key/get-vanilla-query-order-key.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key/get-vanilla-query-order-key.creq new file mode 100644 index 000000000..36c3cdfae --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key/get-vanilla-query-order-key.creq @@ -0,0 +1,8 @@ +GET +/ +Param1=Value1&Param1=value2 +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key/get-vanilla-query-order-key.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key/get-vanilla-query-order-key.req new file mode 100644 index 000000000..375a49655 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key/get-vanilla-query-order-key.req @@ -0,0 +1,3 @@ +GET /?Param1=value2&Param1=Value1 HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key/get-vanilla-query-order-key.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key/get-vanilla-query-order-key.sreq new file mode 100644 index 000000000..bc8e65201 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key/get-vanilla-query-order-key.sreq @@ -0,0 +1,4 @@ +GET /?Param1=value2&Param1=Value1 HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=eedbc4e291e521cf13422ffca22be7d2eb8146eecf653089df300a15b2382bd1 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key/get-vanilla-query-order-key.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key/get-vanilla-query-order-key.sts new file mode 100644 index 000000000..fd43a414c --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-key/get-vanilla-query-order-key.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +704b4cef673542d84cdff252633f065e8daeba5f168b77116f8b1bcaf3d38f89 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-value/get-vanilla-query-order-value.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-value/get-vanilla-query-order-value.authz new file mode 100644 index 000000000..b8ad91f66 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-value/get-vanilla-query-order-value.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5772eed61e12b33fae39ee5e7012498b51d56abc0abb7c60486157bd471c4694 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-value/get-vanilla-query-order-value.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-value/get-vanilla-query-order-value.creq new file mode 100644 index 000000000..26898ebeb --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-value/get-vanilla-query-order-value.creq @@ -0,0 +1,8 @@ +GET +/ +Param1=value1&Param1=value2 +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-value/get-vanilla-query-order-value.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-value/get-vanilla-query-order-value.req new file mode 100644 index 000000000..9255bee05 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-value/get-vanilla-query-order-value.req @@ -0,0 +1,3 @@ +GET /?Param1=value2&Param1=value1 HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-value/get-vanilla-query-order-value.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-value/get-vanilla-query-order-value.sreq new file mode 100644 index 000000000..4793e218c --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-value/get-vanilla-query-order-value.sreq @@ -0,0 +1,4 @@ +GET /?Param1=value2&Param1=value1 HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5772eed61e12b33fae39ee5e7012498b51d56abc0abb7c60486157bd471c4694 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-value/get-vanilla-query-order-value.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-value/get-vanilla-query-order-value.sts new file mode 100644 index 000000000..90e66b8da --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-order-value/get-vanilla-query-order-value.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +c968629d70850097a2d8781c9bf7edcb988b04cac14cca9be4acc3595f884606 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.authz new file mode 100644 index 000000000..a44ca5be8 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9c3e54bfcdf0b19771a7f523ee5669cdf59bc7cc0884027167c21bb143a40197 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.creq new file mode 100644 index 000000000..5249be3bf --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.creq @@ -0,0 +1,8 @@ +GET +/ +-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.req new file mode 100644 index 000000000..d2833b32f --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.req @@ -0,0 +1,3 @@ +GET /?-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sreq new file mode 100644 index 000000000..ba1ef4023 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sreq @@ -0,0 +1,4 @@ +GET /?-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9c3e54bfcdf0b19771a7f523ee5669cdf59bc7cc0884027167c21bb143a40197 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sts new file mode 100644 index 000000000..24a97d209 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +c30d4703d9f799439be92736156d47ccfb2d879ddf56f5befa6d1d6aab979177 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query/get-vanilla-query.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query/get-vanilla-query.authz new file mode 100644 index 000000000..551c0271d --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query/get-vanilla-query.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query/get-vanilla-query.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query/get-vanilla-query.creq new file mode 100644 index 000000000..ed91561f4 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query/get-vanilla-query.creq @@ -0,0 +1,8 @@ +GET +/ + +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query/get-vanilla-query.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query/get-vanilla-query.req new file mode 100644 index 000000000..0f7a9bfae --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query/get-vanilla-query.req @@ -0,0 +1,3 @@ +GET / HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query/get-vanilla-query.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query/get-vanilla-query.sreq new file mode 100644 index 000000000..d739b01fd --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query/get-vanilla-query.sreq @@ -0,0 +1,4 @@ +GET / HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query/get-vanilla-query.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query/get-vanilla-query.sts new file mode 100644 index 000000000..b187649cb --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-query/get-vanilla-query.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-utf8-query/get-vanilla-utf8-query.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-utf8-query/get-vanilla-utf8-query.authz new file mode 100644 index 000000000..e016c3da0 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-utf8-query/get-vanilla-utf8-query.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=2cdec8eed098649ff3a119c94853b13c643bcf08f8b0a1d91e12c9027818dd04 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-utf8-query/get-vanilla-utf8-query.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-utf8-query/get-vanilla-utf8-query.creq new file mode 100644 index 000000000..a835c9e49 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-utf8-query/get-vanilla-utf8-query.creq @@ -0,0 +1,8 @@ +GET +/ +%E1%88%B4=bar +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-utf8-query/get-vanilla-utf8-query.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-utf8-query/get-vanilla-utf8-query.req new file mode 100644 index 000000000..cc2757e16 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-utf8-query/get-vanilla-utf8-query.req @@ -0,0 +1,3 @@ +GET /?ሴ=bar HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-utf8-query/get-vanilla-utf8-query.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-utf8-query/get-vanilla-utf8-query.sreq new file mode 100644 index 000000000..7baf4c82f --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-utf8-query/get-vanilla-utf8-query.sreq @@ -0,0 +1,4 @@ +GET /?ሴ=bar HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=2cdec8eed098649ff3a119c94853b13c643bcf08f8b0a1d91e12c9027818dd04 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-utf8-query/get-vanilla-utf8-query.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-utf8-query/get-vanilla-utf8-query.sts new file mode 100644 index 000000000..51ee71b74 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-utf8-query/get-vanilla-utf8-query.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +eb30c5bed55734080471a834cc727ae56beb50e5f39d1bff6d0d38cb192a7073 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-with-session-token/get-vanilla-with-session-token.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-with-session-token/get-vanilla-with-session-token.authz new file mode 100644 index 000000000..cb7ae61e1 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-with-session-token/get-vanilla-with-session-token.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=07ec1639c89043aa0e3e2de82b96708f198cceab042d4a97044c66dd9f74e7f8 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-with-session-token/get-vanilla-with-session-token.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-with-session-token/get-vanilla-with-session-token.creq new file mode 100644 index 000000000..ccacdeb49 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-with-session-token/get-vanilla-with-session-token.creq @@ -0,0 +1,9 @@ +GET +/ + +host:example.amazonaws.com +x-amz-date:20150830T123600Z +x-amz-security-token:6e86291e8372ff2a2260956d9b8aae1d763fbf315fa00fa31553b73ebf194267 + +host;x-amz-date;x-amz-security-token +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-with-session-token/get-vanilla-with-session-token.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-with-session-token/get-vanilla-with-session-token.req new file mode 100644 index 000000000..0f7a9bfae --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-with-session-token/get-vanilla-with-session-token.req @@ -0,0 +1,3 @@ +GET / HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-with-session-token/get-vanilla-with-session-token.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-with-session-token/get-vanilla-with-session-token.sreq new file mode 100644 index 000000000..406ac5690 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-with-session-token/get-vanilla-with-session-token.sreq @@ -0,0 +1,5 @@ +GET / HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +X-Amz-Security-Token:6e86291e8372ff2a2260956d9b8aae1d763fbf315fa00fa31553b73ebf194267 +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-with-session-token/get-vanilla-with-session-token.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-with-session-token/get-vanilla-with-session-token.sts new file mode 100644 index 000000000..742b880cb --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla-with-session-token/get-vanilla-with-session-token.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +067b36aa60031588cea4a4cde1f21215227a047690c72247f1d70b32fbbfad2b \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla/get-vanilla.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla/get-vanilla.authz new file mode 100644 index 000000000..551c0271d --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla/get-vanilla.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla/get-vanilla.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla/get-vanilla.creq new file mode 100644 index 000000000..ed91561f4 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla/get-vanilla.creq @@ -0,0 +1,8 @@ +GET +/ + +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla/get-vanilla.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla/get-vanilla.req new file mode 100644 index 000000000..0f7a9bfae --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla/get-vanilla.req @@ -0,0 +1,3 @@ +GET / HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla/get-vanilla.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla/get-vanilla.sreq new file mode 100644 index 000000000..d739b01fd --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla/get-vanilla.sreq @@ -0,0 +1,4 @@ +GET / HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla/get-vanilla.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla/get-vanilla.sts new file mode 100644 index 000000000..b187649cb --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/get-vanilla/get-vanilla.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative-relative/get-relative-relative.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative-relative/get-relative-relative.authz new file mode 100644 index 000000000..551c0271d --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative-relative/get-relative-relative.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative-relative/get-relative-relative.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative-relative/get-relative-relative.creq new file mode 100644 index 000000000..ed91561f4 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative-relative/get-relative-relative.creq @@ -0,0 +1,8 @@ +GET +/ + +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative-relative/get-relative-relative.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative-relative/get-relative-relative.req new file mode 100644 index 000000000..cfd4e8b74 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative-relative/get-relative-relative.req @@ -0,0 +1,3 @@ +GET /example1/example2/../.. HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative-relative/get-relative-relative.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative-relative/get-relative-relative.sreq new file mode 100644 index 000000000..cbdebe2cc --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative-relative/get-relative-relative.sreq @@ -0,0 +1,4 @@ +GET /example1/example2/../.. HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative-relative/get-relative-relative.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative-relative/get-relative-relative.sts new file mode 100644 index 000000000..b187649cb --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative-relative/get-relative-relative.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative/get-relative.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative/get-relative.authz new file mode 100644 index 000000000..551c0271d --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative/get-relative.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative/get-relative.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative/get-relative.creq new file mode 100644 index 000000000..ed91561f4 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative/get-relative.creq @@ -0,0 +1,8 @@ +GET +/ + +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative/get-relative.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative/get-relative.req new file mode 100644 index 000000000..9d6d7ca20 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative/get-relative.req @@ -0,0 +1,3 @@ +GET /example/.. HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative/get-relative.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative/get-relative.sreq new file mode 100644 index 000000000..4f59e7d20 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative/get-relative.sreq @@ -0,0 +1,4 @@ +GET /example/.. HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative/get-relative.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative/get-relative.sts new file mode 100644 index 000000000..b187649cb --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-relative/get-relative.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.authz new file mode 100644 index 000000000..551c0271d --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.creq new file mode 100644 index 000000000..ed91561f4 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.creq @@ -0,0 +1,8 @@ +GET +/ + +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.req new file mode 100644 index 000000000..f3537b709 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.req @@ -0,0 +1,3 @@ +GET /./ HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sreq new file mode 100644 index 000000000..23a2b41ce --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sreq @@ -0,0 +1,4 @@ +GET /./ HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sts new file mode 100644 index 000000000..b187649cb --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.authz new file mode 100644 index 000000000..b76ca1e2d --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=ef75d96142cf21edca26f06005da7988e4f8dc83a165a80865db7089db637ec5 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.creq new file mode 100644 index 000000000..915c57f21 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.creq @@ -0,0 +1,8 @@ +GET +/example + +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.req new file mode 100644 index 000000000..3c9107171 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.req @@ -0,0 +1,3 @@ +GET /./example HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sreq new file mode 100644 index 000000000..809660965 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sreq @@ -0,0 +1,4 @@ +GET /./example HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=ef75d96142cf21edca26f06005da7988e4f8dc83a165a80865db7089db637ec5 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sts new file mode 100644 index 000000000..7429923e6 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +214d50c111a8edc4819da6a636336472c916b5240f51e9a51b5c3305180cf702 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash/get-slash.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash/get-slash.authz new file mode 100644 index 000000000..551c0271d --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash/get-slash.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash/get-slash.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash/get-slash.creq new file mode 100644 index 000000000..ed91561f4 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash/get-slash.creq @@ -0,0 +1,8 @@ +GET +/ + +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash/get-slash.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash/get-slash.req new file mode 100644 index 000000000..ede8e3c8e --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash/get-slash.req @@ -0,0 +1,3 @@ +GET // HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash/get-slash.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash/get-slash.sreq new file mode 100644 index 000000000..cde31b438 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash/get-slash.sreq @@ -0,0 +1,4 @@ +GET // HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash/get-slash.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash/get-slash.sts new file mode 100644 index 000000000..b187649cb --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slash/get-slash.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slashes/get-slashes.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slashes/get-slashes.authz new file mode 100644 index 000000000..307c1051d --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slashes/get-slashes.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9a624bd73a37c9a373b5312afbebe7a714a789de108f0bdfe846570885f57e84 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slashes/get-slashes.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slashes/get-slashes.creq new file mode 100644 index 000000000..2bdaf7479 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slashes/get-slashes.creq @@ -0,0 +1,8 @@ +GET +/example/ + +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slashes/get-slashes.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slashes/get-slashes.req new file mode 100644 index 000000000..a4307ce42 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slashes/get-slashes.req @@ -0,0 +1,3 @@ +GET //example// HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slashes/get-slashes.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slashes/get-slashes.sreq new file mode 100644 index 000000000..c84a80d56 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slashes/get-slashes.sreq @@ -0,0 +1,4 @@ +GET //example// HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9a624bd73a37c9a373b5312afbebe7a714a789de108f0bdfe846570885f57e84 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slashes/get-slashes.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slashes/get-slashes.sts new file mode 100644 index 000000000..95d1fc258 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-slashes/get-slashes.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +cb96b4ac96d501f7c5c15bc6d67b3035061cfced4af6585ad927f7e6c985c015 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-space/get-space.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-space/get-space.authz new file mode 100644 index 000000000..832d8a50d --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-space/get-space.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=652487583200325589f1fba4c7e578f72c47cb61beeca81406b39ddec1366741 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-space/get-space.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-space/get-space.creq new file mode 100644 index 000000000..124a7096a --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-space/get-space.creq @@ -0,0 +1,8 @@ +GET +/example%20space/ + +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-space/get-space.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-space/get-space.req new file mode 100644 index 000000000..b346af2d4 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-space/get-space.req @@ -0,0 +1,3 @@ +GET /example%20space/ HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-space/get-space.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-space/get-space.sreq new file mode 100644 index 000000000..7676d07d7 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-space/get-space.sreq @@ -0,0 +1,4 @@ +GET /example%20space/ HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=652487583200325589f1fba4c7e578f72c47cb61beeca81406b39ddec1366741 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-space/get-space.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-space/get-space.sts new file mode 100644 index 000000000..a633f0c05 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-space/get-space.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +63ee75631ed7234ae61b5f736dfc7754cdccfedbff4b5128a915706ee9390d86 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-special-character/get-special-character.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-special-character/get-special-character.authz new file mode 100644 index 000000000..858b601bb --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-special-character/get-special-character.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=a853c9b21b528b19643d00910d35b83a10c366a10833ceefb45edd6c80e40f27 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-special-character/get-special-character.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-special-character/get-special-character.creq new file mode 100644 index 000000000..236b8f27d --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-special-character/get-special-character.creq @@ -0,0 +1,8 @@ +GET +/example/%24delete + +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-special-character/get-special-character.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-special-character/get-special-character.req new file mode 100644 index 000000000..e657d8858 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-special-character/get-special-character.req @@ -0,0 +1,3 @@ +GET /example/$delete HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-special-character/get-special-character.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-special-character/get-special-character.sreq new file mode 100644 index 000000000..d3a607188 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-special-character/get-special-character.sreq @@ -0,0 +1,4 @@ +GET /example/$delete HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=a853c9b21b528b19643d00910d35b83a10c366a10833ceefb45edd6c80e40f27 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-special-character/get-special-character.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-special-character/get-special-character.sts new file mode 100644 index 000000000..df29bc44d --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/get-special-character/get-special-character.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +4053e45b5cef7cec5e17f736b1c12b3faf0388fd4c0bd24326386f132039ce5c \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/normalize-path.txt b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/normalize-path.txt new file mode 100644 index 000000000..caaf34fb5 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/normalize-path/normalize-path.txt @@ -0,0 +1,3 @@ +A note about signing requests to Amazon S3: + +In exception to this, you do not normalize URI paths for requests to Amazon S3. For example, if you have a bucket with an object named my-object//example//photo.user, use that path. Normalizing the path to my-object/example/photo.user will cause the request to fail. For more information, see Task 1: Create a Canonical Request in the Amazon Simple Storage Service API Reference: http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html#canonical-request \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-case/post-header-key-case.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-case/post-header-key-case.authz new file mode 100644 index 000000000..89e572e60 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-case/post-header-key-case.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-case/post-header-key-case.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-case/post-header-key-case.creq new file mode 100644 index 000000000..5c3a9434e --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-case/post-header-key-case.creq @@ -0,0 +1,8 @@ +POST +/ + +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-case/post-header-key-case.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-case/post-header-key-case.req new file mode 100644 index 000000000..3dc417901 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-case/post-header-key-case.req @@ -0,0 +1,3 @@ +POST / HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-case/post-header-key-case.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-case/post-header-key-case.sreq new file mode 100644 index 000000000..a5ada0d94 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-case/post-header-key-case.sreq @@ -0,0 +1,4 @@ +POST / HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-case/post-header-key-case.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-case/post-header-key-case.sts new file mode 100644 index 000000000..a63670394 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-case/post-header-key-case.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +553f88c9e4d10fc9e109e2aeb65f030801b70c2f6468faca261d401ae622fc87 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-sort/post-header-key-sort.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-sort/post-header-key-sort.authz new file mode 100644 index 000000000..a62589ff7 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-sort/post-header-key-sort.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c5410059b04c1ee005303aed430f6e6645f61f4dc9e1461ec8f8916fdf18852c \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-sort/post-header-key-sort.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-sort/post-header-key-sort.creq new file mode 100644 index 000000000..ebe943e89 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-sort/post-header-key-sort.creq @@ -0,0 +1,9 @@ +POST +/ + +host:example.amazonaws.com +my-header1:value1 +x-amz-date:20150830T123600Z + +host;my-header1;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-sort/post-header-key-sort.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-sort/post-header-key-sort.req new file mode 100644 index 000000000..0253f1945 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-sort/post-header-key-sort.req @@ -0,0 +1,4 @@ +POST / HTTP/1.1 +Host:example.amazonaws.com +My-Header1:value1 +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-sort/post-header-key-sort.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-sort/post-header-key-sort.sreq new file mode 100644 index 000000000..b4b78a166 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-sort/post-header-key-sort.sreq @@ -0,0 +1,5 @@ +POST / HTTP/1.1 +Host:example.amazonaws.com +My-Header1:value1 +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c5410059b04c1ee005303aed430f6e6645f61f4dc9e1461ec8f8916fdf18852c \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-sort/post-header-key-sort.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-sort/post-header-key-sort.sts new file mode 100644 index 000000000..eb6636269 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-key-sort/post-header-key-sort.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +9368318c2967cf6de74404b30c65a91e8f6253e0a8659d6d5319f1a812f87d65 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-value-case/post-header-value-case.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-value-case/post-header-value-case.authz new file mode 100644 index 000000000..d9e52a379 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-value-case/post-header-value-case.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=cdbc9802e29d2942e5e10b5bccfdd67c5f22c7c4e8ae67b53629efa58b974b7d \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-value-case/post-header-value-case.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-value-case/post-header-value-case.creq new file mode 100644 index 000000000..af824c889 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-value-case/post-header-value-case.creq @@ -0,0 +1,9 @@ +POST +/ + +host:example.amazonaws.com +my-header1:VALUE1 +x-amz-date:20150830T123600Z + +host;my-header1;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-value-case/post-header-value-case.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-value-case/post-header-value-case.req new file mode 100644 index 000000000..3f9987af7 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-value-case/post-header-value-case.req @@ -0,0 +1,4 @@ +POST / HTTP/1.1 +Host:example.amazonaws.com +My-Header1:VALUE1 +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-value-case/post-header-value-case.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-value-case/post-header-value-case.sreq new file mode 100644 index 000000000..99c3210c9 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-value-case/post-header-value-case.sreq @@ -0,0 +1,5 @@ +POST / HTTP/1.1 +Host:example.amazonaws.com +My-Header1:VALUE1 +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=cdbc9802e29d2942e5e10b5bccfdd67c5f22c7c4e8ae67b53629efa58b974b7d \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-value-case/post-header-value-case.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-value-case/post-header-value-case.sts new file mode 100644 index 000000000..40062c79f --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-header-value-case/post-header-value-case.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +d51ced243e649e3de6ef63afbbdcbca03131a21a7103a1583706a64618606a93 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-after/post-sts-header-after.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-after/post-sts-header-after.authz new file mode 100644 index 000000000..89e572e60 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-after/post-sts-header-after.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-after/post-sts-header-after.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-after/post-sts-header-after.creq new file mode 100644 index 000000000..5c3a9434e --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-after/post-sts-header-after.creq @@ -0,0 +1,8 @@ +POST +/ + +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-after/post-sts-header-after.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-after/post-sts-header-after.req new file mode 100644 index 000000000..3dc417901 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-after/post-sts-header-after.req @@ -0,0 +1,3 @@ +POST / HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-after/post-sts-header-after.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-after/post-sts-header-after.sreq new file mode 100644 index 000000000..291ed0756 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-after/post-sts-header-after.sreq @@ -0,0 +1,5 @@ +POST / HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +X-Amz-Security-Token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-after/post-sts-header-after.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-after/post-sts-header-after.sts new file mode 100644 index 000000000..a63670394 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-after/post-sts-header-after.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +553f88c9e4d10fc9e109e2aeb65f030801b70c2f6468faca261d401ae622fc87 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-before/post-sts-header-before.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-before/post-sts-header-before.authz new file mode 100644 index 000000000..64aa046db --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-before/post-sts-header-before.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=85d96828115b5dc0cfc3bd16ad9e210dd772bbebba041836c64533a82be05ead \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-before/post-sts-header-before.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-before/post-sts-header-before.creq new file mode 100644 index 000000000..1d5a462ee --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-before/post-sts-header-before.creq @@ -0,0 +1,9 @@ +POST +/ + +host:example.amazonaws.com +x-amz-date:20150830T123600Z +x-amz-security-token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== + +host;x-amz-date;x-amz-security-token +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-before/post-sts-header-before.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-before/post-sts-header-before.req new file mode 100644 index 000000000..9d917755f --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-before/post-sts-header-before.req @@ -0,0 +1,4 @@ +POST / HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +X-Amz-Security-Token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-before/post-sts-header-before.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-before/post-sts-header-before.sreq new file mode 100644 index 000000000..37b2f0419 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-before/post-sts-header-before.sreq @@ -0,0 +1,5 @@ +POST / HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +X-Amz-Security-Token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=85d96828115b5dc0cfc3bd16ad9e210dd772bbebba041836c64533a82be05ead \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-before/post-sts-header-before.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-before/post-sts-header-before.sts new file mode 100644 index 000000000..bc39ccfc5 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/post-sts-header-before/post-sts-header-before.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +c237e1b440d4c63c32ca95b5b99481081cb7b13c7e40434868e71567c1a882f6 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/readme.txt b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/readme.txt new file mode 100644 index 000000000..3731a3012 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-sts-token/readme.txt @@ -0,0 +1,15 @@ +A note about using temporary security credentials: + +You can use temporary security credentials provided by the AWS Security Token Service (AWS STS) to sign a request. The process is the same as using long-term credentials but requires an additional HTTP header or query string parameter for the security token. The name of the header or query string parameter is X-Amz-Security-Token, and the value is the session token (the string that you received from AWS STS when you obtained temporary security credentials). + +When you add X-Amz-Security-Token, some services require that you include this parameter in the canonical (signed) request. For other services, you add this parameter at the end, after you calculate the signature. For details see the API reference documentation for that service. + +The test suite has 2 examples: + +post-sts-header-before - The X-Amz-Security-Token header is part of the canonical request. + +post-sts-header-after - The X-Amz-Security-Token header is added to the request after you calculate the signature. + +The test suite uses this example value for X-Amz-Security-Token: + +AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA== \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.authz new file mode 100644 index 000000000..44280cd7b --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.creq new file mode 100644 index 000000000..f5058d430 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.creq @@ -0,0 +1,8 @@ +POST +/ +Param1=value1 +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.req new file mode 100644 index 000000000..9157bc74d --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.req @@ -0,0 +1,3 @@ +POST /?Param1=value1 HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sreq new file mode 100644 index 000000000..82af1505e --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sreq @@ -0,0 +1,4 @@ +POST /?Param1=value1 HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sts new file mode 100644 index 000000000..ca7cc661d --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +9d659678c1756bb3113e2ce898845a0a79dbbc57b740555917687f1b3340fbbd \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-query/post-vanilla-query.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-query/post-vanilla-query.authz new file mode 100644 index 000000000..44280cd7b --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-query/post-vanilla-query.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-query/post-vanilla-query.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-query/post-vanilla-query.creq new file mode 100644 index 000000000..f5058d430 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-query/post-vanilla-query.creq @@ -0,0 +1,8 @@ +POST +/ +Param1=value1 +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-query/post-vanilla-query.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-query/post-vanilla-query.req new file mode 100644 index 000000000..9157bc74d --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-query/post-vanilla-query.req @@ -0,0 +1,3 @@ +POST /?Param1=value1 HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-query/post-vanilla-query.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-query/post-vanilla-query.sreq new file mode 100644 index 000000000..82af1505e --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-query/post-vanilla-query.sreq @@ -0,0 +1,4 @@ +POST /?Param1=value1 HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-query/post-vanilla-query.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-query/post-vanilla-query.sts new file mode 100644 index 000000000..ca7cc661d --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla-query/post-vanilla-query.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +9d659678c1756bb3113e2ce898845a0a79dbbc57b740555917687f1b3340fbbd \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla/post-vanilla.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla/post-vanilla.authz new file mode 100644 index 000000000..89e572e60 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla/post-vanilla.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla/post-vanilla.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla/post-vanilla.creq new file mode 100644 index 000000000..5c3a9434e --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla/post-vanilla.creq @@ -0,0 +1,8 @@ +POST +/ + +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +host;x-amz-date +e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla/post-vanilla.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla/post-vanilla.req new file mode 100644 index 000000000..3dc417901 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla/post-vanilla.req @@ -0,0 +1,3 @@ +POST / HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla/post-vanilla.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla/post-vanilla.sreq new file mode 100644 index 000000000..a5ada0d94 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla/post-vanilla.sreq @@ -0,0 +1,4 @@ +POST / HTTP/1.1 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla/post-vanilla.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla/post-vanilla.sts new file mode 100644 index 000000000..a63670394 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-vanilla/post-vanilla.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +553f88c9e4d10fc9e109e2aeb65f030801b70c2f6468faca261d401ae622fc87 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.authz new file mode 100644 index 000000000..531b89b45 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=1a72ec8f64bd914b0e42e42607c7fbce7fb2c7465f63e3092b3b0d39fa77a6fe \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.creq new file mode 100644 index 000000000..8ec0d6cf0 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.creq @@ -0,0 +1,9 @@ +POST +/ + +content-type:application/x-www-form-urlencoded; charset=utf8 +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +content-type;host;x-amz-date +9095672bbd1f56dfc5b65f3e153adc8731a4a654192329106275f4c7b24d0b6e \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.req new file mode 100644 index 000000000..5ce537e67 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.req @@ -0,0 +1,6 @@ +POST / HTTP/1.1 +Content-Type:application/x-www-form-urlencoded; charset=utf8 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z + +Param1=value1 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sreq new file mode 100644 index 000000000..88beb82a3 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sreq @@ -0,0 +1,7 @@ +POST / HTTP/1.1 +Content-Type:application/x-www-form-urlencoded; charset=utf8 +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=1a72ec8f64bd914b0e42e42607c7fbce7fb2c7465f63e3092b3b0d39fa77a6fe + +Param1=value1 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sts new file mode 100644 index 000000000..3e83c524b --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +2e1cf7ed91881a30569e46552437e4156c823447bf1781b921b5d486c568dd1c \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.authz b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.authz new file mode 100644 index 000000000..d7baf5354 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.authz @@ -0,0 +1 @@ +AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=ff11897932ad3f4e8b18135d722051e5ac45fc38421b1da7b9d196a0fe09473a \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.creq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.creq new file mode 100644 index 000000000..d7197f17e --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.creq @@ -0,0 +1,9 @@ +POST +/ + +content-type:application/x-www-form-urlencoded +host:example.amazonaws.com +x-amz-date:20150830T123600Z + +content-type;host;x-amz-date +9095672bbd1f56dfc5b65f3e153adc8731a4a654192329106275f4c7b24d0b6e \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.req b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.req new file mode 100644 index 000000000..ada7f8776 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.req @@ -0,0 +1,6 @@ +POST / HTTP/1.1 +Content-Type:application/x-www-form-urlencoded +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z + +Param1=value1 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sreq b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sreq new file mode 100644 index 000000000..9bac9311b --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sreq @@ -0,0 +1,7 @@ +POST / HTTP/1.1 +Content-Type:application/x-www-form-urlencoded +Host:example.amazonaws.com +X-Amz-Date:20150830T123600Z +Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=ff11897932ad3f4e8b18135d722051e5ac45fc38421b1da7b9d196a0fe09473a + +Param1=value1 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sts b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sts new file mode 100644 index 000000000..65ab66371 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/aws4_testsuite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sts @@ -0,0 +1,4 @@ +AWS4-HMAC-SHA256 +20150830T123600Z +20150830/us-east-1/service/aws4_request +42a5e5bb34198acb3e84da4f085bb7927f2bc277ca766e6d19c73c2154021281 \ No newline at end of file diff --git a/packages/aws-sdk-signers/tests/unit/auth/test_sigv4.py b/packages/aws-sdk-signers/tests/unit/auth/test_sigv4.py new file mode 100644 index 000000000..49dda1269 --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/auth/test_sigv4.py @@ -0,0 +1,206 @@ +import os +import pathlib +import re +from collections.abc import Iterable +from datetime import UTC, datetime +from http.server import BaseHTTPRequestHandler +from io import BytesIO + +import pytest +from aws_sdk_signers import ( + URI, + AsyncBytesReader, + AWSCredentialIdentity, + AWSRequest, + Field, + Fields, +) +from aws_sdk_signers.exceptions import AWSSDKWarning +from aws_sdk_signers.signers import ( + SIGV4_TIMESTAMP_FORMAT, + AsyncSigV4Signer, + SigV4Signer, + SigV4SigningProperties, +) +from freezegun import freeze_time + +SECRET_KEY: str = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY" +ACCESS_KEY: str = "AKIDEXAMPLE" +SERVICE: str = "service" +REGION: str = "us-east-1" + +DATE: datetime = datetime( + year=2015, month=8, day=30, hour=12, minute=36, second=0, tzinfo=UTC +) +DATE_STR: str = DATE.strftime(SIGV4_TIMESTAMP_FORMAT) + +TESTSUITE_DIR: pathlib.Path = ( + pathlib.Path(__file__).absolute().parent / "aws4_testsuite" +) + + +class RawRequest(BaseHTTPRequestHandler): + def __init__(self, raw_request: bytes): + self.rfile = BytesIO(initial_bytes=raw_request) + self.raw_requestline: bytes = self.rfile.readline() + self.error_code: int | None = None + self.error_message: str | None = None + self.parse_request() + + def send_error( + self, code: int, message: str | None = None, explain: str | None = None + ) -> None: + self.error_code = code + self.error_message = message + + +class SignatureTestCase: + def __init__(self, test_case: str) -> None: + self.name: str = os.path.basename(test_case) + base_path: pathlib.Path = TESTSUITE_DIR / test_case + + self.raw_request: bytes = (base_path / f"{self.name}.req").read_bytes() + self.canonical_request: str = (base_path / f"{self.name}.creq").read_text() + self.string_to_sign: str = (base_path / f"{self.name}.sts").read_text() + self.authorization_header: str = (base_path / f"{self.name}.authz").read_text() + self.signed_request: str = (base_path / f"{self.name}.sreq").read_text() + self.credentials: AWSCredentialIdentity = AWSCredentialIdentity( + access_key_id=ACCESS_KEY, + secret_access_key=SECRET_KEY, + session_token=self.get_token(), + ) + + def get_token(self) -> str | None: + token_pattern = r"^x-amz-security-token:(.*)$" + token_match = re.search(token_pattern, self.canonical_request, re.MULTILINE) + return token_match.group(1) if token_match else None + + +def generate_test_cases() -> Iterable[str]: + for dirpath, _, filenames in os.walk(TESTSUITE_DIR): + # Skip over tests without a request file + if not any(f.endswith(".req") for f in filenames): + continue + + test_case_name = os.path.relpath(dirpath, TESTSUITE_DIR).replace(os.sep, "/") + + yield test_case_name + + +@pytest.mark.parametrize("test_case_name", generate_test_cases()) +@freeze_time("2015-08-30 12:36:00") +def test_signature_version_4_sync(test_case_name: str) -> None: + signer = SigV4Signer() + _test_signature_version_4_sync(test_case_name, signer) + + +def _test_signature_version_4_sync(test_case_name: str, signer: SigV4Signer) -> None: + test_case = SignatureTestCase(test_case_name) + request = create_request_from_raw_request(test_case) + + signing_props = SigV4SigningProperties( + region=REGION, + service=SERVICE, + date=DATE_STR, + ) + with pytest.warns(AWSSDKWarning): + actual_canonical_request = signer.canonical_request( + signing_properties=signing_props, request=request + ) + assert test_case.canonical_request == actual_canonical_request + actual_string_to_sign = signer.string_to_sign( + canonical_request=actual_canonical_request, signing_properties=signing_props + ) + assert test_case.string_to_sign == actual_string_to_sign + with pytest.warns(AWSSDKWarning): + signed_request = signer.sign( + signing_properties=signing_props, + request=request, + identity=test_case.credentials, + ) + assert ( + signed_request.fields["Authorization"].as_string() + == test_case.authorization_header + ) + + +@pytest.mark.parametrize("test_case_name", generate_test_cases()) +@freeze_time("2015-08-30 12:36:00") +async def test_signature_version_4_async(test_case_name: str) -> None: + signer = AsyncSigV4Signer() + await _test_signature_version_4_async(test_case_name, signer) + + +async def _test_signature_version_4_async( + test_case_name: str, signer: AsyncSigV4Signer +) -> None: + test_case = SignatureTestCase(test_case_name) + request = create_request_from_raw_request(test_case, async_body=True) + + signing_props = SigV4SigningProperties( + region=REGION, + service=SERVICE, + date=DATE_STR, + ) + with pytest.warns(AWSSDKWarning): + actual_canonical_request = await signer.canonical_request( + signing_properties=signing_props, request=request + ) + assert test_case.canonical_request == actual_canonical_request + actual_string_to_sign = await signer.string_to_sign( + canonical_request=actual_canonical_request, signing_properties=signing_props + ) + assert test_case.string_to_sign == actual_string_to_sign + with pytest.warns(AWSSDKWarning): + signed_request = await signer.sign( + signing_properties=signing_props, + request=request, + identity=test_case.credentials, + ) + assert ( + signed_request.fields["Authorization"].as_string() + == test_case.authorization_header + ) + + +def create_request_from_raw_request( + test_case: SignatureTestCase, async_body: bool = False +) -> AWSRequest: + raw = RawRequest(raw_request=test_case.raw_request) + if raw.error_code is not None: + raise Exception(raw.error_message) + + request_method = raw.command + fields = Fields() + for k, v in raw.headers.items(): + if k in fields: + fields[k].add(value=v) + else: + field = Field(name=k, values=[v]) + fields.set_field(field=field) + fields.set_field(Field(name="X-Amz-Date", values=[DATE_STR])) + if test_case.credentials.session_token is not None: + fields.set_field( + Field( + name="X-Amz-Security-Token", + values=[test_case.credentials.session_token], + ) + ) + body = raw.rfile + if async_body: + body = AsyncBytesReader(raw.rfile) + # BaseHTTPRequestHandler encodes the first line of the request + # as 'iso-8859-1', so we need to decode this into utf-8. + path = raw.path.encode(encoding="iso-8859-1").decode(encoding="utf-8") + if "?" in path: + path, query = path.split(sep="?", maxsplit=1) + else: + query = "" + host = raw.headers.get("host", "") + url = URI(host=host, path=path, query=query) + return AWSRequest( + destination=url, + method=request_method, + fields=fields, + body=body, + ) diff --git a/packages/aws-sdk-signers/tests/unit/test_fields.py b/packages/aws-sdk-signers/tests/unit/test_fields.py new file mode 100644 index 000000000..2731f100d --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/test_fields.py @@ -0,0 +1,316 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +import pytest +from aws_sdk_signers import Field, Fields +from aws_sdk_signers.interfaces.http import FieldPosition + + +def test_field_single_valued_basics() -> None: + field = Field(name="fname", values=["fval"], kind=FieldPosition.HEADER) + assert field.name == "fname" + assert field.kind == FieldPosition.HEADER + assert field.values == ["fval"] + assert field.as_string() == "fval" + assert field.as_tuples() == [("fname", "fval")] + + +def test_field_multi_valued_basics() -> None: + field = Field(name="fname", values=["fval1", "fval2"], kind=FieldPosition.HEADER) + assert field.name == "fname" + assert field.kind == FieldPosition.HEADER + assert field.values == ["fval1", "fval2"] + assert field.as_string() == "fval1,fval2" + assert field.as_tuples() == [("fname", "fval1"), ("fname", "fval2")] + + +@pytest.mark.parametrize( + "values,expected", + [ + # Single-valued fields are serialized without any quoting or escaping. + (["val1"], "val1"), + (['"val1"'], '"val1"'), + (['"'], '"'), + (['val"1'], 'val"1'), + (["val\\1"], "val\\1"), + # Multi-valued fields are joined with one comma and one space as separator. + (["val1", "val2"], "val1,val2"), + (["val1", "val2", "val3", "val4"], "val1,val2,val3,val4"), + (["©väl", "val2"], "©väl,val2"), + # Values containing commas must be double-quoted. + (["val1", "val2,val3", "val4"], 'val1,"val2,val3",val4'), + (["v,a,l,1", "val2"], '"v,a,l,1",val2'), + # In strings that get quoted, pre-existing double quotes are escaped with a + # single backslash. The second backslash below is for escaping the actual + # backslash in the string for Python. + (["slc", '4,196"'], 'slc,"4,196\\""'), + (['"val1"', "val2"], '"\\"val1\\"",val2'), + (["val1", '"'], 'val1,"\\""'), + (['val1:2",val3:4"', "val5"], '"val1:2\\",val3:4\\"",val5'), + # If quoting happens, backslashes are also escaped. The following case is a + # single backslash getting serialized into two backslashes. Python escaping + # accounts for each actual backslash being written as two. + (["foo,bar\\", "val2"], '"foo,bar\\\\",val2'), + ], +) +def test_field_serialization(values: list[str], expected: str) -> None: + field = Field(name="_", values=values) + assert field.as_string() == expected + + +@pytest.mark.parametrize( + "field,expected_repr", + [ + ( + Field(name="fname", values=["fval1", "fval2"], kind=FieldPosition.HEADER), + "Field(name='fname', value=['fval1', 'fval2'], kind=)", + ), + ( + Field(name="fname", kind=FieldPosition.TRAILER), + "Field(name='fname', value=[], kind=)", + ), + ( + Field(name="fname"), + "Field(name='fname', value=[], kind=)", + ), + ], +) +def test_field_repr(field: Field, expected_repr: str) -> None: + assert repr(field) == expected_repr + + +@pytest.mark.parametrize( + "f1,f2", + [ + ( + Field(name="fname", values=["fval1", "fval2"], kind=FieldPosition.TRAILER), + Field(name="fname", values=["fval1", "fval2"], kind=FieldPosition.TRAILER), + ), + ( + Field(name="fname", values=["fval1", "fval2"]), + Field(name="fname", values=["fval1", "fval2"]), + ), + ( + Field(name="fname"), + Field(name="fname"), + ), + ], +) +def test_field_equality(f1: Field, f2: Field) -> None: + assert f1 == f2 + + +@pytest.mark.parametrize( + "f1,f2", + [ + ( + Field(name="fname", values=["fval1", "fval2"], kind=FieldPosition.HEADER), + Field(name="fname", values=["fval1", "fval2"], kind=FieldPosition.TRAILER), + ), + ( + Field(name="fname", values=["fval1", "fval2"], kind=FieldPosition.HEADER), + Field(name="fname", values=["fval2", "fval1"], kind=FieldPosition.HEADER), + ), + ( + Field(name="fname", values=["fval1", "fval2"], kind=FieldPosition.HEADER), + Field(name="fname", values=["fval1"], kind=FieldPosition.HEADER), + ), + ( + Field(name="fname1", values=["fval1", "fval2"], kind=FieldPosition.HEADER), + Field(name="fname2", values=["fval1", "fval2"], kind=FieldPosition.HEADER), + ), + ], +) +def test_field_inqueality(f1: Field, f2: Field) -> None: + assert f1 != f2 + + +@pytest.mark.parametrize( + "fs1,fs2", + [ + ( + Fields([Field(name="fname", values=["fval1", "fval2"])]), + Fields([Field(name="fname", values=["fval1", "fval2"])]), + ), + ], +) +def test_fields_equality(fs1: Fields, fs2: Fields) -> None: + assert fs1 == fs2 + + +@pytest.mark.parametrize( + "fs1,fs2", + [ + ( + Fields(), + Fields([Field(name="fname")]), + ), + ( + Fields([Field(name="fname1")]), + Fields([Field(name="fname2")]), + ), + ( + Fields(encoding="utf-1"), + Fields(encoding="utf-2"), + ), + ( + Fields([Field(name="fname", values=["val1"])]), + Fields([Field(name="fname", values=["val2"])]), + ), + ( + Fields([Field(name="fname", values=["val2", "val1"])]), + Fields([Field(name="fname", values=["val1", "val2"])]), + ), + ( + Fields([Field(name="f1"), Field(name="f2")]), + Fields([Field(name="f2"), Field(name="f1")]), + ), + ], +) +def test_fields_inequality(fs1: Fields, fs2: Fields) -> None: + assert fs1 != fs2 + + +@pytest.mark.parametrize( + "initial_fields", + [ + [ + Field(name="fname1", values=["val1"]), + Field(name="fname1", values=["val2"]), + ], + # uniqueness is checked _after_ normaling field names + [ + Field(name="fNaMe1", values=["val1"]), + Field(name="fname1", values=["val2"]), + ], + ], +) +def test_repeated_initial_field_names(initial_fields: list[Field]) -> None: + with pytest.raises(ValueError): + Fields(initial_fields) + + +@pytest.mark.parametrize( + "fields,expected_length", + [ + (Fields(), 0), + (Fields([Field(name="fname1")]), 1), + (Fields(encoding="utf-1"), 0), + (Fields([Field(name="fname", values=["val2", "val1"])]), 1), + (Fields([Field(name="f1"), Field(name="f2")]), 2), + ], +) +def test_fields_length_value(fields: Fields, expected_length: int) -> None: + assert len(fields) == expected_length + + +@pytest.mark.parametrize( + "fields,expected_repr", + [ + ( + Fields([Field(name="fname1")]), + ( + "Fields(OrderedDict({'fname1': Field(name='fname1', value=[], " + "kind=)}))" + ), + ), + ], +) +def test_fields_repr(fields: Field, expected_repr: str) -> None: + assert repr(fields) == expected_repr + + +@pytest.mark.parametrize( + "fields,key,contained", + [ + (Fields(), "bad_key", False), + (Fields([Field(name="fname1")]), "FNAME1", True), + (Fields([Field(name="fname1")]), "fname1", True), + (Fields([Field(name="fname2")]), "fname1", False), + (Fields([Field(name="f1"), Field(name="f2")]), "f1", True), + (Fields([Field(name="f1"), Field(name="f2")]), "f3", False), + ], +) +def test_fields_contains(fields: Fields, key: str, contained: bool) -> None: + assert (key in fields) is contained + + +@pytest.mark.parametrize( + "fields,key,expected", + [ + (Fields(), "bad_key", None), + (Fields([Field(name="fname1")]), "FNAME1", Field(name="fname1")), + (Fields([Field(name="fname1")]), "fname1", Field(name="fname1")), + (Fields([Field(name="fname2")]), "fname1", None), + (Fields([Field(name="f1"), Field(name="f2")]), "f1", Field(name="f1")), + (Fields([Field(name="f1"), Field(name="f2")]), "f2", Field(name="f2")), + (Fields([Field(name="f1"), Field(name="f2")]), "f3", None), + ], +) +def test_fields_getitem(fields: Fields, key: str, expected: Field | None) -> None: + assert fields.get(key) == expected + + +def test_fields_get_index() -> None: + fields = Fields([Field(name="f1"), Field(name="f2")]) + assert fields["f1"] == Field(name="f1") + + +def test_fields_get_missing_index() -> None: + fields = Fields([Field(name="fname1")]) + with pytest.raises(KeyError): + fields["fname2"] + + +@pytest.mark.parametrize( + "fields,field", + [ + (Fields(), Field(name="fname1")), + (Fields([Field(name="fname1", values=["1", "2"])]), Field(name="fname1")), + (Fields([Field(name="f1"), Field(name="f2")]), Field(name="f3")), + ], +) +def test_fields_setitem(fields: Fields, field: Field) -> None: + fields[field.name] = field + assert field.name in fields + assert fields[field.name] == field + + +@pytest.mark.parametrize( + "fields,field", + [ + (Fields(), Field(name="fname1")), + (Fields([Field(name="fname1", values=["1", "2"])]), Field(name="fname1")), + (Fields([Field(name="f1"), Field(name="f2")]), Field(name="f3")), + ], +) +def test_fields_set_field(fields: Fields, field: Field) -> None: + fields.set_field(field) + assert field.name in fields + assert fields[field.name] == field + + +@pytest.mark.parametrize( + "fields,field_name,expected_keys", + [ + (Fields([Field(name="fname1", values=["1", "2"])]), "fname1", []), + (Fields([Field(name="f1"), Field(name="f2")]), "f2", ["f1"]), + ], +) +def test_fields_delitem( + fields: Fields, field_name: str, expected_keys: list[str] +) -> None: + assert field_name in fields + del fields[field_name] + assert field_name not in fields + + # Ensure we don't delete anything unexpected + assert len(fields) == len(expected_keys) + for key in expected_keys: + assert key in fields + + +def test_fields_delitem_missing() -> None: + fields = Fields([Field(name="fname1")]) + with pytest.raises(KeyError): + del fields["fname2"] diff --git a/packages/aws-sdk-signers/tests/unit/test_identity.py b/packages/aws-sdk-signers/tests/unit/test_identity.py new file mode 100644 index 000000000..f09c99dee --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/test_identity.py @@ -0,0 +1,93 @@ +from datetime import UTC, datetime, timedelta + +import pytest +from aws_sdk_signers import AWSCredentialIdentity + + +@pytest.mark.parametrize( + "access_key_id,secret_access_key,session_token,expiration", + [ + ( + "AKID1234EXAMPLE", + "SECRET1234", + None, + None, + ), + ( + "AKID1234EXAMPLE", + "SECRET1234", + "SESS_TOKEN_1234", + None, + ), + ( + "AKID1234EXAMPLE", + "SECRET1234", + None, + datetime(2024, 5, 1, 0, 0, 0, tzinfo=UTC), + ), + ( + "AKID1234EXAMPLE", + "SECRET1234", + "SESS_TOKEN_1234", + datetime(2024, 5, 1, 0, 0, 0, tzinfo=UTC), + ), + ], +) +def test_aws_credential_identity( + access_key_id: str, + secret_access_key: str, + session_token: str | None, + expiration: datetime | None, +) -> None: + creds = AWSCredentialIdentity( + access_key_id=access_key_id, + secret_access_key=secret_access_key, + session_token=session_token, + expiration=expiration, + ) + assert creds.access_key_id == access_key_id + assert creds.secret_access_key == secret_access_key + assert creds.session_token == session_token + assert creds.expiration == expiration + + +@pytest.mark.parametrize( + "access_key_id,secret_access_key,session_token,expiration,is_expired", + [ + ( + "AKID1234EXAMPLE", + "SECRET1234", + "SESS_TOKEN_1234", + None, + False, + ), + ( + "AKID1234EXAMPLE", + "SECRET1234", + None, + datetime(2024, 5, 1, 0, 0, 0, tzinfo=UTC), + True, + ), + ( + "AKID1234EXAMPLE", + "SECRET1234", + "SESS_TOKEN_1234", + datetime.now(UTC) + timedelta(hours=1), + False, + ), + ], +) +def test_aws_credential_identity_expired( + access_key_id: str, + secret_access_key: str, + session_token: str | None, + expiration: datetime | None, + is_expired: bool, +) -> None: + creds = AWSCredentialIdentity( + access_key_id=access_key_id, + secret_access_key=secret_access_key, + session_token=session_token, + expiration=expiration, + ) + assert creds.is_expired is is_expired diff --git a/packages/aws-sdk-signers/tests/unit/test_signers.py b/packages/aws-sdk-signers/tests/unit/test_signers.py new file mode 100644 index 000000000..3b70c97db --- /dev/null +++ b/packages/aws-sdk-signers/tests/unit/test_signers.py @@ -0,0 +1,158 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +import re +import typing +from datetime import UTC, datetime +from io import BytesIO + +import pytest +from aws_sdk_signers import ( + URI, + AsyncSigV4Signer, + AWSCredentialIdentity, + AWSRequest, + Fields, + SigV4Signer, + SigV4SigningProperties, +) + +SIGV4_RE = re.compile( + r"AWS4-HMAC-SHA256 " + r"Credential=(?P\w+)/\d+/" + r"(?P[a-z0-9-]+)/" +) + + +@pytest.fixture(scope="module") +def aws_identity() -> AWSCredentialIdentity: + return AWSCredentialIdentity( + access_key_id="AKID123456", + secret_access_key="EXAMPLE1234SECRET", + session_token="X123456SESSION", + ) + + +@pytest.fixture(scope="module") +def signing_properties() -> SigV4SigningProperties: + return SigV4SigningProperties( + region="us-west-2", + service="ec2", + payload_signing_enabled=False, + ) + + +@pytest.fixture(scope="module") +def aws_request() -> AWSRequest: + return AWSRequest( + destination=URI( + scheme="https", + host="127.0.0.1", + port=8000, + ), + method="GET", + body=BytesIO(b"123456"), + fields=Fields({}), + ) + + +class TestSigV4Signer: + SIGV4_SYNC_SIGNER = SigV4Signer() + + def test_sign( + self, + aws_identity: AWSCredentialIdentity, + aws_request: AWSRequest, + signing_properties: SigV4SigningProperties, + ) -> None: + signed_request = self.SIGV4_SYNC_SIGNER.sign( + signing_properties=signing_properties, + request=aws_request, + identity=aws_identity, + ) + assert isinstance(signed_request, AWSRequest) + assert signed_request is not aws_request + assert "authorization" in signed_request.fields + authorization_field = signed_request.fields["authorization"] + assert SIGV4_RE.match(authorization_field.as_string()) + + @typing.no_type_check + def test_sign_with_invalid_identity( + self, aws_request: AWSRequest, signing_properties: SigV4SigningProperties + ) -> None: + """Ignore typing as we're testing an invalid input state.""" + identity = object() + assert not isinstance(identity, AWSCredentialIdentity) + with pytest.raises(ValueError): + self.SIGV4_SYNC_SIGNER.sign( + signing_properties=signing_properties, + request=aws_request, + identity=identity, + ) + + def test_sign_with_expired_identity( + self, aws_request: AWSRequest, signing_properties: SigV4SigningProperties + ) -> None: + identity = AWSCredentialIdentity( + access_key_id="AKID123456", + secret_access_key="EXAMPLE1234SECRET", + session_token="X123456SESSION", + expiration=datetime(1970, 1, 1, tzinfo=UTC), + ) + with pytest.raises(ValueError): + self.SIGV4_SYNC_SIGNER.sign( + signing_properties=signing_properties, + request=aws_request, + identity=identity, + ) + + +class TestAsyncSigV4Signer: + SIGV4_ASYNC_SIGNER = AsyncSigV4Signer() + + async def test_sign( + self, + aws_identity: AWSCredentialIdentity, + aws_request: AWSRequest, + signing_properties: SigV4SigningProperties, + ) -> None: + signed_request = await self.SIGV4_ASYNC_SIGNER.sign( + signing_properties=signing_properties, + request=aws_request, + identity=aws_identity, + ) + assert isinstance(signed_request, AWSRequest) + assert signed_request is not aws_request + assert "authorization" in signed_request.fields + authorization_field = signed_request.fields["authorization"] + assert SIGV4_RE.match(authorization_field.as_string()) + + @typing.no_type_check + async def test_sign_with_invalid_identity( + self, aws_request: AWSRequest, signing_properties: SigV4SigningProperties + ) -> None: + """Ignore typing as we're testing an invalid input state.""" + identity = object() + assert not isinstance(identity, AWSCredentialIdentity) + with pytest.raises(ValueError): + await self.SIGV4_ASYNC_SIGNER.sign( + signing_properties=signing_properties, + request=aws_request, + identity=identity, + ) + + async def test_sign_with_expired_identity( + self, aws_request: AWSRequest, signing_properties: SigV4SigningProperties + ) -> None: + identity = AWSCredentialIdentity( + access_key_id="AKID123456", + secret_access_key="EXAMPLE1234SECRET", + session_token="X123456SESSION", + expiration=datetime(1970, 1, 1, tzinfo=UTC), + ) + with pytest.raises(ValueError): + await self.SIGV4_ASYNC_SIGNER.sign( + signing_properties=signing_properties, + request=aws_request, + identity=identity, + ) diff --git a/packages/smithy-http/src/smithy_http/__init__.py b/packages/smithy-http/src/smithy_http/__init__.py index 90869db71..826be5fcb 100644 --- a/packages/smithy-http/src/smithy_http/__init__.py +++ b/packages/smithy-http/src/smithy_http/__init__.py @@ -44,8 +44,8 @@ def remove(self, value: str) -> None: except ValueError: return - def as_string(self) -> str: - """Get comma-delimited string of all values. + def as_string(self, delimiter: str = ",") -> str: + """Get delimited string of all values. A comma is used by default. If the ``Field`` has zero values, the empty string is returned. If the ``Field`` has exactly one value, the value is returned unmodified. diff --git a/packages/smithy-http/src/smithy_http/aio/aiohttp.py b/packages/smithy-http/src/smithy_http/aio/aiohttp.py index 6ac0464a4..ad5f19f6b 100644 --- a/packages/smithy-http/src/smithy_http/aio/aiohttp.py +++ b/packages/smithy-http/src/smithy_http/aio/aiohttp.py @@ -78,10 +78,7 @@ async def send( request_config = request_config or HTTPRequestConfiguration() headers_list = list( - chain.from_iterable( - fld.as_tuples() - for fld in request.fields.get_by_type(FieldPosition.HEADER) - ) + chain.from_iterable(fld.as_tuples() for fld in request.fields) ) body: StreamingBlob = request.body diff --git a/pyproject.toml b/pyproject.toml index 6bdb5f1b1..7c21adcba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,7 @@ smithy_http = { workspace = true } smithy_json = { workspace = true } smithy_aws_core = { workspace = true } aws_event_stream = { workspace = true } +aws_sdk_signers = {workspace = true } [tool.pyright] typeCheckingMode = "strict" diff --git a/uv.lock b/uv.lock index e204ab079..41cf3b3c8 100644 --- a/uv.lock +++ b/uv.lock @@ -5,6 +5,7 @@ requires-python = ">=3.12" [manifest] members = [ "aws-event-stream", + "aws-sdk-signers", "smithy-aws-core", "smithy-core", "smithy-event-stream", @@ -107,6 +108,29 @@ requires-dist = [ { name = "smithy-event-stream", editable = "packages/smithy-event-stream" }, ] +[[package]] +name = "aws-sdk-signers" +source = { editable = "packages/aws-sdk-signers" } + +[package.optional-dependencies] +test = [ + { name = "freezegun" }, + { name = "mypy" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "ruff" }, +] + +[package.metadata] +requires-dist = [ + { name = "freezegun", marker = "extra == 'test'" }, + { name = "mypy", marker = "extra == 'test'" }, + { name = "pytest", marker = "extra == 'test'" }, + { name = "pytest-asyncio", marker = "extra == 'test'" }, + { name = "ruff", marker = "extra == 'test'" }, +] +provides-extras = ["test"] + [[package]] name = "awscrt" version = "0.23.10" @@ -388,6 +412,31 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/99/b7/b9e70fde2c0f0c9af4cc5277782a89b66d35948ea3369ec9f598358c3ac5/multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506", size = 10051 }, ] +[[package]] +name = "mypy" +version = "1.15.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mypy-extensions" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/43/d5e49a86afa64bd3839ea0d5b9c7103487007d728e1293f52525d6d5486a/mypy-1.15.0.tar.gz", hash = "sha256:404534629d51d3efea5c800ee7c42b72a6554d6c400e6a79eafe15d11341fd43", size = 3239717 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/98/3a/03c74331c5eb8bd025734e04c9840532226775c47a2c39b56a0c8d4f128d/mypy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:aea39e0583d05124836ea645f412e88a5c7d0fd77a6d694b60d9b6b2d9f184fd", size = 10793981 }, + { url = "https://files.pythonhosted.org/packages/f0/1a/41759b18f2cfd568848a37c89030aeb03534411eef981df621d8fad08a1d/mypy-1.15.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f2147ab812b75e5b5499b01ade1f4a81489a147c01585cda36019102538615f", size = 9749175 }, + { url = "https://files.pythonhosted.org/packages/12/7e/873481abf1ef112c582db832740f4c11b2bfa510e829d6da29b0ab8c3f9c/mypy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ce436f4c6d218a070048ed6a44c0bbb10cd2cc5e272b29e7845f6a2f57ee4464", size = 11455675 }, + { url = "https://files.pythonhosted.org/packages/b3/d0/92ae4cde706923a2d3f2d6c39629134063ff64b9dedca9c1388363da072d/mypy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8023ff13985661b50a5928fc7a5ca15f3d1affb41e5f0a9952cb68ef090b31ee", size = 12410020 }, + { url = "https://files.pythonhosted.org/packages/46/8b/df49974b337cce35f828ba6fda228152d6db45fed4c86ba56ffe442434fd/mypy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1124a18bc11a6a62887e3e137f37f53fbae476dc36c185d549d4f837a2a6a14e", size = 12498582 }, + { url = "https://files.pythonhosted.org/packages/13/50/da5203fcf6c53044a0b699939f31075c45ae8a4cadf538a9069b165c1050/mypy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:171a9ca9a40cd1843abeca0e405bc1940cd9b305eaeea2dda769ba096932bb22", size = 9366614 }, + { url = "https://files.pythonhosted.org/packages/6a/9b/fd2e05d6ffff24d912f150b87db9e364fa8282045c875654ce7e32fffa66/mypy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:93faf3fdb04768d44bf28693293f3904bbb555d076b781ad2530214ee53e3445", size = 10788592 }, + { url = "https://files.pythonhosted.org/packages/74/37/b246d711c28a03ead1fd906bbc7106659aed7c089d55fe40dd58db812628/mypy-1.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:811aeccadfb730024c5d3e326b2fbe9249bb7413553f15499a4050f7c30e801d", size = 9753611 }, + { url = "https://files.pythonhosted.org/packages/a6/ac/395808a92e10cfdac8003c3de9a2ab6dc7cde6c0d2a4df3df1b815ffd067/mypy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:98b7b9b9aedb65fe628c62a6dc57f6d5088ef2dfca37903a7d9ee374d03acca5", size = 11438443 }, + { url = "https://files.pythonhosted.org/packages/d2/8b/801aa06445d2de3895f59e476f38f3f8d610ef5d6908245f07d002676cbf/mypy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c43a7682e24b4f576d93072216bf56eeff70d9140241f9edec0c104d0c515036", size = 12402541 }, + { url = "https://files.pythonhosted.org/packages/c7/67/5a4268782eb77344cc613a4cf23540928e41f018a9a1ec4c6882baf20ab8/mypy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:baefc32840a9f00babd83251560e0ae1573e2f9d1b067719479bfb0e987c6357", size = 12494348 }, + { url = "https://files.pythonhosted.org/packages/83/3e/57bb447f7bbbfaabf1712d96f9df142624a386d98fb026a761532526057e/mypy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b9378e2c00146c44793c98b8d5a61039a048e31f429fb0eb546d93f4b000bedf", size = 9373648 }, + { url = "https://files.pythonhosted.org/packages/09/4e/a7d65c7322c510de2c409ff3828b03354a7c43f5a8ed458a7a131b41c7b9/mypy-1.15.0-py3-none-any.whl", hash = "sha256:5469affef548bd1895d86d3bf10ce2b44e33d86923c29e4d675b3e323437ea3e", size = 2221777 }, +] + [[package]] name = "mypy-extensions" version = "1.0.0"