kiba-core 0.5.3.dev66__tar.gz → 0.5.3.dev68__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.
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/CHANGELOG.md +3 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/PKG-INFO +1 -1
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/authorizer.py +9 -7
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/route.py +5 -3
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/queues/message_queue_processor.py +1 -1
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/kiba_core.egg-info/PKG-INFO +1 -1
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/pyproject.toml +1 -1
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/api/test_authorizer.py +32 -1
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/api/test_route.py +24 -6
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/.github/pull_request_template.md +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/.github/workflows/deploy.yml +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/.github/workflows/pull-request.yml +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/.github/workflows/release.yml +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/.gitignore +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/Dockerfile +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/README.md +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/__init__.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/__init__.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/api_request.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/api_response.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/default_routes.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/health.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/json_route.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/middleware/__init__.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/middleware/database_connection_middleware.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/middleware/exception_handling_middleware.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/middleware/logging_middleware.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/middleware/origin_ip_middleware.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/middleware/server_headers_middleware.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/openapi.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/rate_limit.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/request_context.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/route_auth.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/route_metadata.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/streaming_json_route.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/aws_requester.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/caching/__init__.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/caching/cache.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/caching/dict_cache.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/caching/file_cache.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/exceptions.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/http/__init__.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/http/basic_authentication.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/http/jwt.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/http/rest_method.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/locks/__init__.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/locks/lock.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/locks/model.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/locks/sql.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/logging.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/notifications/__init__.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/notifications/discord_client.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/notifications/notification_client.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/notifications/slack_client.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/py.typed +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/queues/__init__.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/queues/aqs.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/queues/cosmos.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/queues/message_queue.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/queues/model.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/queues/sql.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/queues/sqs.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/requester/__init__.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/requester/requester.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/s3_manager.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/service_client.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/store/__init__.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/store/database.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/store/retriever.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/store/saver.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/util/__init__.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/util/async_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/util/chain_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/util/date_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/util/dict_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/util/file_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/util/hashing_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/util/http_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/util/json_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/util/list_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/util/process_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/util/string_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/util/typing_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/util/url_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/util/value_holder.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/web3/__init__.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/web3/eth_client.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/web3/multicall3.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/kiba_core.egg-info/SOURCES.txt +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/kiba_core.egg-info/dependency_links.txt +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/kiba_core.egg-info/requires.txt +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/kiba_core.egg-info/top_level.txt +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/makefile +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/setup.cfg +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/__init__.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/api/test_api_response.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/api/test_exception_handling_middleware.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/api/test_json_route.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/api/test_openapi.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/api/test_origin_ip_middleware.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/api/test_rate_limit.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/api/test_request_context.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/api/test_streaming_json_route.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/caching/test_dict_cache.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/caching/test_file_cache.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/locks/test_sql_lock.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/queues/conftest.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/queues/test_aqs_queue.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/queues/test_cosmos_queue.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/queues/test_sql_queue.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/queues/test_sqs_queue.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/requester/__init__.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/requester/test_requester.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/test_database.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/test_exceptions.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/util/__init__.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/util/test_async_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/util/test_chain_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/util/test_date_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/util/test_dict_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/util/test_file_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/util/test_json_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/util/test_list_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/util/test_process_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/util/test_string_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/util/test_url_util.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/web3/__init__.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/web3/test_rest_eth_client.py +0 -0
- {kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/uv.lock +0 -0
|
@@ -8,6 +8,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
|
+
- [PATCH] Updated `MessageQueueProcessor` failure notifications to show the exception type before its message, so failures from exceptions without text (e.g. `httpx` timeouts) are still identifiable
|
|
12
|
+
- [MINOR] Added `authorize_bearer_jwt_request` for validating bearer JWTs in `RouteAuthResolver` implementations; it and `authorize_bearer_jwt` now only strip the leading `Bearer ` prefix, let cancellation propagate, and log why a JWT was rejected
|
|
13
|
+
- [MINOR] Made the `route` `authResolver` optional for routes without `auth`; setting `auth` without an `authResolver` raises `ValueError` at registration
|
|
11
14
|
- [MAJOR] Replaced `MessageQueue.delete_message` with `complete_message`, and added abstract `retry_message` (reschedule with a delay) and `fail_message` (apply the queue's failure policy); `MessageQueueProcessor` now reschedules `MessageNeedsReprocessingException` messages with `retry_message`
|
|
12
15
|
- [MINOR] Updated `MessageQueueProcessor` to retry messages that raise `LockedException` up to 3 times (30s linear backoff) before failing them
|
|
13
16
|
- [MINOR] Updated `MessageQueueProcessor` to log instead of raise when a failure notification cannot be sent
|
|
@@ -10,6 +10,7 @@ from core.api.api_request import KibaApiRequest
|
|
|
10
10
|
from core.api.route_metadata import OpenApiSecurityScheme
|
|
11
11
|
from core.api.route_metadata import update_route_metadata
|
|
12
12
|
from core.exceptions import ForbiddenException
|
|
13
|
+
from core.exceptions import KibaException
|
|
13
14
|
from core.exceptions import UnauthorizedException
|
|
14
15
|
from core.http.basic_authentication import BasicAuthentication
|
|
15
16
|
from core.http.jwt import Jwt
|
|
@@ -27,18 +28,19 @@ class SignatureAuthorizer:
|
|
|
27
28
|
raise NotImplementedError
|
|
28
29
|
|
|
29
30
|
|
|
30
|
-
async def
|
|
31
|
+
async def authorize_bearer_jwt_request[ApiRequest: BaseModel](request: KibaApiRequest[ApiRequest], authorizer: Authorizer) -> Jwt:
|
|
31
32
|
authorization = request.headers.get('Authorization')
|
|
32
33
|
if not authorization:
|
|
33
34
|
raise ForbiddenException(message='AUTH_NOT_PROVIDED')
|
|
34
35
|
if not authorization.startswith('Bearer '):
|
|
35
36
|
raise ForbiddenException(message='AUTH_INVALID')
|
|
36
|
-
jwtString = authorization.replace('Bearer ', '')
|
|
37
37
|
try:
|
|
38
|
-
|
|
39
|
-
except
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
return await authorizer.validate_jwt(jwtString=authorization.removeprefix('Bearer '))
|
|
39
|
+
except Exception as exception:
|
|
40
|
+
# NOTE(krishan711): only log kiba messages as other exception messages (e.g. json decode errors) can contain parts of the token
|
|
41
|
+
reason = exception.message if isinstance(exception, KibaException) else type(exception).__name__
|
|
42
|
+
logging.info(f'Bearer JWT rejected: {reason}')
|
|
43
|
+
raise ForbiddenException(message='AUTH_INVALID') from exception
|
|
42
44
|
|
|
43
45
|
|
|
44
46
|
def authorize_bearer_jwt[ApiRequest: BaseModel]( # type: ignore[explicit-any]
|
|
@@ -52,7 +54,7 @@ def authorize_bearer_jwt[ApiRequest: BaseModel]( # type: ignore[explicit-any]
|
|
|
52
54
|
|
|
53
55
|
@functools.wraps(func)
|
|
54
56
|
async def async_wrapper(request: KibaApiRequest[ApiRequest]) -> typing.Any: # type: ignore[explicit-any, misc]
|
|
55
|
-
request.authJwt = await
|
|
57
|
+
request.authJwt = await authorize_bearer_jwt_request(request=request, authorizer=authorizer)
|
|
56
58
|
result = func(request)
|
|
57
59
|
# NOTE(krishan711): this is here to support streaming responses which return an async generator
|
|
58
60
|
if hasattr(result, '__aiter__'):
|
|
@@ -42,7 +42,7 @@ def route[ApiRequest: BaseModel, ApiResponse: BaseModel]( # type: ignore[explic
|
|
|
42
42
|
requestType: typing.Type[ApiRequest],
|
|
43
43
|
responseType: typing.Type[ApiResponse],
|
|
44
44
|
*,
|
|
45
|
-
authResolver: RouteAuthResolver,
|
|
45
|
+
authResolver: RouteAuthResolver | None = None,
|
|
46
46
|
isStreaming: bool = False,
|
|
47
47
|
operationId: str | None = None,
|
|
48
48
|
summary: str | None = None,
|
|
@@ -52,13 +52,15 @@ def route[ApiRequest: BaseModel, ApiResponse: BaseModel]( # type: ignore[explic
|
|
|
52
52
|
rateLimit: RateLimitConfig | None = None,
|
|
53
53
|
extensions: Mapping[str, object] | None = None,
|
|
54
54
|
) -> typing.Callable[[typing.Callable[[KibaApiRequest[ApiRequest]], _AnyReturn]], typing.Callable[[Request], typing.Awaitable[KibaJSONResponse | StreamingResponse]]]:
|
|
55
|
-
securitySchemeNames = authResolver.get_route_security_schemes(auth=auth) if auth is not None else []
|
|
56
|
-
|
|
57
55
|
def decorator(func: typing.Callable[[KibaApiRequest[ApiRequest]], _AnyReturn]) -> typing.Callable[[Request], typing.Awaitable[KibaJSONResponse | StreamingResponse]]: # type: ignore[explicit-any]
|
|
58
56
|
handler: typing.Callable[[KibaApiRequest[ApiRequest]], _AnyReturn] = func
|
|
59
57
|
if rateLimit is not None:
|
|
60
58
|
handler = rate_limit(rateLimit)(handler)
|
|
59
|
+
securitySchemeNames: list[str] = []
|
|
61
60
|
if auth is not None:
|
|
61
|
+
if authResolver is None:
|
|
62
|
+
raise ValueError(f'Auth policy {auth} requires an authResolver')
|
|
63
|
+
securitySchemeNames = authResolver.get_route_security_schemes(auth=auth)
|
|
62
64
|
handler = _authorize_route(authResolver, auth)(handler)
|
|
63
65
|
endpointDecorator = streaming_json_route if isStreaming else json_route
|
|
64
66
|
endpoint = endpointDecorator(requestType=requestType, responseType=responseType)(handler) # type: ignore[arg-type, ty:invalid-argument-type]
|
|
@@ -53,7 +53,7 @@ class MessageQueueProcessor[MessageType: Message]:
|
|
|
53
53
|
await self.queue.fail_message(message=message, errorMessage=kibaException.message)
|
|
54
54
|
for client in self.notificationClients:
|
|
55
55
|
try:
|
|
56
|
-
await client.post(messageText=f'Error processing message: {message.command}\n```\n{requestId}\n{message.content}\n{kibaException.message}```')
|
|
56
|
+
await client.post(messageText=f'Error processing message: {message.command}\n```\n{requestId}\n{message.content}\n{kibaException.exceptionType}: {kibaException.message}```')
|
|
57
57
|
except Exception as notificationException: # noqa: BLE001
|
|
58
58
|
logging.error('Failed to send message failure notification:')
|
|
59
59
|
logging.exception(notificationException)
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
import asyncio
|
|
1
2
|
import json
|
|
2
3
|
import pytest
|
|
3
4
|
from collections.abc import AsyncIterator
|
|
5
|
+
from types import SimpleNamespace
|
|
4
6
|
from pydantic import BaseModel
|
|
5
7
|
from starlette.applications import Starlette
|
|
6
8
|
from starlette.routing import Route
|
|
7
9
|
from starlette.testclient import TestClient
|
|
8
10
|
|
|
9
11
|
from core.api.api_request import KibaApiRequest
|
|
10
|
-
from core.api.authorizer import Authorizer, SignatureAuthorizer, StaticTokenAuthorizer, authorize_bearer_jwt, authorize_signature, authorize_static_header_request, authorize_token
|
|
12
|
+
from core.api.authorizer import Authorizer, SignatureAuthorizer, StaticTokenAuthorizer, authorize_bearer_jwt, authorize_bearer_jwt_request, authorize_signature, authorize_static_header_request, authorize_token
|
|
11
13
|
from core.api.json_route import json_route
|
|
12
14
|
from core.api.middleware.exception_handling_middleware import ExceptionHandlingMiddleware
|
|
13
15
|
from core.api.streaming_json_route import streaming_json_route
|
|
@@ -159,6 +161,10 @@ def test_jwt_json_invalid_token_returns_403(jwt_json_client):
|
|
|
159
161
|
response = jwt_json_client.post('/protected', json={'value': 'hello'}, headers={'Authorization': 'Bearer bad-token'})
|
|
160
162
|
assert response.status_code == 403
|
|
161
163
|
|
|
164
|
+
def test_jwt_json_repeated_bearer_prefix_returns_403(jwt_json_client):
|
|
165
|
+
response = jwt_json_client.post('/protected', json={'value': 'hello'}, headers={'Authorization': f'Bearer Bearer {VALID_JWT_TOKEN}'})
|
|
166
|
+
assert response.status_code == 403
|
|
167
|
+
|
|
162
168
|
|
|
163
169
|
def test_jwt_json_sets_auth_jwt_on_request(jwt_json_client):
|
|
164
170
|
response = jwt_json_client.post('/protected', json={'value': 'hello'}, headers={'Authorization': f'Bearer {VALID_JWT_TOKEN}'})
|
|
@@ -186,6 +192,31 @@ def test_jwt_streaming_valid_token_streams_data(jwt_streaming_client):
|
|
|
186
192
|
assert data['user_id'] == VALID_USER_ID
|
|
187
193
|
|
|
188
194
|
|
|
195
|
+
# --- authorize_bearer_jwt_request ---
|
|
196
|
+
|
|
197
|
+
class RaisingJwtAuthorizer(Authorizer):
|
|
198
|
+
def __init__(self, exception: BaseException) -> None:
|
|
199
|
+
self.exception = exception
|
|
200
|
+
|
|
201
|
+
async def validate_jwt(self, jwtString: str) -> Jwt:
|
|
202
|
+
raise self.exception
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
@pytest.mark.asyncio
|
|
206
|
+
async def test_bearer_jwt_request_maps_unexpected_validation_errors_to_auth_invalid():
|
|
207
|
+
request = SimpleNamespace(headers={'Authorization': 'Bearer token'})
|
|
208
|
+
with pytest.raises(ForbiddenException) as exceptionInfo:
|
|
209
|
+
await authorize_bearer_jwt_request(request=request, authorizer=RaisingJwtAuthorizer(exception=ValueError('bad token')))
|
|
210
|
+
assert exceptionInfo.value.message == 'AUTH_INVALID'
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@pytest.mark.asyncio
|
|
214
|
+
async def test_bearer_jwt_request_propagates_cancellation():
|
|
215
|
+
request = SimpleNamespace(headers={'Authorization': 'Bearer token'})
|
|
216
|
+
with pytest.raises(asyncio.CancelledError):
|
|
217
|
+
await authorize_bearer_jwt_request(request=request, authorizer=RaisingJwtAuthorizer(exception=asyncio.CancelledError()))
|
|
218
|
+
|
|
219
|
+
|
|
189
220
|
# --- authorize_signature + json_route ---
|
|
190
221
|
|
|
191
222
|
def test_sig_json_no_auth_header_returns_403(sig_json_client):
|
|
@@ -176,9 +176,27 @@ def test_route_static_token_policy_accepts_valid_credentials() -> None:
|
|
|
176
176
|
|
|
177
177
|
def test_route_rejects_unknown_auth_policy_during_registration() -> None:
|
|
178
178
|
with pytest.raises(ValueError, match='Unknown auth policy: unknown'):
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
179
|
+
|
|
180
|
+
@api_route(requestType=ExampleRequest, responseType=ExampleResponse, authResolver=ExampleRouteAuthResolver(), auth='unknown')
|
|
181
|
+
async def endpoint(request: KibaApiRequest[ExampleRequest]) -> ExampleResponse:
|
|
182
|
+
return ExampleResponse(result=request.data.value)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def test_route_without_auth_does_not_require_an_auth_resolver() -> None:
|
|
186
|
+
@api_route(requestType=ExampleRequest, responseType=ExampleResponse)
|
|
187
|
+
async def endpoint(request: KibaApiRequest[ExampleRequest]) -> ExampleResponse:
|
|
188
|
+
return ExampleResponse(result=request.data.value)
|
|
189
|
+
|
|
190
|
+
client = TestClient(Starlette(routes=[Route('/example', endpoint, methods=['POST'])]), raise_server_exceptions=False)
|
|
191
|
+
response = client.post('/example', json={'value': 'hello'})
|
|
192
|
+
|
|
193
|
+
assert response.status_code == 200
|
|
194
|
+
assert response.json() == {'result': 'hello'}
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def test_route_rejects_auth_policy_without_auth_resolver_during_registration() -> None:
|
|
198
|
+
with pytest.raises(ValueError, match='Auth policy signature requires an authResolver'):
|
|
199
|
+
|
|
200
|
+
@api_route(requestType=ExampleRequest, responseType=ExampleResponse, auth='signature')
|
|
201
|
+
async def endpoint(request: KibaApiRequest[ExampleRequest]) -> ExampleResponse:
|
|
202
|
+
return ExampleResponse(result=request.data.value)
|
|
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
|
{kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/middleware/exception_handling_middleware.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/core/api/middleware/server_headers_middleware.py
RENAMED
|
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
|
|
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
|
{kiba_core-0.5.3.dev66 → kiba_core-0.5.3.dev68}/tests/api/test_exception_handling_middleware.py
RENAMED
|
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
|