From e472826dcc0faed74fdb72a1c62622fa9a93d249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Rafael=20Siqueira?= Date: Mon, 14 Apr 2025 10:49:28 -0300 Subject: [PATCH] add reset password --- http-api/app.py | 4 +- http-api/cognito.py | 37 +++++ http-api/middlewares/__init__.py | 4 +- http-api/middlewares/audit_log_middleware.py | 15 +- ...leware.py => authentication_middleware.py} | 2 +- http-api/middlewares/tenant_middelware.py | 2 +- http-api/routes/enrollments/__init__.py | 2 +- http-api/routes/users/__init__.py | 35 ++++- http-api/tests/routes/test_courses.py | 2 +- http-api/tests/routes/test_orgs.py | 10 +- http-api/tests/routes/test_users.py | 11 +- http-api/tests/test_middelwares.py | 4 +- http-api/user.py | 2 +- layercake/Makefile | 3 + layercake/layercake/dynamodb.py | 6 +- layercake/pyproject.toml | 2 +- layercake/uv.lock | 143 +++++++++++++++--- 17 files changed, 228 insertions(+), 56 deletions(-) rename http-api/middlewares/{authorizer_middleware.py => authentication_middleware.py} (96%) diff --git a/http-api/app.py b/http-api/app.py index 7f1359a..f16c4d6 100644 --- a/http-api/app.py +++ b/http-api/app.py @@ -12,7 +12,7 @@ from aws_lambda_powertools.event_handler.exceptions import ServiceError from aws_lambda_powertools.logging import correlation_paths from aws_lambda_powertools.utilities.typing import LambdaContext -from middlewares import AuthorizerMiddleware +from middlewares import AuthenticationMiddleware from routes import courses, enrollments, lookup, orders, orgs, settings, users, webhooks tracer = Tracer() @@ -28,7 +28,7 @@ app = APIGatewayHttpResolver( cors=cors, debug='AWS_SAM_LOCAL' in os.environ, ) -app.use(middlewares=[AuthorizerMiddleware()]) +app.use(middlewares=[AuthenticationMiddleware()]) app.include_router(courses.router, prefix='/courses') app.include_router(enrollments.router, prefix='/enrollments') app.include_router(orders.router, prefix='/orders') diff --git a/http-api/cognito.py b/http-api/cognito.py index b9f96d0..4b182e1 100644 --- a/http-api/cognito.py +++ b/http-api/cognito.py @@ -36,3 +36,40 @@ def admin_get_user( return None else: return user + + +def admin_set_user_password( + username: str, + password: str, + *, + user_pool_id: str, + permanent: bool = False, + idp_client, +) -> bool: + """Sets the specified user's password in a user pool as an administrator. + Works on any user. + + The password can be temporary or permanent. If it is temporary, the user + status enters the FORCE_CHANGE_PASSWORD state. + + When the user next tries to sign in, the InitiateAuth/AdminInitiateAuth + response will contain the NEW_PASSWORD_REQUIRED challenge. + + If the user doesn't sign in before it expires, the user won't be able + to sign in, and an administrator must reset their password. + + Once the user has set a new password, or the password is permanent, + the user status is set to Confirmed. + """ + try: + idp_client.admin_set_user_password( + UserPoolId=user_pool_id, + Username=username, + Password=password, + Permanent=permanent, + ) + except idp_client.exceptions as err: + logger.exception(err) + return False + else: + return True diff --git a/http-api/middlewares/__init__.py b/http-api/middlewares/__init__.py index c7d342e..4096e56 100644 --- a/http-api/middlewares/__init__.py +++ b/http-api/middlewares/__init__.py @@ -1,9 +1,9 @@ from .audit_log_middleware import AuditLogMiddleware -from .authorizer_middleware import AuthorizerMiddleware, User +from .authentication_middleware import AuthenticationMiddleware, User from .tenant_middelware import Tenant, TenantMiddleware __all__ = [ - 'AuthorizerMiddleware', + 'AuthenticationMiddleware', 'AuditLogMiddleware', 'TenantMiddleware', 'User', diff --git a/http-api/middlewares/audit_log_middleware.py b/http-api/middlewares/audit_log_middleware.py index 6ec2573..ea80f56 100644 --- a/http-api/middlewares/audit_log_middleware.py +++ b/http-api/middlewares/audit_log_middleware.py @@ -17,7 +17,7 @@ from layercake.dynamodb import ( ) from layercake.funcs import pick -from .authorizer_middleware import User +from .authentication_middleware import User YEAR_DAYS = 365 LOG_RETENTION_DAYS = YEAR_DAYS * 2 @@ -29,14 +29,14 @@ class AuditLogMiddleware(BaseMiddlewareHandler): Parameters ---------- - action : str + action: str The identifier for the audit log action. - collect : DynamoDBCollection + collect: DynamoDBCollection The collection instance used to persist the audit log data. - audit_attrs : tuple of str, optional + audit_attrs: tuple of str, optional A tuple of attribute names to extract from the response body for logging. These represent the specific fields to include in the audit log. - retention_days : int or None, optional + retention_days: int or None, optional The number of days the log is retained on the server. If None, no time-to-live (TTL) will be applied. """ @@ -64,10 +64,13 @@ class AuditLogMiddleware(BaseMiddlewareHandler): ip_addr = req_context.http.source_ip response = next_middleware(app) + print(app.context['_route']) + # Successful response # https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status#successful_responses if 200 <= response.status_code < 300 and user: now_ = now() + author = pick(('id', 'name'), dict(user)) data = ( pick(self.audit_attrs, extract_event_from_common_models(response.body)) if response.is_json() @@ -89,7 +92,7 @@ class AuditLogMiddleware(BaseMiddlewareHandler): action=self.action, data=data, ip=ip_addr, - author='himself', + author=author, ttl=retention_days, ) diff --git a/http-api/middlewares/authorizer_middleware.py b/http-api/middlewares/authentication_middleware.py similarity index 96% rename from http-api/middlewares/authorizer_middleware.py rename to http-api/middlewares/authentication_middleware.py index 0686260..7182e26 100644 --- a/http-api/middlewares/authorizer_middleware.py +++ b/http-api/middlewares/authentication_middleware.py @@ -23,7 +23,7 @@ class CognitoUser(User): sub: UUID4 -class AuthorizerMiddleware(BaseMiddlewareHandler): +class AuthenticationMiddleware(BaseMiddlewareHandler): """This middleware extracts user authentication details from the Lambda authorizer context and makes them available in the application context.""" diff --git a/http-api/middlewares/tenant_middelware.py b/http-api/middlewares/tenant_middelware.py index 924c9d1..0018566 100644 --- a/http-api/middlewares/tenant_middelware.py +++ b/http-api/middlewares/tenant_middelware.py @@ -18,7 +18,7 @@ from pydantic import UUID4, BaseModel from auth import AuthFlowType -from .authorizer_middleware import User +from .authentication_middleware import User class Tenant(BaseModel): diff --git a/http-api/routes/enrollments/__init__.py b/http-api/routes/enrollments/__init__.py index 37c1b21..9085eb7 100644 --- a/http-api/routes/enrollments/__init__.py +++ b/http-api/routes/enrollments/__init__.py @@ -15,7 +15,7 @@ import elastic from boto3clients import dynamodb_client from enrollment import set_status_as_canceled from middlewares.audit_log_middleware import AuditLogMiddleware -from middlewares.authorizer_middleware import User +from middlewares.authentication_middleware import User from settings import ELASTIC_CONN, ENROLLMENT_TABLE, USER_TABLE router = Router() diff --git a/http-api/routes/users/__init__.py b/http-api/routes/users/__init__.py index ff86548..ac8663d 100644 --- a/http-api/routes/users/__init__.py +++ b/http-api/routes/users/__init__.py @@ -24,6 +24,7 @@ from pydantic import UUID4, BaseModel, EmailStr, StringConstraints import cognito import elastic +import middlewares from boto3clients import dynamodb_client, idp_client from middlewares import AuditLogMiddleware from models import User @@ -70,9 +71,31 @@ class Password(BaseModel): new_password: Annotated[str, StringConstraints(min_length=6)] -@router.post('//password', compress=True, tags=['User'], include_in_schema=False) -def new_password(id: str, payload: Password): - return Response(status_code=HTTPStatus.OK) +@router.post( + '//password', + compress=True, + tags=['User'], + include_in_schema=False, + middlewares=[ + AuditLogMiddleware('PASSWORD_RESET', user_collect, ('id', 'cognito_sub')) + ], +) +def password(id: str, payload: Password): + cognito.admin_set_user_password( + username=str(payload.cognito_sub), + password=payload.new_password, + user_pool_id=USER_POOOL_ID, + idp_client=idp_client, + ) + + return Response( + body={ + 'id': id, + 'cognito_sub': payload.cognito_sub, + }, + content_type=content_types.APPLICATION_JSON, + status_code=HTTPStatus.OK, + ) @router.get('/', compress=True, tags=['User'], summary='Get user') @@ -80,10 +103,10 @@ def get_user(id: str): return user_collect.get_item(KeyPair(id, '0')) -@router.get('//idp', compress=True, include_in_schema=False) -def get_idp(id: str): +@router.get('//idp', compress=True, include_in_schema=False) +def get_idp(sub: str): return cognito.admin_get_user( - sub=id, + sub=sub, user_pool_id=USER_POOOL_ID, idp_client=idp_client, ) diff --git a/http-api/tests/routes/test_courses.py b/http-api/tests/routes/test_courses.py index 01403bf..34a531e 100644 --- a/http-api/tests/routes/test_courses.py +++ b/http-api/tests/routes/test_courses.py @@ -14,7 +14,7 @@ from ..conftest import HttpApiProxy, LambdaContext YEAR_DAYS = 365 -def test_get_course( +def test_get_courses( mock_app, dynamodb_seeds, http_api_proxy: HttpApiProxy, diff --git a/http-api/tests/routes/test_orgs.py b/http-api/tests/routes/test_orgs.py index 33f4612..879e975 100644 --- a/http-api/tests/routes/test_orgs.py +++ b/http-api/tests/routes/test_orgs.py @@ -1,11 +1,7 @@ import json from http import HTTPMethod, HTTPStatus -from layercake.dynamodb import ( - DynamoDBCollection, - DynamoDBPersistenceLayer, - KeyPair, -) +from layercake.dynamodb import DynamoDBCollection, DynamoDBPersistenceLayer, KeyPair from ..conftest import HttpApiProxy, LambdaContext @@ -32,7 +28,7 @@ def test_get_policies( } -def test_put_org( +def test_put_policies( mock_app, dynamodb_seeds, dynamodb_persistence_layer: DynamoDBPersistenceLayer, @@ -44,7 +40,7 @@ def test_put_org( raw_path='/orgs/cJtK9SsnJhKPyxESe7g3DG/policies', method=HTTPMethod.PUT, headers={'X-Tenant': '*'}, - body={}, + body={'payment_policy': None}, ), lambda_context, ) diff --git a/http-api/tests/routes/test_users.py b/http-api/tests/routes/test_users.py index 0cbe677..9de6a26 100644 --- a/http-api/tests/routes/test_users.py +++ b/http-api/tests/routes/test_users.py @@ -31,7 +31,16 @@ def test_get_emails( 'id': '5OxmMjL-ujoR5IMGegQz', 'create_date': '2019-03-25T00:00:00-03:00', 'update_date': '2023-11-09T12:13:04.308986-03:00', - } + }, + { + 'email_verified': True, + 'mx_record_exists': True, + 'sk': 'osergiosiqueira@gmail.com', + 'email_primary': False, + 'id': '5OxmMjL-ujoR5IMGegQz', + 'create_date': '2019-03-25T00:00:00-03:00', + 'update_date': '2023-11-09T12:13:04.308986-03:00', + }, ], 'last_key': None, } diff --git a/http-api/tests/test_middelwares.py b/http-api/tests/test_middelwares.py index f4d1480..057890b 100644 --- a/http-api/tests/test_middelwares.py +++ b/http-api/tests/test_middelwares.py @@ -4,7 +4,7 @@ import pytest from aws_lambda_powertools.event_handler.api_gateway import APIGatewayHttpResolver from layercake.dynamodb import DynamoDBCollection, DynamoDBPersistenceLayer -from middlewares import AuthorizerMiddleware, TenantMiddleware +from middlewares import AuthenticationMiddleware, TenantMiddleware from .conftest import HttpApiProxy, LambdaContext @@ -13,7 +13,7 @@ from .conftest import HttpApiProxy, LambdaContext def mock_app(dynamodb_persistence_layer: DynamoDBPersistenceLayer): collect = DynamoDBCollection(dynamodb_persistence_layer) app = APIGatewayHttpResolver() - app.use(middlewares=[AuthorizerMiddleware(), TenantMiddleware(collect)]) + app.use(middlewares=[AuthenticationMiddleware(), TenantMiddleware(collect)]) @app.get('/') def index(): diff --git a/http-api/user.py b/http-api/user.py index 89c2982..d251983 100644 --- a/http-api/user.py +++ b/http-api/user.py @@ -67,7 +67,7 @@ def del_email( key=KeyPair('email', email), ) transact.delete( - key=KeyPair(id, ComposeKey(email, 'emails')), + key=KeyPair(id, ComposeKey(email, prefix='emails')), cond_expr='email_primary <> :primary', expr_attr_values={':primary': True}, ) diff --git a/layercake/Makefile b/layercake/Makefile index 967204e..6066f29 100644 --- a/layercake/Makefile +++ b/layercake/Makefile @@ -10,3 +10,6 @@ deploy: export build .PHONY: docs docs: uv run mkdocs build + +docs_serve: + uv run mkdocs serve --dev-addr localhost:8080 diff --git a/layercake/layercake/dynamodb.py b/layercake/layercake/dynamodb.py index 5cd4979..2bd467c 100644 --- a/layercake/layercake/dynamodb.py +++ b/layercake/layercake/dynamodb.py @@ -421,8 +421,12 @@ class TransactItems: ) +if TYPE_CHECKING: + from mypy_boto3_dynamodb.client import DynamoDBClient + + class DynamoDBPersistenceLayer: - def __init__(self, table_name: str, dynamodb_client) -> None: + def __init__(self, table_name: str, dynamodb_client: DynamoDBClient) -> None: self.table_name = table_name self.dynamodb_client = dynamodb_client diff --git a/layercake/pyproject.toml b/layercake/pyproject.toml index c48b2a3..9d05ba3 100644 --- a/layercake/pyproject.toml +++ b/layercake/pyproject.toml @@ -23,11 +23,11 @@ dependencies = [ "meilisearch>=0.34.0", "arnparse>=0.0.2", "weasyprint>=65.0", - "uuid-utils>=0.10.0", ] [dependency-groups] dev = [ + "boto3-stubs[essential]>=1.37.33", "jsonlines>=4.0.0", "mkdocstrings[python]>=0.29.0", "pytest>=8.3.5", diff --git a/layercake/uv.lock b/layercake/uv.lock index b1be57e..4748efb 100644 --- a/layercake/uv.lock +++ b/layercake/uv.lock @@ -102,6 +102,30 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/09/7f/d667f5be40ccc21fed232f26473b352c6e329298e495b3cf230aef889348/boto3-1.37.16-py3-none-any.whl", hash = "sha256:7ba243b8f9e11ea8856b1875293f1d43f3aa04960d823f2016cb624f47d45048", size = 139562 }, ] +[[package]] +name = "boto3-stubs" +version = "1.37.33" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "botocore-stubs" }, + { name = "types-s3transfer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/8c/3972bf3a03e38d06ec61d42fe81f25edc84d70722a716065c84bd2e27efb/boto3_stubs-1.37.33.tar.gz", hash = "sha256:d0561aadb98fa4ecffd34f34c3b8be3abd0a0e5de1367f7d3e985eb4de904c5b", size = 99118 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/f6/aad1bd83b17c96d69bcbda5b679d8f17f015712bac25aad0e25d4b6b7470/boto3_stubs-1.37.33-py3-none-any.whl", hash = "sha256:b811b9c99bb47300efead119af7cfc4496545b9e571dde62e7447901966e74f8", size = 68644 }, +] + +[package.optional-dependencies] +essential = [ + { name = "mypy-boto3-cloudformation" }, + { name = "mypy-boto3-dynamodb" }, + { name = "mypy-boto3-ec2" }, + { name = "mypy-boto3-lambda" }, + { name = "mypy-boto3-rds" }, + { name = "mypy-boto3-s3" }, + { name = "mypy-boto3-sqs" }, +] + [[package]] name = "botocore" version = "1.37.16" @@ -116,6 +140,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/e9/98d3f5135cce841b54a8952f244db906511acba624252559e21188f84e90/botocore-1.37.16-py3-none-any.whl", hash = "sha256:d74d04830ead12933a96dc407175ae98b32a5dd0059d7d2b28fc7aa4ed9d3b48", size = 13422674 }, ] +[[package]] +name = "botocore-stubs" +version = "1.37.29" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "types-awscrt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/21/84/3c6f85a562fc2c0b6856a7742ccc0f59c7ff1ea2595e0159bc3b12ff7122/botocore_stubs-1.37.29.tar.gz", hash = "sha256:c59898bf1d09bf6a9f491f4705c5696e74b83156b766aa1716867f11b8a04ea1", size = 42239 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/26/8857ff237c8d70fb8591575749468eb8973e16c65931226b856dda50c160/botocore_stubs-1.37.29-py3-none-any.whl", hash = "sha256:923127abb5fac0b8b0f11837a4641f2863bb4398b5bd6b11d1604134966c4bb6", size = 65569 }, +] + [[package]] name = "brotli" version = "1.1.0" @@ -600,7 +636,7 @@ wheels = [ [[package]] name = "layercake" -version = "0.2.8" +version = "0.2.9" source = { editable = "." } dependencies = [ { name = "arnparse" }, @@ -617,12 +653,12 @@ dependencies = [ { name = "pydantic-extra-types" }, { name = "pytz" }, { name = "requests" }, - { name = "uuid-utils" }, { name = "weasyprint" }, ] [package.dev-dependencies] dev = [ + { name = "boto3-stubs", extra = ["essential"] }, { name = "jsonlines" }, { name = "mkdocstrings", extra = ["python"] }, { name = "pytest" }, @@ -647,12 +683,12 @@ requires-dist = [ { name = "pydantic-extra-types", specifier = ">=2.10.3" }, { name = "pytz", specifier = ">=2025.1" }, { name = "requests", specifier = ">=2.32.3" }, - { name = "uuid-utils", specifier = ">=0.10.0" }, { name = "weasyprint", specifier = ">=65.0" }, ] [package.metadata.requires-dev] dev = [ + { name = "boto3-stubs", extras = ["essential"], specifier = ">=1.37.33" }, { name = "jsonlines", specifier = ">=4.0.0" }, { name = "mkdocstrings", extras = ["python"], specifier = ">=0.29.0" }, { name = "pytest", specifier = ">=8.3.5" }, @@ -818,6 +854,69 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/67/d0/ef6e82f7a68c7ac02e1a01815fbe88773f4f9e40728ed35bd1664a5d76f2/mkdocstrings_python-1.16.8-py3-none-any.whl", hash = "sha256:211b7aaf776cd45578ecb531e5ad0d3a35a8be9101a6bfa10de38a69af9d8fd8", size = 124116 }, ] +[[package]] +name = "mypy-boto3-cloudformation" +version = "1.37.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4a/43/69eef04d4da88a002b388b1789b766f9f6140cde44491d62f05f579bbe3e/mypy_boto3_cloudformation-1.37.22.tar.gz", hash = "sha256:bf6bed27d1ad82d1156964adabeb0d627b555ab870d689305a160cb93e0ef255", size = 57670 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/8b/801446e0ec5174601f04a42679d6051d0f5d3986b0cfbca8a8d994769a87/mypy_boto3_cloudformation-1.37.22-py3-none-any.whl", hash = "sha256:c411740ee8d48cd497a2555fd1a525b6e521631e58a1a7f6c863f5ee6005d178", size = 69619 }, +] + +[[package]] +name = "mypy-boto3-dynamodb" +version = "1.37.33" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/35/25f710ea66927a3b45a7fc0664328c95a43894883d40ac2fe3f16fd10180/mypy_boto3_dynamodb-1.37.33.tar.gz", hash = "sha256:f924999d45fc23dd080b30b4d10498627220bf8d3f47824a513e43445b9ea02d", size = 47468 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/7d/a648593b9e0a61d5fefd132ec578b31aeba86c6b16961150948e5cd8e70c/mypy_boto3_dynamodb-1.37.33-py3-none-any.whl", hash = "sha256:3c0325a495b0487ef1dbd0aa6578d5892862b226461cdcda7d87cb48c20f1e8b", size = 56405 }, +] + +[[package]] +name = "mypy-boto3-ec2" +version = "1.37.28" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/48/97/e137d815d435587d4764403be228c014536c5fdbc4eafe475c9395e57d8a/mypy_boto3_ec2-1.37.28.tar.gz", hash = "sha256:771d2004cfdff9d4dc7cf6e4903fe907c327007a4d704e009a3b1e6b00ae9df4", size = 393729 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/1e/678f7ac49ad21722541cc0769c3cdc73ea52df2cce714062091bbfa798b6/mypy_boto3_ec2-1.37.28-py3-none-any.whl", hash = "sha256:67ce870ed108140f6532a51b5964eecf495690d6fd88fe9a593964f674dc5adb", size = 383233 }, +] + +[[package]] +name = "mypy-boto3-lambda" +version = "1.37.16" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/de/09/d7dd80cabde53fe7f39a9d7f6342b59af14707bb775606b1d9ffe82efa8b/mypy_boto3_lambda-1.37.16.tar.gz", hash = "sha256:d58f20bb0416aeb04fda6cfaa8a2f2c31532ca5009004c3e2bcbe8ac50a8cf7c", size = 41727 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/60/0b/6d6256885c2de708d21782ba4ab1d2042c98095ca381e983cb3aeeac5810/mypy_boto3_lambda-1.37.16-py3-none-any.whl", hash = "sha256:22dd22f29ef77febad9f4a2ec3d4560385b9b19a4ca1909de863206392145273", size = 48225 }, +] + +[[package]] +name = "mypy-boto3-rds" +version = "1.37.21" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2b/e7/d4ed2d847739795bc4e7e336b2a89676db18a5011eeb0d48910275f15059/mypy_boto3_rds-1.37.21.tar.gz", hash = "sha256:8fa78d077226489fef28d1b3449646882425d259f93523f7b08afd9b761dfdf8", size = 84345 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/50/b722981a6a5506513f6cd0222cf014235c642e8d3880561f2809c8262dd8/mypy_boto3_rds-1.37.21-py3-none-any.whl", hash = "sha256:af163f0e97e54b55155bfe57dc1e292edc6a1d5c2e5c9d01e62c28c952c92f2c", size = 90492 }, +] + +[[package]] +name = "mypy-boto3-s3" +version = "1.37.24" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0e/1d/8ddb398335991d7f04c3cbc64232aea673c877146f3de5bd7cbea88f1d4f/mypy_boto3_s3-1.37.24.tar.gz", hash = "sha256:4df0975256132ab452896b9d36571866a816157d519cf12c661795b2906e2e9c", size = 73709 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/fb/17efbbde4fbbf027078e7577a38dc26c68e608deed8c31627328849c5825/mypy_boto3_s3-1.37.24-py3-none-any.whl", hash = "sha256:8afd8d64be352652bc888ed81750788bebabd2b6e8ee6fe9be00ff25228df39b", size = 80318 }, +] + +[[package]] +name = "mypy-boto3-sqs" +version = "1.37.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/c9/ebbd0efd7225b3eac16b409ac9a6f14250bc00d123b04c9255e7a482afa3/mypy_boto3_sqs-1.37.0.tar.gz", hash = "sha256:ed56df72494425d4e7d0e048f2321cc17514fa3bbedfc410bbb8b709c4bff564", size = 23508 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/8e/45f49fadc0cdea30c5d0286f17849821dd24ee95adffecd901d92dff2588/mypy_boto3_sqs-1.37.0-py3-none-any.whl", hash = "sha256:2622aa681386cdbffdfcfa638dab0e081054ff9d6fe495758f4a817bcc8ec6ed", size = 33611 }, +] + [[package]] name = "orjson" version = "3.10.15" @@ -1261,6 +1360,24 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5c/de/27c57899297163a4a84104d5cec0af3b1ac5faf62f44667e506373c6b8ce/tinyhtml5-2.0.0-py3-none-any.whl", hash = "sha256:13683277c5b176d070f82d099d977194b7a1e26815b016114f581a74bbfbf47e", size = 39793 }, ] +[[package]] +name = "types-awscrt" +version = "0.26.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/29/d0597055f1b700193463361b4b2284cf4548acbb10287d6253ce429728d5/types_awscrt-0.26.1.tar.gz", hash = "sha256:aca96f889b3745c0e74f42f08f277fed3bf6e9baa2cf9b06a36f78d77720e504", size = 15537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/c0/4708ee11a50d4e80c4dfb88dc2d6d69b1c296cd7397f1fe6352f119fd310/types_awscrt-0.26.1-py3-none-any.whl", hash = "sha256:176d320a26990efc057d4bf71396e05be027c142252ac48cc0d87aaea0704280", size = 19575 }, +] + +[[package]] +name = "types-s3transfer" +version = "0.11.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/93/a9/440d8ba72a81bcf2cc5a56ef63f23b58ce93e7b9b62409697553bdcdd181/types_s3transfer-0.11.4.tar.gz", hash = "sha256:05fde593c84270f19fd053f0b1e08f5a057d7c5f036b9884e68fb8cd3041ac30", size = 14074 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/69/0b5ae42c3c33d31a32f7dcb9f35a3e327365360a6e4a2a7b491904bd38aa/types_s3transfer-0.11.4-py3-none-any.whl", hash = "sha256:2a76d92c07d4a3cb469e5343b2e7560e0b8078b2e03696a65407b8c44c861b61", size = 19516 }, +] + [[package]] name = "typing-extensions" version = "4.12.2" @@ -1279,26 +1396,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, ] -[[package]] -name = "uuid-utils" -version = "0.10.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/66/0a/cbdb2eb4845dafeb632d02a18f47b02f87f2ce4f25266f5e3c017976ce89/uuid_utils-0.10.0.tar.gz", hash = "sha256:5db0e1890e8f008657ffe6ded4d9459af724ab114cfe82af1557c87545301539", size = 18828 } -wheels = [ - { url = "https://files.pythonhosted.org/packages/44/54/9d22fa16b19e5d1676eba510f08a9c458d96e2a62ff2c8ebad64251afb18/uuid_utils-0.10.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:8d5a4508feefec62456cd6a41bcdde458d56827d908f226803b886d22a3d5e63", size = 573006 }, - { url = "https://files.pythonhosted.org/packages/08/8e/f895c6e52aa603e521fbc13b8626ba5dd99b6e2f5a55aa96ba5b232f4c53/uuid_utils-0.10.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:dbefc2b9113f9dfe56bdae58301a2b3c53792221410d422826f3d1e3e6555fe7", size = 292543 }, - { url = "https://files.pythonhosted.org/packages/b6/58/cc4834f377a5e97d6e184408ad96d13042308de56643b6e24afe1f6f34df/uuid_utils-0.10.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffc49c33edf87d1ec8112a9b43e4cf55326877716f929c165a2cc307d31c73d5", size = 323340 }, - { url = "https://files.pythonhosted.org/packages/37/e3/6aeddf148f6a7dd7759621b000e8c85382ec83f52ae79b60842d1dc3ab6b/uuid_utils-0.10.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0636b6208f69d5a4e629707ad2a89a04dfa8d1023e1999181f6830646ca048a1", size = 329653 }, - { url = "https://files.pythonhosted.org/packages/0c/00/dd6c2164ace70b7b1671d9129267df331481d7d1e5f9c5e6a564f07953f6/uuid_utils-0.10.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7bc06452856b724df9dedfc161c3582199547da54aeb81915ec2ed54f92d19b0", size = 365471 }, - { url = "https://files.pythonhosted.org/packages/b4/e7/0ab8080fcae5462a7b5e555c1cef3d63457baffb97a59b9bc7b005a3ecb1/uuid_utils-0.10.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:263b2589111c61decdd74a762e8f850c9e4386fb78d2cf7cb4dfc537054cda1b", size = 325844 }, - { url = "https://files.pythonhosted.org/packages/73/39/52d94e9ef75b03f44b39ffc6ac3167e93e74ef4d010a93d25589d9f48540/uuid_utils-0.10.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a558db48b7096de6b4d2d2210d82bba8586a6d55f99106b03bb7d01dc5c5bcd6", size = 344389 }, - { url = "https://files.pythonhosted.org/packages/7c/29/4824566f62666238290d99c62a58e4ab2a8b9cf2eccf94cebd9b3359131e/uuid_utils-0.10.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:807465067f3c892514230326ac71a79b28a8dfe2c88ecd2d5675fc844f3c76b5", size = 510078 }, - { url = "https://files.pythonhosted.org/packages/5e/8f/bbcc7130d652462c685f0d3bd26bb214b754215b476340885a4cb50fb89a/uuid_utils-0.10.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:57423d4a2b9d7b916de6dbd75ba85465a28f9578a89a97f7d3e098d9aa4e5d4a", size = 515937 }, - { url = "https://files.pythonhosted.org/packages/23/f8/34e0c00f5f188604d336713e6a020fcf53b10998e8ab24735a39ab076740/uuid_utils-0.10.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:76d8d660f18ff6b767e319b1b5f927350cd92eafa4831d7ef5b57fdd1d91f974", size = 494111 }, - { url = "https://files.pythonhosted.org/packages/1a/52/b7f0066cc90a7a9c28d54061ed195cd617fde822e5d6ac3ccc88509c3c44/uuid_utils-0.10.0-cp39-abi3-win32.whl", hash = "sha256:6c11a71489338837db0b902b75e1ba7618d5d29f05fde4f68b3f909177dbc226", size = 173520 }, - { url = "https://files.pythonhosted.org/packages/8b/15/f04f58094674d333974243fb45d2c740cf4b79186fb707168e57943c84a3/uuid_utils-0.10.0-cp39-abi3-win_amd64.whl", hash = "sha256:11c55ae64f6c0a7a0c741deae8ca2a4eaa11e9c09dbb7bec2099635696034cf7", size = 182965 }, -] - [[package]] name = "watchdog" version = "6.0.0"