hexcore 11.3.0__tar.gz → 11.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {hexcore-11.3.0/hexcore.egg-info → hexcore-11.4.0}/PKG-INFO +1 -1
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/application/services.py +14 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/domain/exceptions.py +23 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/domain/ports.py +20 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/api/dependencies.py +5 -1
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/api/middlewares.py +75 -7
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/orms/beanie/repositories.py +12 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/orms/sqlalchemy/repositories.py +13 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/testing/fakes.py +9 -0
- {hexcore-11.3.0 → hexcore-11.4.0/hexcore.egg-info}/PKG-INFO +1 -1
- {hexcore-11.3.0 → hexcore-11.4.0}/pyproject.toml +1 -1
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_http.py +190 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/LICENSE +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/MANIFEST.in +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/README.md +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/__main__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/_deprecation.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/cqrs/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/cqrs/adapters.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/cqrs/config.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/cqrs/factory.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/cqrs/in_memory_buses.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/cqrs/pipeline.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/cqrs/registry.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/cqrs/scheduler.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/dtos/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/dtos/base.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/dtos/cursor.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/dtos/errors.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/dtos/query.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/eventsourcing/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/eventsourcing/config.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/eventsourcing/factory.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/eventsourcing/projector.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/eventsourcing/relay.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/eventsourcing/repository.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/use_cases/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/use_cases/base.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/application/use_cases/query.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/capabilities.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/config.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/cqrs.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/cqrs.pyi +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/__init__.pyi +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/application/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/application/authorization.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/application/authz_middleware.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/application/commands.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/application/config.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/application/container.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/application/hooks.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/application/plugins.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/domain/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/domain/authorization.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/domain/context.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/domain/entities.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/domain/events.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/domain/permissions.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/domain/plugins.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/domain/value_objects.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/api/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/api/authorization.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/api/routers.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/cli.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/clock.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/envelope.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/hashing.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/keys.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/lifespan.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/orms/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/orms/beanie/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/orms/beanie/documents.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/orms/beanie/schema.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/orms/selection.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/orms/sqlalchemy/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/orms/sqlalchemy/models.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/orms/sqlalchemy/models_mixins.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/orms/sqlalchemy/registry.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/orms/sqlalchemy/schema.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/revocation.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/secretbox.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/tokens.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/transports.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/drbac/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/drbac/cache.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/drbac/conditions.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/drbac/domain.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/drbac/orms/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/drbac/orms/beanie/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/drbac/orms/beanie/repository.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/drbac/orms/sqlalchemy/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/drbac/orms/sqlalchemy/models.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/drbac/orms/sqlalchemy/models_mixins.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/drbac/orms/sqlalchemy/repository.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/drbac/pdp.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/drbac/pip.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/drbac/predicates.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/drbac/provider.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/drbac/router.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/drbac/service.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/impersonate/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/impersonate/commands.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/impersonate/domain.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/impersonate/router.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/impersonate/service.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/magic_link/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/magic_link/commands.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/magic_link/router.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/oauth/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/oauth/domain.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/oauth/http.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/oauth/orms/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/oauth/orms/beanie/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/oauth/orms/beanie/repository.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/oauth/orms/sqlalchemy/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/oauth/orms/sqlalchemy/models.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/oauth/orms/sqlalchemy/models_mixins.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/oauth/orms/sqlalchemy/repository.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/oauth/providers.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/oauth/router.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/oauth/service.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/organization/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/organization/domain.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/organization/orms/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/organization/orms/beanie/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/organization/orms/beanie/repository.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/organization/orms/sqlalchemy/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/organization/orms/sqlalchemy/models.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/organization/orms/sqlalchemy/models_mixins.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/organization/orms/sqlalchemy/repository.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/organization/router.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/organization/service.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/passkey/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/passkey/domain.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/passkey/orms/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/passkey/orms/beanie/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/passkey/orms/beanie/repository.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/passkey/orms/sqlalchemy/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/passkey/orms/sqlalchemy/models.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/passkey/orms/sqlalchemy/models_mixins.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/passkey/orms/sqlalchemy/repository.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/passkey/router.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/passkey/service.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/passkey/webauthn_adapter.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/rbac/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/rbac/cache.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/rbac/cli.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/rbac/domain.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/rbac/matcher.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/rbac/orms/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/rbac/orms/beanie/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/rbac/orms/beanie/repository.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/rbac/orms/sqlalchemy/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/rbac/orms/sqlalchemy/models.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/rbac/orms/sqlalchemy/models_mixins.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/rbac/orms/sqlalchemy/repository.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/rbac/provider.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/rbac/resolver.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/rbac/router.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/rbac/service.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/storage.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/two_factor/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/two_factor/commands.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/two_factor/crypto.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/two_factor/domain.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/two_factor/orms/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/two_factor/orms/beanie/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/two_factor/orms/beanie/repository.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/two_factor/orms/sqlalchemy/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/two_factor/orms/sqlalchemy/models.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/two_factor/orms/sqlalchemy/models_mixins.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/two_factor/orms/sqlalchemy/repository.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/two_factor/router.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/two_factor/service.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/plugins/two_factor/totp.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/testing/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/testing/fixtures.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/testing/helpers.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/base.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/cqrs/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/cqrs/buses.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/cqrs/commands.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/cqrs/context.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/cqrs/cron.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/cqrs/decorators.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/cqrs/dispatch.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/cqrs/envelope.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/cqrs/exceptions.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/cqrs/handlers.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/cqrs/middleware.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/cqrs/queries.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/cqrs/resolution.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/cqrs/serializer.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/cqrs/task_queues.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/events.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/eventsourcing/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/eventsourcing/aggregate.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/eventsourcing/exceptions.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/eventsourcing/projections.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/eventsourcing/snapshots.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/eventsourcing/store.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/eventsourcing/stored.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/exceptions.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/repositories.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/services.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/domain/uow.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/eventsourcing.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/eventsourcing.pyi +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/fastapi.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/fastapi.pyi +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/api/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/api/app.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/api/cqrs.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/api/eventsourcing.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/api/exception_handlers.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/api/health.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/api/lifespan.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/api/middlewares.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/api/rate_limit.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/api/routing.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/api/streaming.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/api/utils.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/cache/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/cache/cache_backends/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/cache/cache_backends/memory.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/cache/cache_backends/redis.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/cli.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/cqrs/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/cqrs/cron_sql.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/cqrs/middlewares.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/cqrs/postgres_bus.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/cqrs/postgres_lock.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/cqrs/procrastinate.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/cqrs/pydantic_serializer.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/cqrs/rabbitmq.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/cqrs/redis_bus.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/cqrs/redis_lock.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/eventsourcing/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/eventsourcing/beanie_documents.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/eventsourcing/beanie_store.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/eventsourcing/memory.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/eventsourcing/redis_store.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/eventsourcing/sqlalchemy_models.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/eventsourcing/sqlalchemy_models_mixins.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/eventsourcing/sqlalchemy_store.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/repositories/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/repositories/_beanie_impl.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/repositories/_sqlalchemy_impl.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/repositories/base.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/repositories/decorators.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/repositories/implementations.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/repositories/orms/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/repositories/orms/beanie/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/repositories/orms/beanie/utils.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/repositories/orms/sqlalchemy/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/repositories/orms/sqlalchemy/id_types.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/repositories/orms/sqlalchemy/session.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/repositories/orms/sqlalchemy/utils.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/repositories/utils.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/task_queues/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/task_queues/celery_adapter.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/task_queues/procrastinate_adapter.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/uow/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/uow/decorators.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/uow/helpers.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/uow/scopes.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/workers/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/workers/consumer.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/workers/rabbitmq_worker.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/infrastructure/workers/runner.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/py.typed +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/sql.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/sql.pyi +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/testing/__init__.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/testing/fakes.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/testing/fixtures.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/testing/helpers.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/testing/repositories.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore/types.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore.egg-info/SOURCES.txt +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore.egg-info/dependency_links.txt +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore.egg-info/entry_points.txt +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore.egg-info/requires.txt +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/hexcore.egg-info/top_level.txt +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/setup.cfg +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_alembic_metadata_completeness.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_anclas_del_repo.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_api_app_and_lifespan.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_api_cors_dinamico.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_api_cqrs_providers.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_api_exception_handlers.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_api_health.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_api_middlewares_and_routing.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_api_rate_limit.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_api_streaming.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_basic.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_beanie_query_utils.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_celery_event_loop.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_config_cors_security.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_config_loading.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_consumer_optional_event_bus.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_cqrs.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_cqrs_envelope.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_cqrs_factory.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_cqrs_middlewares.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_cron_sql.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_10_4_hardening.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_authorization_engine.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_authorization_http.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_backend_neutrality.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_beanie.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_cli.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_container.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_context.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_custom_plugin.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_drbac_conditions.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_drbac_pdp.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_drbac_router.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_drbac_service.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_envelope.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_facade_and_events.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_flows.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_hashing.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_impersonate.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_magic_link.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_models.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_oauth.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_openapi.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_organization.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_passkey.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_permissions.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_plugin_decoupling.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_plugins.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_principals.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_production_defaults.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_rbac.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_rbac_service.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_repositories.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_revocation.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_schema_registration.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_storage_selection.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_tokens.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_two_factor.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_username.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_darwin_value_objects.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_deprecations.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_documentation_examples.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_domain_base_contract.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_domain_service_query.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_dotted_resolution.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_event_dispatch_mro.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_eventsourcing_aggregate.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_eventsourcing_beanie.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_eventsourcing_config.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_eventsourcing_ports.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_eventsourcing_projections.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_eventsourcing_redis.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_eventsourcing_relay.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_eventsourcing_repository.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_eventsourcing_snapshot_checkpoint.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_eventsourcing_sql_schema.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_eventsourcing_store_contract.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_eventsourcing_uow_integration.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_facades.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_handler_registry.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_infrastructure_query_path.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_lock_error_policy.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_optional_dependencies.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_packaging.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_postgres_bus.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_postgres_lock.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_query_cursor_and_endpoint.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_query_field_validation.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_rabbitmq_bus.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_redis_bus.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_redis_lock.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_repositories_utils.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_scheduler.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_scheduler_catchup.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_smart_routing.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_sql_naming_convention.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_sql_session_layer.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_task_queues_adapters.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_testing_kit.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_testing_utilities.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_typing_gate.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_uow_session_regression.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_use_cases_query.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_worker_bus_integration.py +0 -0
- {hexcore-11.3.0 → hexcore-11.4.0}/tests/test_worker_runner.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hexcore
|
|
3
|
-
Version: 11.
|
|
3
|
+
Version: 11.4.0
|
|
4
4
|
Summary: Reusable core for Python projects with hexagonal architecture, DDD, CQRS and background tasks. Ships the abstractions and the infrastructure: SQL session layer, FastAPI factories, worker runner, dynamic cron and identity.
|
|
5
5
|
Author-email: "David Latosefki (Indroic)" <indroic@outlook.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -381,6 +381,20 @@ class SessionService:
|
|
|
381
381
|
impersonation=impersonacion,
|
|
382
382
|
)
|
|
383
383
|
|
|
384
|
+
async def previous_session_ids(self, session_id: UUID, *, limit: int = 100) -> list[UUID]:
|
|
385
|
+
"""
|
|
386
|
+
Los `sid` de las sesiones **anteriores** de la familia de `session_id` (sin incluirla).
|
|
387
|
+
|
|
388
|
+
Existe para que `CsrfMiddleware` reconozca un token anti-CSRF que fue válido para la
|
|
389
|
+
sesión que el refresh acaba de reemplazar. Lee la base, pero sólo en el camino de
|
|
390
|
+
rechazo y con tope: el camino feliz de un request no lo llama.
|
|
391
|
+
"""
|
|
392
|
+
sesion = await self._sessions.get(session_id)
|
|
393
|
+
if sesion is None:
|
|
394
|
+
return []
|
|
395
|
+
ids = await self._sessions.list_family_session_ids(sesion.family_id, limit=limit)
|
|
396
|
+
return [sid for sid in ids if sid != session_id]
|
|
397
|
+
|
|
384
398
|
# ── Rotar ─────────────────────────────────────────────────────────────────
|
|
385
399
|
async def refresh(
|
|
386
400
|
self, refresh_token: str, *, transport: Transport
|
|
@@ -36,6 +36,8 @@ __all__ = [
|
|
|
36
36
|
"EmailNotVerifiedError",
|
|
37
37
|
"ImpersonationNotPermittedError",
|
|
38
38
|
"CsrfValidationError",
|
|
39
|
+
"CSRF_CODE_INVALID",
|
|
40
|
+
"CSRF_CODE_PREVIOUS_SESSION",
|
|
39
41
|
# ── Estado (409 / 423) ──
|
|
40
42
|
"EmailAlreadyRegisteredError",
|
|
41
43
|
"UsernameAlreadyTakenError",
|
|
@@ -164,14 +166,35 @@ class ImpersonationNotPermittedError(AuthorizationError):
|
|
|
164
166
|
"""
|
|
165
167
|
|
|
166
168
|
|
|
169
|
+
#: `code` de un rechazo de CSRF cuyo token no verifica contra la sesión en curso ni contra
|
|
170
|
+
#: ninguna anterior de su familia: lo mandó alguien que no lo recibió del servidor.
|
|
171
|
+
CSRF_CODE_INVALID = "CsrfTokenInvalid"
|
|
172
|
+
|
|
173
|
+
#: `code` de un rechazo de CSRF cuyo token **sí** fue válido, pero de una sesión anterior de la
|
|
174
|
+
#: misma familia de rotación: el cliente firmó antes de que un refresh rotara el `sid`. Es el
|
|
175
|
+
#: caso que se arregla pidiendo el token vigente (`GET /auth/csrf`) y reintentando.
|
|
176
|
+
CSRF_CODE_PREVIOUS_SESSION = "CsrfTokenFromPreviousSession"
|
|
177
|
+
|
|
178
|
+
|
|
167
179
|
class CsrfValidationError(AuthorizationError):
|
|
168
180
|
"""
|
|
169
181
|
Falló el chequeo anti-CSRF del transporte por cookie.
|
|
170
182
|
|
|
171
183
|
Sólo aplica al camino de cookie: un cliente Bearer manda el token a propósito en cada
|
|
172
184
|
petición, así que no hay nada que falsificar desde otro origen.
|
|
185
|
+
|
|
186
|
+
`code` distingue, **cuando el rechazo es por el token**, entre `CSRF_CODE_INVALID` y
|
|
187
|
+
`CSRF_CODE_PREVIOUS_SESSION`; viaja en el cuerpo del 403 junto a `error`
|
|
188
|
+
(`CsrfValidationError`, que sigue siendo el discriminante). Es `None` cuando el rechazo
|
|
189
|
+
no es por el token (un origen en el que no se confía), porque ahí no hay nada que
|
|
190
|
+
recuperar. No revela nada útil a un atacante: sólo se llega a `CSRF_CODE_PREVIOUS_SESSION`
|
|
191
|
+
mandando un token que el servidor ya emitió para esa misma familia de sesiones.
|
|
173
192
|
"""
|
|
174
193
|
|
|
194
|
+
def __init__(self, *args: object, code: str | None = None) -> None:
|
|
195
|
+
super().__init__(*args)
|
|
196
|
+
self.code = code
|
|
197
|
+
|
|
175
198
|
|
|
176
199
|
# ── Estado del recurso ────────────────────────────────────────────────────────
|
|
177
200
|
class EmailAlreadyRegisteredError(IdentityError):
|
|
@@ -187,6 +187,26 @@ class AbstractSessionRepository(abc.ABC):
|
|
|
187
187
|
async def revoke(self, session_id: UUID, *, at: datetime, reason: str) -> None:
|
|
188
188
|
raise NotImplementedError
|
|
189
189
|
|
|
190
|
+
async def list_family_session_ids(
|
|
191
|
+
self, family_id: UUID, *, limit: int = 100
|
|
192
|
+
) -> "list[UUID]":
|
|
193
|
+
"""
|
|
194
|
+
Los `id` de las sesiones de una familia de rotación, las **más recientes primero**, con
|
|
195
|
+
tope de `limit`.
|
|
196
|
+
|
|
197
|
+
Sólo lo usa `CsrfMiddleware` para clasificar un rechazo de CSRF: reconocer que el token
|
|
198
|
+
que llegó era de una sesión anterior de la familia (el cliente firmó antes de que un
|
|
199
|
+
refresh rotara el `sid`). Incluye revocadas y consumidas: justamente las anteriores lo
|
|
200
|
+
están.
|
|
201
|
+
|
|
202
|
+
**No es abstracto a propósito**: es un puerto agregado después, y volverlo abstracto
|
|
203
|
+
rompería a todo repositorio propio ya escrito. La implementación por defecto devuelve
|
|
204
|
+
`[]`, y entonces el rechazo se clasifica siempre como token inválido: el comportamiento
|
|
205
|
+
(403) no cambia, sólo se pierde el `code` más específico.
|
|
206
|
+
"""
|
|
207
|
+
del family_id, limit
|
|
208
|
+
return []
|
|
209
|
+
|
|
190
210
|
@abc.abstractmethod
|
|
191
211
|
async def revoke_family(self, family_id: UUID, *, at: datetime, reason: str) -> int:
|
|
192
212
|
"""
|
|
@@ -194,13 +194,17 @@ def identity_exception_payload(exc: Exception) -> t.Mapping[str, t.Any]:
|
|
|
194
194
|
Darwin) devuelve sólo lo del núcleo en vez de lanzar — mismo trato que `_mapa_de_plugins()`
|
|
195
195
|
en `app.py`.
|
|
196
196
|
"""
|
|
197
|
-
from hexcore.darwin.domain.exceptions import AccessDeniedError
|
|
197
|
+
from hexcore.darwin.domain.exceptions import AccessDeniedError, CsrfValidationError
|
|
198
198
|
|
|
199
199
|
base: dict[str, t.Any] = {}
|
|
200
200
|
if isinstance(exc, AccessDeniedError):
|
|
201
201
|
# Sólo `required`: `Decision.reason` es interno y nunca llega al cliente. Ver el
|
|
202
202
|
# docstring de `AccessDeniedError`.
|
|
203
203
|
base["required"] = exc.required
|
|
204
|
+
elif isinstance(exc, CsrfValidationError) and exc.code is not None:
|
|
205
|
+
# El mismo `code` que `CsrfMiddleware` pone en su 403, para que un rechazo de CSRF
|
|
206
|
+
# lanzado desde un handler tenga la misma forma que el del middleware.
|
|
207
|
+
base["code"] = exc.code
|
|
204
208
|
|
|
205
209
|
from hexcore.darwin.application.container import get_identity_container
|
|
206
210
|
|
|
@@ -25,7 +25,12 @@ from starlette.requests import Request
|
|
|
25
25
|
from starlette.responses import JSONResponse, Response
|
|
26
26
|
|
|
27
27
|
from hexcore.darwin.domain.context import AUTH_CONTEXT
|
|
28
|
-
from hexcore.darwin.domain.exceptions import
|
|
28
|
+
from hexcore.darwin.domain.exceptions import (
|
|
29
|
+
CSRF_CODE_INVALID,
|
|
30
|
+
CSRF_CODE_PREVIOUS_SESSION,
|
|
31
|
+
CsrfValidationError,
|
|
32
|
+
IdentityError,
|
|
33
|
+
)
|
|
29
34
|
|
|
30
35
|
if t.TYPE_CHECKING:
|
|
31
36
|
from hexcore.darwin.application.config import IdentityConfig
|
|
@@ -224,13 +229,74 @@ class CsrfMiddleware(BaseHTTPMiddleware):
|
|
|
224
229
|
logger.info(
|
|
225
230
|
"CSRF rechazado en %s %s: %s", request.method, request.url.path, exc
|
|
226
231
|
)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
232
|
+
if exc.code == CSRF_CODE_INVALID and await self._es_de_sesion_anterior(
|
|
233
|
+
request, contenedor
|
|
234
|
+
):
|
|
235
|
+
exc = CsrfValidationError(
|
|
236
|
+
"El token anti-CSRF del header es de una sesión anterior: el refresh "
|
|
237
|
+
"rotó la sesión después de que el cliente lo obtuvo. Pedí el vigente con "
|
|
238
|
+
"`GET /auth/csrf` y repetí la petición.",
|
|
239
|
+
code=CSRF_CODE_PREVIOUS_SESSION,
|
|
240
|
+
)
|
|
241
|
+
content: dict[str, t.Any] = {"detail": str(exc), "error": type(exc).__name__}
|
|
242
|
+
if exc.code is not None:
|
|
243
|
+
content["code"] = exc.code
|
|
244
|
+
return JSONResponse(status_code=403, content=content)
|
|
231
245
|
|
|
232
246
|
return await call_next(request)
|
|
233
247
|
|
|
248
|
+
async def _es_de_sesion_anterior(
|
|
249
|
+
self, request: Request, contenedor: t.Any
|
|
250
|
+
) -> bool:
|
|
251
|
+
"""
|
|
252
|
+
Si el token del header fue el de una sesión **anterior de la misma familia** que la de
|
|
253
|
+
la cookie de acceso.
|
|
254
|
+
|
|
255
|
+
Sólo corre al rechazar, así que el camino feliz de un request no paga la lectura de la
|
|
256
|
+
base. Tiene que ser **mejor esfuerzo**: cualquier fallo (credencial que no autentica,
|
|
257
|
+
base caída, un backend sin `list_family_session_ids`) devuelve `False` y el rechazo
|
|
258
|
+
sale como token inválido, que es lo que habría salido sin esta clasificación. Nunca
|
|
259
|
+
convierte un 403 en un 500.
|
|
260
|
+
"""
|
|
261
|
+
from hexcore.darwin.infrastructure.hashing import compare_hashes, derive_csrf_token
|
|
262
|
+
|
|
263
|
+
enviado = request.headers.get(CSRF_HEADER)
|
|
264
|
+
clave = contenedor.config.secret_key
|
|
265
|
+
if not enviado or clave is None:
|
|
266
|
+
return False
|
|
267
|
+
|
|
268
|
+
try:
|
|
269
|
+
auth = auth_from_request(request)
|
|
270
|
+
if auth is None:
|
|
271
|
+
from hexcore.darwin.infrastructure.transports import TransportResolver
|
|
272
|
+
|
|
273
|
+
transporte = TransportResolver(cookies=contenedor.config.cookies).resolve(
|
|
274
|
+
request
|
|
275
|
+
)
|
|
276
|
+
token = transporte.extract_access(request)
|
|
277
|
+
if token is None:
|
|
278
|
+
return False
|
|
279
|
+
auth = await contenedor.session_service().authenticate(
|
|
280
|
+
token, transport=transporte.name
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
sid = getattr(auth.actor, "session_id", None)
|
|
284
|
+
if sid is None or auth.transport != "cookie":
|
|
285
|
+
return False
|
|
286
|
+
|
|
287
|
+
secreto = clave.get_secret_value()
|
|
288
|
+
previas = await contenedor.session_service().previous_session_ids(sid)
|
|
289
|
+
# Se recorren **todas** sin cortar en la primera que coincide: el tiempo no tiene
|
|
290
|
+
# que depender de cuál era (aunque `compare_hashes` ya es en tiempo constante).
|
|
291
|
+
coincide = False
|
|
292
|
+
for previa in previas:
|
|
293
|
+
if compare_hashes(enviado, derive_csrf_token(str(previa), secreto)):
|
|
294
|
+
coincide = True
|
|
295
|
+
return coincide
|
|
296
|
+
except Exception:
|
|
297
|
+
logger.debug("No se pudo clasificar el rechazo de CSRF.", exc_info=True)
|
|
298
|
+
return False
|
|
299
|
+
|
|
234
300
|
def _exento(self, request: Request) -> bool:
|
|
235
301
|
if request.method.upper() in SAFE_METHODS:
|
|
236
302
|
return True
|
|
@@ -262,14 +328,16 @@ class CsrfMiddleware(BaseHTTPMiddleware):
|
|
|
262
328
|
if not enviado or not esperado:
|
|
263
329
|
raise CsrfValidationError(
|
|
264
330
|
f"Falta el chequeo anti-CSRF: la petición tiene que mandar el header "
|
|
265
|
-
f"'{CSRF_HEADER}' con el mismo valor que la cookie de CSRF."
|
|
331
|
+
f"'{CSRF_HEADER}' con el mismo valor que la cookie de CSRF.",
|
|
332
|
+
code=CSRF_CODE_INVALID,
|
|
266
333
|
)
|
|
267
334
|
|
|
268
335
|
from hexcore.darwin.infrastructure.hashing import compare_hashes
|
|
269
336
|
|
|
270
337
|
if not compare_hashes(enviado, esperado):
|
|
271
338
|
raise CsrfValidationError(
|
|
272
|
-
"El valor anti-CSRF del header no coincide con el de la cookie."
|
|
339
|
+
"El valor anti-CSRF del header no coincide con el de la cookie.",
|
|
340
|
+
code=CSRF_CODE_INVALID,
|
|
273
341
|
)
|
|
274
342
|
|
|
275
343
|
@staticmethod
|
|
@@ -245,6 +245,18 @@ class BeanieSessionRepository(_BaseBeanieRepository, AbstractSessionRepository):
|
|
|
245
245
|
{"$set": {"revoked_at": at, "updated_at": datetime.now(UTC)}}
|
|
246
246
|
)
|
|
247
247
|
|
|
248
|
+
async def list_family_session_ids(
|
|
249
|
+
self, family_id: UUID, *, limit: int = 100
|
|
250
|
+
) -> list[UUID]:
|
|
251
|
+
"""Los `id` de la familia, las más recientes primero. Ver el puerto."""
|
|
252
|
+
docs = (
|
|
253
|
+
await self._doc.find(self._doc.family_id == family_id)
|
|
254
|
+
.sort("-created_at")
|
|
255
|
+
.limit(limit)
|
|
256
|
+
.to_list()
|
|
257
|
+
)
|
|
258
|
+
return [doc.entity_id for doc in docs]
|
|
259
|
+
|
|
248
260
|
async def revoke_family(
|
|
249
261
|
self, family_id: UUID, *, at: datetime, reason: str
|
|
250
262
|
) -> int:
|
{hexcore-11.3.0 → hexcore-11.4.0}/hexcore/darwin/infrastructure/orms/sqlalchemy/repositories.py
RENAMED
|
@@ -221,6 +221,19 @@ class SqlAlchemySessionRepository(_BaseIdentityRepository, AbstractSessionReposi
|
|
|
221
221
|
)
|
|
222
222
|
await session.commit()
|
|
223
223
|
|
|
224
|
+
async def list_family_session_ids(
|
|
225
|
+
self, family_id: UUID, *, limit: int = 100
|
|
226
|
+
) -> list[UUID]:
|
|
227
|
+
"""Los `id` de la familia, las más recientes primero. Ver el puerto."""
|
|
228
|
+
async with self._session_scope() as session:
|
|
229
|
+
resultado = await session.execute(
|
|
230
|
+
select(self._model.id)
|
|
231
|
+
.where(self._model.family_id == family_id)
|
|
232
|
+
.order_by(self._model.created_at.desc())
|
|
233
|
+
.limit(limit)
|
|
234
|
+
)
|
|
235
|
+
return list(resultado.scalars().all())
|
|
236
|
+
|
|
224
237
|
async def revoke_family(
|
|
225
238
|
self, family_id: UUID, *, at: datetime, reason: str
|
|
226
239
|
) -> int:
|
|
@@ -259,6 +259,15 @@ class FakeSessionRepository(AbstractSessionRepository):
|
|
|
259
259
|
)
|
|
260
260
|
self.reasons[session_id] = reason
|
|
261
261
|
|
|
262
|
+
async def list_family_session_ids(
|
|
263
|
+
self, family_id: UUID, *, limit: int = 100
|
|
264
|
+
) -> list[UUID]:
|
|
265
|
+
"""Los `id` de la familia, las más recientes primero. Ver el puerto."""
|
|
266
|
+
with self._lock:
|
|
267
|
+
familia = [s for s in self._por_id.values() if s.family_id == family_id]
|
|
268
|
+
familia.sort(key=lambda s: s.created_at, reverse=True)
|
|
269
|
+
return [s.id for s in familia[:limit]]
|
|
270
|
+
|
|
262
271
|
async def revoke_family(
|
|
263
272
|
self, family_id: UUID, *, at: datetime, reason: str
|
|
264
273
|
) -> int:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hexcore
|
|
3
|
-
Version: 11.
|
|
3
|
+
Version: 11.4.0
|
|
4
4
|
Summary: Reusable core for Python projects with hexagonal architecture, DDD, CQRS and background tasks. Ships the abstractions and the infrastructure: SQL session layer, FastAPI factories, worker runner, dynamic cron and identity.
|
|
5
5
|
Author-email: "David Latosefki (Indroic)" <indroic@outlook.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -1006,3 +1006,193 @@ def test_una_app_sin_el_feature_no_resuelve_credenciales(contenedor):
|
|
|
1006
1006
|
r = TestClient(app).get("/x")
|
|
1007
1007
|
|
|
1008
1008
|
assert r.json()["hay_contexto"] is False
|
|
1009
|
+
|
|
1010
|
+
|
|
1011
|
+
# ── 2c. CSRF y rotación de sesión (HC-01) ─────────────────────────────────────
|
|
1012
|
+
#
|
|
1013
|
+
# El refresh rota la sesión: crea una con `sid` nuevo, y el token anti-CSRF es `HMAC(sid)`. Una
|
|
1014
|
+
# petición firmada **antes** del refresh llega con el token de la sesión anterior y se rechaza
|
|
1015
|
+
# con 403. Es un caso distinto de un token inventado, y el cliente lo resuelve pidiendo el token
|
|
1016
|
+
# vigente: por eso el 403 lleva un `code` que los distingue.
|
|
1017
|
+
CODIGO_SESION_ANTERIOR = "CsrfTokenFromPreviousSession"
|
|
1018
|
+
CODIGO_INVALIDO = "CsrfTokenInvalid"
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
def _escribir(client: TestClient, token: str | None):
|
|
1022
|
+
"""Una escritura por cookie con el token dado en el header (o sin header si es `None`)."""
|
|
1023
|
+
headers = {"Origin": ORIGEN}
|
|
1024
|
+
if token is not None:
|
|
1025
|
+
headers[CSRF_HEADER] = token
|
|
1026
|
+
return client.post("/auth/sign-out", headers=headers)
|
|
1027
|
+
|
|
1028
|
+
|
|
1029
|
+
def test_el_refresh_entrega_siempre_el_token_csrf_de_la_sesion_nueva(client, contenedor):
|
|
1030
|
+
_alta(client)
|
|
1031
|
+
anterior = _sign_in_cookie(client).headers[CSRF_HEADER]
|
|
1032
|
+
|
|
1033
|
+
r = client.post("/auth/refresh", headers=_csrf_headers(client))
|
|
1034
|
+
|
|
1035
|
+
assert r.status_code == 200, r.text
|
|
1036
|
+
sid_nuevo = r.json()["session_id"]
|
|
1037
|
+
assert r.headers[CSRF_HEADER] == derive_csrf_token(sid_nuevo, CLAVE)
|
|
1038
|
+
assert r.headers[CSRF_HEADER] != anterior
|
|
1039
|
+
|
|
1040
|
+
|
|
1041
|
+
def test_un_token_de_una_sesion_anterior_de_la_familia_tiene_su_propio_code(client, contenedor):
|
|
1042
|
+
_alta(client)
|
|
1043
|
+
anterior = _sign_in_cookie(client).headers[CSRF_HEADER]
|
|
1044
|
+
r = client.post("/auth/refresh", headers=_csrf_headers(client))
|
|
1045
|
+
assert r.status_code == 200, r.text
|
|
1046
|
+
|
|
1047
|
+
r = _escribir(client, anterior)
|
|
1048
|
+
|
|
1049
|
+
assert r.status_code == 403
|
|
1050
|
+
cuerpo = r.json()
|
|
1051
|
+
assert cuerpo["error"] == "CsrfValidationError"
|
|
1052
|
+
assert cuerpo["code"] == CODIGO_SESION_ANTERIOR
|
|
1053
|
+
assert "sesión anterior" in cuerpo["detail"]
|
|
1054
|
+
|
|
1055
|
+
|
|
1056
|
+
def test_tambien_se_reconoce_un_token_de_dos_rotaciones_atras(client, contenedor):
|
|
1057
|
+
_alta(client)
|
|
1058
|
+
primero = _sign_in_cookie(client).headers[CSRF_HEADER]
|
|
1059
|
+
assert client.post("/auth/refresh", headers=_csrf_headers(client)).status_code == 200
|
|
1060
|
+
assert client.post("/auth/refresh", headers=_csrf_headers(client)).status_code == 200
|
|
1061
|
+
|
|
1062
|
+
r = _escribir(client, primero)
|
|
1063
|
+
|
|
1064
|
+
assert r.status_code == 403
|
|
1065
|
+
assert r.json()["code"] == CODIGO_SESION_ANTERIOR
|
|
1066
|
+
|
|
1067
|
+
|
|
1068
|
+
def test_un_token_inventado_tiene_el_code_generico(client, contenedor):
|
|
1069
|
+
_alta(client)
|
|
1070
|
+
_sign_in_cookie(client)
|
|
1071
|
+
|
|
1072
|
+
r = _escribir(client, "x" * 43)
|
|
1073
|
+
|
|
1074
|
+
assert r.status_code == 403
|
|
1075
|
+
cuerpo = r.json()
|
|
1076
|
+
assert cuerpo["error"] == "CsrfValidationError"
|
|
1077
|
+
assert cuerpo["code"] == CODIGO_INVALIDO
|
|
1078
|
+
assert "sesión anterior" not in cuerpo["detail"]
|
|
1079
|
+
|
|
1080
|
+
|
|
1081
|
+
def test_un_token_inventado_tras_un_refresh_sigue_siendo_generico(client, contenedor):
|
|
1082
|
+
"""Que la sesión haya rotado no vuelve «de una sesión anterior» a cualquier valor."""
|
|
1083
|
+
_alta(client)
|
|
1084
|
+
_sign_in_cookie(client)
|
|
1085
|
+
assert client.post("/auth/refresh", headers=_csrf_headers(client)).status_code == 200
|
|
1086
|
+
|
|
1087
|
+
r = _escribir(client, derive_csrf_token("00000000-0000-0000-0000-000000000000", CLAVE))
|
|
1088
|
+
|
|
1089
|
+
assert r.status_code == 403
|
|
1090
|
+
assert r.json()["code"] == CODIGO_INVALIDO
|
|
1091
|
+
|
|
1092
|
+
|
|
1093
|
+
def test_el_token_de_otra_cuenta_es_generico_no_de_sesion_anterior(client, contenedor):
|
|
1094
|
+
"""Un token **válido** pero de otra familia no se reconoce como «de una sesión anterior»."""
|
|
1095
|
+
ajeno = TestClient(client.app, raise_server_exceptions=False)
|
|
1096
|
+
r = ajeno.post("/auth/sign-up", json={"email": "bea@test.com", "password": PASS})
|
|
1097
|
+
assert r.status_code == 201, r.text
|
|
1098
|
+
r = ajeno.post(
|
|
1099
|
+
"/auth/verify-email",
|
|
1100
|
+
json={"email": "bea@test.com", "code": r.json()["verification_code"]},
|
|
1101
|
+
)
|
|
1102
|
+
assert r.status_code == 200, r.text
|
|
1103
|
+
token_ajeno = ajeno.post(
|
|
1104
|
+
"/auth/sign-in",
|
|
1105
|
+
json={"email": "bea@test.com", "password": PASS},
|
|
1106
|
+
headers={"Origin": ORIGEN},
|
|
1107
|
+
).headers[CSRF_HEADER]
|
|
1108
|
+
|
|
1109
|
+
# El alta y el sign-in tienen un límite de 5 por IP que comparten las dos cuentas del test.
|
|
1110
|
+
from hexcore.config import LazyConfig
|
|
1111
|
+
from hexcore.infrastructure.cache.cache_backends.memory import MemoryCache
|
|
1112
|
+
|
|
1113
|
+
LazyConfig.get_config().cache_backend = MemoryCache()
|
|
1114
|
+
|
|
1115
|
+
_alta(client)
|
|
1116
|
+
entrada = _sign_in_cookie(client)
|
|
1117
|
+
assert entrada.status_code == 200, entrada.text
|
|
1118
|
+
r = _escribir(client, token_ajeno)
|
|
1119
|
+
|
|
1120
|
+
assert r.status_code == 403
|
|
1121
|
+
assert r.json()["code"] == CODIGO_INVALIDO
|
|
1122
|
+
|
|
1123
|
+
|
|
1124
|
+
def test_sin_el_header_el_rechazo_tiene_el_code_generico(client, contenedor):
|
|
1125
|
+
_alta(client)
|
|
1126
|
+
_sign_in_cookie(client)
|
|
1127
|
+
|
|
1128
|
+
r = _escribir(client, None)
|
|
1129
|
+
|
|
1130
|
+
assert r.status_code == 403
|
|
1131
|
+
assert r.json()["code"] == CODIGO_INVALIDO
|
|
1132
|
+
|
|
1133
|
+
|
|
1134
|
+
def test_un_origen_ajeno_se_rechaza_sin_code(client, contenedor):
|
|
1135
|
+
"""No es un problema del token: no hay nada que recuperar pidiendo otro."""
|
|
1136
|
+
_alta(client)
|
|
1137
|
+
_sign_in_cookie(client)
|
|
1138
|
+
|
|
1139
|
+
r = client.post(
|
|
1140
|
+
"/auth/sign-out",
|
|
1141
|
+
headers={"Origin": "https://evil.test", CSRF_HEADER: client.cookies.get("csrf") or ""},
|
|
1142
|
+
)
|
|
1143
|
+
|
|
1144
|
+
assert r.status_code == 403
|
|
1145
|
+
assert "code" not in r.json()
|
|
1146
|
+
|
|
1147
|
+
|
|
1148
|
+
def test_un_repositorio_sin_listado_de_familia_cae_al_code_generico(client, contenedor):
|
|
1149
|
+
"""Un backend propio que no implementa el puerto nuevo no pierde el 403, sólo el `code`."""
|
|
1150
|
+
_alta(client)
|
|
1151
|
+
anterior = _sign_in_cookie(client).headers[CSRF_HEADER]
|
|
1152
|
+
assert client.post("/auth/refresh", headers=_csrf_headers(client)).status_code == 200
|
|
1153
|
+
|
|
1154
|
+
from hexcore.darwin.domain.ports import AbstractSessionRepository
|
|
1155
|
+
|
|
1156
|
+
repo = contenedor.sessions_repository()
|
|
1157
|
+
original = type(repo).list_family_session_ids
|
|
1158
|
+
type(repo).list_family_session_ids = AbstractSessionRepository.list_family_session_ids
|
|
1159
|
+
try:
|
|
1160
|
+
r = _escribir(client, anterior)
|
|
1161
|
+
finally:
|
|
1162
|
+
type(repo).list_family_session_ids = original
|
|
1163
|
+
|
|
1164
|
+
assert r.status_code == 403
|
|
1165
|
+
assert r.json()["code"] == CODIGO_INVALIDO
|
|
1166
|
+
|
|
1167
|
+
|
|
1168
|
+
def test_si_la_clasificacion_falla_el_rechazo_sigue_siendo_un_403(client, contenedor):
|
|
1169
|
+
"""La lectura de la base es accesoria: si revienta nunca se convierte en un 500."""
|
|
1170
|
+
_alta(client)
|
|
1171
|
+
anterior = _sign_in_cookie(client).headers[CSRF_HEADER]
|
|
1172
|
+
assert client.post("/auth/refresh", headers=_csrf_headers(client)).status_code == 200
|
|
1173
|
+
|
|
1174
|
+
repo = contenedor.sessions_repository()
|
|
1175
|
+
original = type(repo).list_family_session_ids
|
|
1176
|
+
|
|
1177
|
+
async def _explota(self, family_id, *, limit=100):
|
|
1178
|
+
raise RuntimeError("la base no responde")
|
|
1179
|
+
|
|
1180
|
+
type(repo).list_family_session_ids = _explota
|
|
1181
|
+
try:
|
|
1182
|
+
r = _escribir(client, anterior)
|
|
1183
|
+
finally:
|
|
1184
|
+
type(repo).list_family_session_ids = original
|
|
1185
|
+
|
|
1186
|
+
assert r.status_code == 403
|
|
1187
|
+
assert r.json()["code"] == CODIGO_INVALIDO
|
|
1188
|
+
|
|
1189
|
+
|
|
1190
|
+
def test_el_token_vigente_sigue_pasando_tras_la_rotacion(client, contenedor):
|
|
1191
|
+
"""La clasificación sólo corre al rechazar: el camino feliz no cambia."""
|
|
1192
|
+
_alta(client)
|
|
1193
|
+
_sign_in_cookie(client)
|
|
1194
|
+
assert client.post("/auth/refresh", headers=_csrf_headers(client)).status_code == 200
|
|
1195
|
+
|
|
1196
|
+
r = _escribir(client, client.cookies.get("csrf"))
|
|
1197
|
+
|
|
1198
|
+
assert r.status_code == 200, r.text
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|