crypticorn-utils 0.1.0rc1__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.
Files changed (45) hide show
  1. crypticorn_utils-0.1.0rc1/LICENSE +15 -0
  2. crypticorn_utils-0.1.0rc1/MANIFEST.in +3 -0
  3. crypticorn_utils-0.1.0rc1/PKG-INFO +95 -0
  4. crypticorn_utils-0.1.0rc1/README.md +48 -0
  5. crypticorn_utils-0.1.0rc1/crypticorn_utils/__init__.py +16 -0
  6. crypticorn_utils-0.1.0rc1/crypticorn_utils/_migration.py +12 -0
  7. crypticorn_utils-0.1.0rc1/crypticorn_utils/ansi_colors.py +41 -0
  8. crypticorn_utils-0.1.0rc1/crypticorn_utils/auth.py +345 -0
  9. crypticorn_utils-0.1.0rc1/crypticorn_utils/cli/__init__.py +4 -0
  10. crypticorn_utils-0.1.0rc1/crypticorn_utils/cli/__main__.py +17 -0
  11. crypticorn_utils-0.1.0rc1/crypticorn_utils/cli/init.py +127 -0
  12. crypticorn_utils-0.1.0rc1/crypticorn_utils/cli/templates/__init__.py +0 -0
  13. crypticorn_utils-0.1.0rc1/crypticorn_utils/cli/templates/auth.py +33 -0
  14. crypticorn_utils-0.1.0rc1/crypticorn_utils/cli/version.py +8 -0
  15. crypticorn_utils-0.1.0rc1/crypticorn_utils/decorators.py +38 -0
  16. crypticorn_utils-0.1.0rc1/crypticorn_utils/enums.py +175 -0
  17. crypticorn_utils-0.1.0rc1/crypticorn_utils/errors.py +915 -0
  18. crypticorn_utils-0.1.0rc1/crypticorn_utils/exceptions.py +183 -0
  19. crypticorn_utils-0.1.0rc1/crypticorn_utils/logging.py +130 -0
  20. crypticorn_utils-0.1.0rc1/crypticorn_utils/metrics.py +32 -0
  21. crypticorn_utils-0.1.0rc1/crypticorn_utils/middleware.py +125 -0
  22. crypticorn_utils-0.1.0rc1/crypticorn_utils/mixins.py +68 -0
  23. crypticorn_utils-0.1.0rc1/crypticorn_utils/openapi.py +10 -0
  24. crypticorn_utils-0.1.0rc1/crypticorn_utils/pagination.py +286 -0
  25. crypticorn_utils-0.1.0rc1/crypticorn_utils/router/admin_router.py +117 -0
  26. crypticorn_utils-0.1.0rc1/crypticorn_utils/router/status_router.py +36 -0
  27. crypticorn_utils-0.1.0rc1/crypticorn_utils/utils.py +93 -0
  28. crypticorn_utils-0.1.0rc1/crypticorn_utils/warnings.py +79 -0
  29. crypticorn_utils-0.1.0rc1/crypticorn_utils.egg-info/PKG-INFO +95 -0
  30. crypticorn_utils-0.1.0rc1/crypticorn_utils.egg-info/SOURCES.txt +43 -0
  31. crypticorn_utils-0.1.0rc1/crypticorn_utils.egg-info/dependency_links.txt +1 -0
  32. crypticorn_utils-0.1.0rc1/crypticorn_utils.egg-info/entry_points.txt +2 -0
  33. crypticorn_utils-0.1.0rc1/crypticorn_utils.egg-info/requires.txt +25 -0
  34. crypticorn_utils-0.1.0rc1/crypticorn_utils.egg-info/top_level.txt +1 -0
  35. crypticorn_utils-0.1.0rc1/pyproject.toml +69 -0
  36. crypticorn_utils-0.1.0rc1/requirements/dev.txt +10 -0
  37. crypticorn_utils-0.1.0rc1/requirements/main.txt +7 -0
  38. crypticorn_utils-0.1.0rc1/requirements/test.txt +7 -0
  39. crypticorn_utils-0.1.0rc1/setup.cfg +4 -0
  40. crypticorn_utils-0.1.0rc1/tests/test_auth_client.py +361 -0
  41. crypticorn_utils-0.1.0rc1/tests/test_common_routers.py +111 -0
  42. crypticorn_utils-0.1.0rc1/tests/test_config.py +22 -0
  43. crypticorn_utils-0.1.0rc1/tests/test_enums.py +44 -0
  44. crypticorn_utils-0.1.0rc1/tests/test_errors.py +63 -0
  45. crypticorn_utils-0.1.0rc1/tests/test_pagination.py +334 -0
@@ -0,0 +1,15 @@
1
+ Copyright © 2025 Crypticorn
2
+
3
+ All rights reserved. This software and accompanying documentation files (collectively referred to as "the Software") are the proprietary property of Crypticorn. Any unauthorised reproduction, modification, integration, publication, distribution, sublicensing, sale, or enabling access to the Software without explicit written permission from Crypticorn is strictly prohibited, subject to the conditions outlined herein:
4
+
5
+ 1. The aforementioned copyright notice and this permission notice must be included unaltered in all copies, or significant portions, of the Software.
6
+
7
+ 2. The Software is provided "AS IS", without any warranty, whether express or implied. Crypticorn expressly disclaims any guarantees, including but not limited to those of satisfactory quality, fitness for a particular purpose, or non-infringement of third-party rights.
8
+
9
+ 3. In no event shall Crypticorn, its authors, or copyright holders be held liable for any claim, damages, or other liability arising from or associated with the use of the Software or any other dealings involving the Software, to the extent that such liability can be limited or excluded by applicable law.
10
+
11
+ Changes to this agreement may be made without notice, and it is the user's responsibility to keep abreast of any changes. This agreement and its interpretation are subject to the laws of the Federal Republic of Germany.
12
+
13
+ If any provision of this license agreement is held to be invalid or unenforceable under applicable law, the remaining provisions will continue in full force and effect. Crypticorn's failure to enforce any provision of this agreement does not constitute a waiver of its right to do so in the future.
14
+
15
+ This agreement is written in English. In the event of any discrepancy between the English version and any translation, the English version shall prevail.
@@ -0,0 +1,3 @@
1
+ graft crypticorn/cli/templates
2
+ include README.md
3
+ include LICENSE
@@ -0,0 +1,95 @@
1
+ Metadata-Version: 2.4
2
+ Name: crypticorn_utils
3
+ Version: 0.1.0rc1
4
+ Summary: Shared utilities for the Crypticorn APIs
5
+ Author-email: Crypticorn <timon@crypticorn.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://crypticorn.com
8
+ Project-URL: Documentation, https://docs.crypticorn.com
9
+ Project-URL: Dashboard, https://app.crypticorn.com
10
+ Classifier: Topic :: Scientific/Engineering
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.9
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: fastapi<1.0.0,>=0.115.0
24
+ Requires-Dist: click<9.0.0,>=8.0.0
25
+ Requires-Dist: psutil<8.0.0,>=7.0.0
26
+ Requires-Dist: setuptools<81.0.0,>=80.0.0
27
+ Requires-Dist: strenum
28
+ Requires-Dist: prometheus-client<1.0.0,>=0.22.0
29
+ Provides-Extra: dev
30
+ Requires-Dist: build; extra == "dev"
31
+ Requires-Dist: twine; extra == "dev"
32
+ Requires-Dist: black; extra == "dev"
33
+ Requires-Dist: ruff; extra == "dev"
34
+ Requires-Dist: isort; extra == "dev"
35
+ Requires-Dist: mypy; extra == "dev"
36
+ Requires-Dist: openapi-generator-cli<8.0.0,>=7.12.0; extra == "dev"
37
+ Requires-Dist: python-semantic-release==9.21.0; extra == "dev"
38
+ Provides-Extra: test
39
+ Requires-Dist: pytest==8.3.5; extra == "test"
40
+ Requires-Dist: pytest-asyncio==0.26.0; extra == "test"
41
+ Requires-Dist: pytest-cov==6.1.1; extra == "test"
42
+ Requires-Dist: python-dotenv==1.0.1; extra == "test"
43
+ Requires-Dist: PyJWT==2.10.0; extra == "test"
44
+ Requires-Dist: httpx>=0.27.0; extra == "test"
45
+ Requires-Dist: crypticorn>=2.17.0; extra == "test"
46
+ Dynamic: license-file
47
+
48
+ This module serves as a central place for providing utilities for our python backends.
49
+
50
+ - **Auth**: Authentication and authorization for APIs with API key and JWT bearer token support
51
+ - **Ansi Colors**: ANSI color codes for terminal text formatting and colorful console output
52
+ - **Decorators**: Utility decorators for model manipulation, including `partial_model` for optional fields
53
+ - **Enums**: Common enumerations for type safety and consistency
54
+ - **Errors**: Comprehensive error handling system with HTTP exceptions and error content structures
55
+ - **Exceptions**: Custom exception classes and error handling utilities
56
+ - **Logging**: Logging configuration and utilities for consistent formatting
57
+ - **Middleware**: API middleware components for request/response processing
58
+ - **Mixins**: Reusable functionality components for class mixing
59
+ - **Pagination**: Utilities for paginated API responses and cursor-based pagination
60
+ - **Router**: API routing utilities and components
61
+ - **Scopes**: Authorization scope definitions and management for API access control
62
+ - **Urls**: URL management utilities including base URLs, service endpoints, and API versioning
63
+ - **Utils**: General utility functions and helper methods
64
+ - **Warnings**: Warning handling and custom warning types
65
+ - **Metrics**: Shared metrics collection from APIs for visualization
66
+
67
+ # Changelog
68
+
69
+ <!-- changelog-insertion -->
70
+
71
+ ## v0.1.0-rc.1 (2025-06-23)
72
+
73
+ ### Documentation
74
+
75
+ - Add changelog
76
+ ([`788f1f6`](https://github.com/crypticorn-ai/util-libraries/commit/788f1f670a8a50251401ebd1fc9ab7d2ca855a8d))
77
+
78
+ - Update Readme
79
+ ([`d2b52cf`](https://github.com/crypticorn-ai/util-libraries/commit/d2b52cfe48de7a8b248ceefbc3bc7007ad21ea72))
80
+
81
+ ### Features
82
+
83
+ - Initial release
84
+ ([`4da5fe3`](https://github.com/crypticorn-ai/util-libraries/commit/4da5fe3d33abd31b3b35462e93052db0cde077c2))
85
+
86
+
87
+ ## Unreleased
88
+
89
+ ### Documentation
90
+
91
+ - Add changelog
92
+ ([`788f1f6`](https://github.com/crypticorn-ai/util-libraries/commit/788f1f670a8a50251401ebd1fc9ab7d2ca855a8d))
93
+
94
+ - Update Readme
95
+ ([`d2b52cf`](https://github.com/crypticorn-ai/util-libraries/commit/d2b52cfe48de7a8b248ceefbc3bc7007ad21ea72))
@@ -0,0 +1,48 @@
1
+ This module serves as a central place for providing utilities for our python backends.
2
+
3
+ - **Auth**: Authentication and authorization for APIs with API key and JWT bearer token support
4
+ - **Ansi Colors**: ANSI color codes for terminal text formatting and colorful console output
5
+ - **Decorators**: Utility decorators for model manipulation, including `partial_model` for optional fields
6
+ - **Enums**: Common enumerations for type safety and consistency
7
+ - **Errors**: Comprehensive error handling system with HTTP exceptions and error content structures
8
+ - **Exceptions**: Custom exception classes and error handling utilities
9
+ - **Logging**: Logging configuration and utilities for consistent formatting
10
+ - **Middleware**: API middleware components for request/response processing
11
+ - **Mixins**: Reusable functionality components for class mixing
12
+ - **Pagination**: Utilities for paginated API responses and cursor-based pagination
13
+ - **Router**: API routing utilities and components
14
+ - **Scopes**: Authorization scope definitions and management for API access control
15
+ - **Urls**: URL management utilities including base URLs, service endpoints, and API versioning
16
+ - **Utils**: General utility functions and helper methods
17
+ - **Warnings**: Warning handling and custom warning types
18
+ - **Metrics**: Shared metrics collection from APIs for visualization
19
+
20
+ # Changelog
21
+
22
+ <!-- changelog-insertion -->
23
+
24
+ ## v0.1.0-rc.1 (2025-06-23)
25
+
26
+ ### Documentation
27
+
28
+ - Add changelog
29
+ ([`788f1f6`](https://github.com/crypticorn-ai/util-libraries/commit/788f1f670a8a50251401ebd1fc9ab7d2ca855a8d))
30
+
31
+ - Update Readme
32
+ ([`d2b52cf`](https://github.com/crypticorn-ai/util-libraries/commit/d2b52cfe48de7a8b248ceefbc3bc7007ad21ea72))
33
+
34
+ ### Features
35
+
36
+ - Initial release
37
+ ([`4da5fe3`](https://github.com/crypticorn-ai/util-libraries/commit/4da5fe3d33abd31b3b35462e93052db0cde077c2))
38
+
39
+
40
+ ## Unreleased
41
+
42
+ ### Documentation
43
+
44
+ - Add changelog
45
+ ([`788f1f6`](https://github.com/crypticorn-ai/util-libraries/commit/788f1f670a8a50251401ebd1fc9ab7d2ca855a8d))
46
+
47
+ - Update Readme
48
+ ([`d2b52cf`](https://github.com/crypticorn-ai/util-libraries/commit/d2b52cfe48de7a8b248ceefbc3bc7007ad21ea72))
@@ -0,0 +1,16 @@
1
+ from crypticorn_utils.ansi_colors import *
2
+ from crypticorn_utils.auth import *
3
+ from crypticorn_utils.decorators import *
4
+ from crypticorn_utils.enums import *
5
+ from crypticorn_utils.errors import *
6
+ from crypticorn_utils.exceptions import *
7
+ from crypticorn_utils.logging import *
8
+ from crypticorn_utils.metrics import *
9
+ from crypticorn_utils.middleware import *
10
+ from crypticorn_utils.mixins import *
11
+ from crypticorn_utils.openapi import *
12
+ from crypticorn_utils.pagination import *
13
+ from crypticorn_utils.router.admin_router import router as admin_router
14
+ from crypticorn_utils.router.status_router import router as status_router
15
+ from crypticorn_utils.utils import *
16
+ from crypticorn_utils.warnings import *
@@ -0,0 +1,12 @@
1
+ # This file is used to check if the crypticorn version is greater than 2.18
2
+ # This is to be compatible with this new utils library
3
+
4
+ import importlib.metadata
5
+ from importlib.metadata import PackageNotFoundError
6
+
7
+ try:
8
+ crypticorn_version = importlib.metadata.distribution("crypticorn").version
9
+ parts = crypticorn_version.split(".")
10
+ has_migrated = parts[0] >= "2" and parts[1] > "18"
11
+ except PackageNotFoundError:
12
+ has_migrated = False
@@ -0,0 +1,41 @@
1
+ try:
2
+ from enum import StrEnum
3
+ except ImportError:
4
+ from strenum import StrEnum
5
+
6
+
7
+ class AnsiColors(StrEnum):
8
+ """Provides a collection of ANSI color codes for terminal text formatting, including regular, bright, and bold text colors. Useful for creating colorful and readable console output."""
9
+
10
+ # Regular Text Colors
11
+ BLACK = "\033[30m" # black
12
+ RED = "\033[31m" # red
13
+ GREEN = "\033[32m" # green
14
+ YELLOW = "\033[33m" # yellow
15
+ BLUE = "\033[34m" # blue
16
+ MAGENTA = "\033[35m" # magenta
17
+ CYAN = "\033[36m" # cyan
18
+ WHITE = "\033[37m" # white
19
+
20
+ # Bright Text Colors
21
+ BLACK_BRIGHT = "\033[90m" # black_bright
22
+ RED_BRIGHT = "\033[91m" # red_bright
23
+ GREEN_BRIGHT = "\033[92m" # green_bright
24
+ YELLOW_BRIGHT = "\033[93m" # yellow_bright
25
+ BLUE_BRIGHT = "\033[94m" # blue_bright
26
+ MAGENTA_BRIGHT = "\033[95m" # magenta_bright
27
+ CYAN_BRIGHT = "\033[96m" # cyan_bright
28
+ WHITE_BRIGHT = "\033[97m" # white_bright
29
+
30
+ # Bold Text Colors
31
+ BLACK_BOLD = "\033[1;30m" # black_bold
32
+ RED_BOLD = "\033[1;31m" # red_bold
33
+ GREEN_BOLD = "\033[1;32m" # green_bold
34
+ YELLOW_BOLD = "\033[1;33m" # yellow_bold
35
+ BLUE_BOLD = "\033[1;34m" # blue_bold
36
+ MAGENTA_BOLD = "\033[1;35m" # magenta_bold
37
+ CYAN_BOLD = "\033[1;36m" # cyan_bold
38
+ WHITE_BOLD = "\033[1;37m" # white_bold
39
+
40
+ # Reset Color
41
+ RESET = "\033[0m"
@@ -0,0 +1,345 @@
1
+ import json
2
+ from typing import Union
3
+
4
+ from crypticorn.auth import AuthClient, Configuration, Verify200Response
5
+ from crypticorn.auth.client.exceptions import ApiException
6
+ from crypticorn_utils.enums import Scope, BaseUrl
7
+ from crypticorn_utils.exceptions import ApiError, ExceptionContent, HTTPException
8
+ from fastapi import Depends, Query
9
+ from fastapi.security import (
10
+ APIKeyHeader,
11
+ HTTPAuthorizationCredentials,
12
+ HTTPBasic,
13
+ HTTPBasicCredentials,
14
+ HTTPBearer,
15
+ SecurityScopes,
16
+ )
17
+ from typing_extensions import Annotated
18
+
19
+ AUTHENTICATE_HEADER = "WWW-Authenticate"
20
+ BEARER_AUTH_SCHEME = "Bearer"
21
+ APIKEY_AUTH_SCHEME = "X-API-Key"
22
+ BASIC_AUTH_SCHEME = "Basic"
23
+
24
+ # Auth Schemes
25
+ http_bearer = HTTPBearer(
26
+ bearerFormat="JWT",
27
+ auto_error=False,
28
+ description="The JWT to use for authentication.",
29
+ )
30
+
31
+ apikey_header = APIKeyHeader(
32
+ name=APIKEY_AUTH_SCHEME,
33
+ auto_error=False,
34
+ description="The API key to use for authentication.",
35
+ )
36
+
37
+ http_basic = HTTPBasic(
38
+ scheme_name=BASIC_AUTH_SCHEME,
39
+ auto_error=False,
40
+ description="The username and password to use for authentication.",
41
+ )
42
+
43
+
44
+ # Auth Handler
45
+ class AuthHandler:
46
+ """
47
+ Middleware for verifying API requests. Verifies the validity of the authentication token, scopes, etc.
48
+
49
+ :param base_url: The base URL of the API.
50
+ :param api_version: The version of the API.
51
+ """
52
+
53
+ def __init__(
54
+ self,
55
+ base_url: BaseUrl = BaseUrl.PROD,
56
+ ):
57
+ self.url = f"{base_url}/v1/auth"
58
+ self.client = AuthClient(Configuration(host=self.url), is_sync=False)
59
+
60
+ async def _verify_api_key(self, api_key: str) -> Verify200Response:
61
+ """
62
+ Verifies the API key.
63
+ """
64
+ # self.client.config.api_key = {apikey_header.scheme_name: api_key}
65
+ return await self.client.login.verify_api_key(api_key)
66
+
67
+ async def _verify_bearer(
68
+ self, bearer: HTTPAuthorizationCredentials
69
+ ) -> Verify200Response:
70
+ """
71
+ Verifies the bearer token.
72
+ """
73
+ self.client.config.access_token = bearer.credentials
74
+ return await self.client.login.verify()
75
+
76
+ async def _verify_basic(self, basic: HTTPBasicCredentials) -> Verify200Response:
77
+ """
78
+ Verifies the basic authentication credentials.
79
+ """
80
+ return await self.client.login.verify_basic_auth(basic.username, basic.password)
81
+
82
+ async def _validate_scopes(
83
+ self, api_scopes: list[Scope], user_scopes: list[Scope]
84
+ ) -> bool:
85
+ """
86
+ Checks if the required scopes are a subset of the user scopes.
87
+ """
88
+ if not set(api_scopes).issubset(user_scopes):
89
+ raise HTTPException(
90
+ content=ExceptionContent(
91
+ error=ApiError.INSUFFICIENT_SCOPES,
92
+ message="Insufficient scopes to access this resource (required: "
93
+ + ", ".join(api_scopes)
94
+ + ")",
95
+ ),
96
+ )
97
+
98
+ async def _extract_message(self, e: ApiException) -> str:
99
+ """
100
+ Tries to extract the message from the body of the exception.
101
+ """
102
+ try:
103
+ load = json.loads(e.body)
104
+ except (json.JSONDecodeError, TypeError):
105
+ return e.body
106
+ else:
107
+ common_keys = ["message"]
108
+ for key in common_keys:
109
+ if key in load:
110
+ return load[key]
111
+ return load
112
+
113
+ async def _handle_exception(self, e: Exception) -> HTTPException:
114
+ """
115
+ Handles exceptions and returns a HTTPException with the appropriate status code and detail.
116
+ """
117
+ if isinstance(e, ApiException):
118
+ # handle the TRPC Zod errors from auth-service
119
+ # Unfortunately, we cannot share the error messages defined in python/crypticorn/common/errors.py with the typescript client
120
+ message = await self._extract_message(e)
121
+ if message == "Invalid API key":
122
+ error = ApiError.INVALID_API_KEY
123
+ elif message == "API key expired":
124
+ error = ApiError.EXPIRED_API_KEY
125
+ elif message == "jwt expired":
126
+ error = ApiError.EXPIRED_BEARER
127
+ elif message == "Invalid basic authentication credentials":
128
+ error = ApiError.INVALID_BASIC_AUTH
129
+ else:
130
+ message = "Invalid bearer token"
131
+ error = (
132
+ ApiError.INVALID_BEARER
133
+ ) # jwt malformed, jwt not active (https://www.npmjs.com/package/jsonwebtoken#errors--codes)
134
+ return HTTPException(
135
+ content=ExceptionContent(
136
+ error=error,
137
+ message=message,
138
+ ),
139
+ )
140
+ elif isinstance(e, HTTPException):
141
+ return e
142
+ else:
143
+ return HTTPException(
144
+ content=ExceptionContent(
145
+ error=ApiError.UNKNOWN_ERROR,
146
+ message=str(e),
147
+ ),
148
+ )
149
+
150
+ async def api_key_auth(
151
+ self,
152
+ api_key: Annotated[Union[str, None], Depends(apikey_header)] = None,
153
+ sec: SecurityScopes = SecurityScopes(),
154
+ ) -> Verify200Response:
155
+ """
156
+ Verifies the API key and checks the scopes.
157
+ Use this function if you only want to allow access via the API key.
158
+ This function is used for HTTP connections.
159
+ """
160
+ try:
161
+ return await self.full_auth(
162
+ bearer=None, api_key=api_key, basic=None, sec=sec
163
+ )
164
+ except HTTPException as e:
165
+ raise HTTPException(
166
+ content=ExceptionContent(
167
+ error=ApiError.from_json(e.detail),
168
+ message=e.detail.get("message"),
169
+ ),
170
+ headers={AUTHENTICATE_HEADER: APIKEY_AUTH_SCHEME},
171
+ )
172
+
173
+ async def bearer_auth(
174
+ self,
175
+ bearer: Annotated[
176
+ Union[HTTPAuthorizationCredentials, None],
177
+ Depends(http_bearer),
178
+ ] = None,
179
+ sec: SecurityScopes = SecurityScopes(),
180
+ ) -> Verify200Response:
181
+ """
182
+ Verifies the bearer token and checks the scopes.
183
+ Use this function if you only want to allow access via the bearer token.
184
+ This function is used for HTTP connections.
185
+ """
186
+ try:
187
+ return await self.full_auth(
188
+ bearer=bearer, api_key=None, basic=None, sec=sec
189
+ )
190
+ except HTTPException as e:
191
+ raise HTTPException(
192
+ content=ExceptionContent(
193
+ error=ApiError.from_json(e.detail),
194
+ message=e.detail.get("message"),
195
+ ),
196
+ headers={AUTHENTICATE_HEADER: BEARER_AUTH_SCHEME},
197
+ )
198
+
199
+ async def basic_auth(
200
+ self,
201
+ credentials: Annotated[Union[HTTPBasicCredentials, None], Depends(http_basic)],
202
+ ) -> Verify200Response:
203
+ """
204
+ Verifies the basic authentication credentials. This authentication method should just be used for special cases like /admin/metrics, where JWT and API key authentication are not desired or not possible.
205
+ """
206
+ try:
207
+ return await self.full_auth(
208
+ basic=credentials, bearer=None, api_key=None, sec=None
209
+ )
210
+ except HTTPException as e:
211
+ raise HTTPException(
212
+ content=ExceptionContent(
213
+ error=ApiError.from_json(e.detail),
214
+ message=e.detail.get("message"),
215
+ ),
216
+ headers={AUTHENTICATE_HEADER: BASIC_AUTH_SCHEME},
217
+ )
218
+
219
+ async def combined_auth(
220
+ self,
221
+ bearer: Annotated[
222
+ Union[HTTPAuthorizationCredentials, None], Depends(http_bearer)
223
+ ] = None,
224
+ api_key: Annotated[Union[str, None], Depends(apikey_header)] = None,
225
+ sec: SecurityScopes = SecurityScopes(),
226
+ ) -> Verify200Response:
227
+ """
228
+ Verifies the bearer token and/or API key and checks the scopes.
229
+ Returns early on the first successful verification, otherwise tries all available tokens.
230
+ Use this function if you want to allow access via either the bearer token or the API key.
231
+ This function is used for HTTP connections.
232
+ """
233
+ try:
234
+ return await self.full_auth(
235
+ basic=None, bearer=bearer, api_key=api_key, sec=sec
236
+ )
237
+ except HTTPException as e:
238
+ raise HTTPException(
239
+ content=ExceptionContent(
240
+ error=ApiError.from_json(e.detail),
241
+ message=e.detail.get("message"),
242
+ ),
243
+ headers={
244
+ AUTHENTICATE_HEADER: f"{BEARER_AUTH_SCHEME}, {APIKEY_AUTH_SCHEME}"
245
+ },
246
+ )
247
+
248
+ async def full_auth(
249
+ self,
250
+ basic: Annotated[Union[HTTPBasicCredentials, None], Depends(http_basic)] = None,
251
+ bearer: Annotated[
252
+ Union[HTTPAuthorizationCredentials, None], Depends(http_bearer)
253
+ ] = None,
254
+ api_key: Annotated[Union[str, None], Depends(apikey_header)] = None,
255
+ sec: SecurityScopes = SecurityScopes(),
256
+ ) -> Verify200Response:
257
+ """
258
+ IMPORTANT: combined_auth is sufficient for most use cases. This function adds basic auth to the mix, which is needed for external services like prometheus, but is not recommended for internal use.
259
+ Verifies the bearer token, API key and basic authentication credentials and checks the scopes.
260
+ Returns early on the first successful verification, otherwise tries all available tokens.
261
+ Use this function if you want to allow access via either the bearer token, the API key or the basic authentication credentials.
262
+ This function is used for HTTP connections.
263
+ """
264
+ tokens = [bearer, api_key, basic]
265
+ last_error = None
266
+ for token in tokens:
267
+ try:
268
+ if token is None:
269
+ continue
270
+ res = None
271
+ if isinstance(token, str):
272
+ res = await self._verify_api_key(token)
273
+ elif isinstance(token, HTTPAuthorizationCredentials):
274
+ res = await self._verify_bearer(token)
275
+ elif isinstance(token, HTTPBasicCredentials):
276
+ res = await self._verify_basic(token)
277
+ if res is None:
278
+ continue
279
+ if sec:
280
+ await self._validate_scopes(sec.scopes, res.scopes)
281
+ return res
282
+
283
+ except Exception as e:
284
+ last_error = await self._handle_exception(e)
285
+ continue
286
+
287
+ if last_error:
288
+ raise last_error
289
+ else:
290
+ raise HTTPException(
291
+ content=ExceptionContent(
292
+ error=ApiError.NO_CREDENTIALS,
293
+ message="No credentials provided. Check the WWW-Authenticate header for the available authentication methods.",
294
+ ),
295
+ headers={
296
+ AUTHENTICATE_HEADER: f"{BEARER_AUTH_SCHEME}, {APIKEY_AUTH_SCHEME}, {BASIC_AUTH_SCHEME}"
297
+ },
298
+ )
299
+
300
+ async def ws_api_key_auth(
301
+ self,
302
+ api_key: Annotated[Union[str, None], Query()] = None,
303
+ sec: SecurityScopes = SecurityScopes(),
304
+ ) -> Verify200Response:
305
+ """
306
+ Verifies the API key and checks the scopes.
307
+ Use this function if you only want to allow access via the API key.
308
+ This function is used for WebSocket connections.
309
+ """
310
+ return await self.api_key_auth(api_key=api_key, sec=sec)
311
+
312
+ async def ws_bearer_auth(
313
+ self,
314
+ bearer: Annotated[Union[str, None], Query()] = None,
315
+ sec: SecurityScopes = SecurityScopes(),
316
+ ) -> Verify200Response:
317
+ """
318
+ Verifies the bearer token and checks the scopes.
319
+ Use this function if you only want to allow access via the bearer token.
320
+ This function is used for WebSocket connections.
321
+ """
322
+ credentials = (
323
+ HTTPAuthorizationCredentials(scheme="Bearer", credentials=bearer)
324
+ if bearer
325
+ else None
326
+ )
327
+ return await self.bearer_auth(bearer=credentials, sec=sec)
328
+
329
+ async def ws_combined_auth(
330
+ self,
331
+ bearer: Annotated[Union[str, None], Query()] = None,
332
+ api_key: Annotated[Union[str, None], Query()] = None,
333
+ sec: SecurityScopes = SecurityScopes(),
334
+ ) -> Verify200Response:
335
+ """
336
+ Verifies the bearer token and/or API key and checks the scopes.
337
+ Use this function if you want to allow access via either the bearer token or the API key.
338
+ This function is used for WebSocket connections.
339
+ """
340
+ credentials = (
341
+ HTTPAuthorizationCredentials(scheme="Bearer", credentials=bearer)
342
+ if bearer
343
+ else None
344
+ )
345
+ return await self.combined_auth(bearer=credentials, api_key=api_key, sec=sec)
@@ -0,0 +1,4 @@
1
+ from crypticorn.cli.init import init_group
2
+ from crypticorn.cli.version import version
3
+
4
+ __all__ = ["init_group", "version"]
@@ -0,0 +1,17 @@
1
+ # crypticorn/cli.py
2
+
3
+ import click
4
+ from crypticorn.cli import init_group, version
5
+
6
+
7
+ @click.group()
8
+ def cli():
9
+ """🧙 Crypticorn CLI — magic for our microservices."""
10
+ pass
11
+
12
+
13
+ cli.add_command(init_group, name="init")
14
+ cli.add_command(version, name="version")
15
+
16
+ if __name__ == "__main__":
17
+ cli()