crypticorn 1.0.2rc3__py3-none-any.whl → 2.0.0__py3-none-any.whl

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 (259) hide show
  1. crypticorn/__init__.py +3 -3
  2. crypticorn/auth/__init__.py +2 -0
  3. crypticorn/auth/client/__init__.py +112 -0
  4. crypticorn/auth/client/api/__init__.py +8 -0
  5. crypticorn/auth/client/api/admin_api.py +522 -0
  6. crypticorn/auth/client/api/auth_api.py +2089 -0
  7. crypticorn/auth/client/api/service_api.py +309 -0
  8. crypticorn/auth/client/api/user_api.py +2540 -0
  9. crypticorn/auth/client/api/wallet_api.py +1698 -0
  10. crypticorn/auth/client/api_client.py +758 -0
  11. crypticorn/auth/client/api_response.py +20 -0
  12. crypticorn/auth/client/configuration.py +584 -0
  13. crypticorn/auth/client/exceptions.py +220 -0
  14. crypticorn/auth/client/models/__init__.py +91 -0
  15. crypticorn/auth/client/models/add_wallet200_response.py +86 -0
  16. crypticorn/auth/client/models/add_wallet_request.py +107 -0
  17. crypticorn/auth/client/models/authorize_user200_response.py +107 -0
  18. crypticorn/auth/client/models/authorize_user200_response_auth.py +101 -0
  19. crypticorn/auth/client/models/authorize_user_request.py +96 -0
  20. crypticorn/auth/client/models/create_user_request.py +114 -0
  21. crypticorn/auth/client/models/list_wallets200_response.py +137 -0
  22. crypticorn/auth/client/models/list_wallets200_response_balances_inner.py +115 -0
  23. crypticorn/auth/client/models/list_wallets200_response_balances_inner_sale_round.py +115 -0
  24. crypticorn/auth/client/models/list_wallets200_response_balances_inner_wallet.py +168 -0
  25. crypticorn/auth/client/models/list_wallets200_response_balances_inner_wallet_vesting_wallets_inner.py +191 -0
  26. crypticorn/auth/client/models/list_wallets200_response_data_inner.py +102 -0
  27. crypticorn/auth/client/models/list_wallets200_response_user_value.py +118 -0
  28. crypticorn/auth/client/models/logout_default_response.py +108 -0
  29. crypticorn/auth/client/models/logout_default_response_issues_inner.py +83 -0
  30. crypticorn/auth/client/models/refresh_token_info200_response.py +97 -0
  31. crypticorn/auth/client/models/refresh_token_info200_response_user_session.py +105 -0
  32. crypticorn/auth/client/models/resend_verification_email_request.py +84 -0
  33. crypticorn/auth/client/models/revoke_user_tokens_request.py +83 -0
  34. crypticorn/auth/client/models/rotate_tokens200_response.py +110 -0
  35. crypticorn/auth/client/models/token_info200_response.py +97 -0
  36. crypticorn/auth/client/models/unlink_wallet_request.py +83 -0
  37. crypticorn/auth/client/models/update_user_request.py +93 -0
  38. crypticorn/auth/client/models/user_reset_password_request.py +87 -0
  39. crypticorn/auth/client/models/user_set_password_request.py +89 -0
  40. crypticorn/auth/client/models/verify200_response.py +110 -0
  41. crypticorn/auth/client/models/verify_email200_response.py +107 -0
  42. crypticorn/auth/client/models/verify_email200_response_auth.py +101 -0
  43. crypticorn/auth/client/models/verify_email200_response_auth_auth.py +110 -0
  44. crypticorn/auth/client/models/verify_email_request.py +83 -0
  45. crypticorn/auth/client/models/verify_wallet_request.py +91 -0
  46. crypticorn/auth/client/models/wallet_verified200_response.py +83 -0
  47. crypticorn/auth/client/models/whoami200_response.py +104 -0
  48. crypticorn/auth/client/rest.py +195 -0
  49. crypticorn/auth/main.py +45 -0
  50. crypticorn/client.py +46 -8
  51. crypticorn/common/__init__.py +5 -0
  52. crypticorn/common/auth.py +43 -0
  53. crypticorn/common/auth_client.py +163 -0
  54. crypticorn/common/errors.py +432 -0
  55. crypticorn/common/scopes.py +29 -0
  56. crypticorn/common/urls.py +25 -0
  57. crypticorn/hive/__init__.py +2 -1
  58. crypticorn/hive/client/__init__.py +57 -0
  59. crypticorn/hive/client/api/__init__.py +6 -0
  60. crypticorn/hive/client/api/data_api.py +594 -0
  61. crypticorn/hive/client/api/models_api.py +1680 -0
  62. crypticorn/hive/client/api/status_api.py +263 -0
  63. crypticorn/hive/client/api_client.py +758 -0
  64. crypticorn/hive/client/api_response.py +20 -0
  65. crypticorn/hive/client/configuration.py +612 -0
  66. crypticorn/hive/client/exceptions.py +220 -0
  67. crypticorn/hive/client/models/__init__.py +38 -0
  68. crypticorn/hive/client/models/coins.py +44 -0
  69. crypticorn/hive/client/models/data_download_response.py +113 -0
  70. crypticorn/hive/client/models/data_info.py +115 -0
  71. crypticorn/hive/client/models/data_value_value_value_inner.py +154 -0
  72. crypticorn/hive/client/models/data_version.py +35 -0
  73. crypticorn/hive/client/models/download_links.py +91 -0
  74. crypticorn/hive/client/models/evaluation.py +86 -0
  75. crypticorn/hive/client/models/evaluation_response.py +85 -0
  76. crypticorn/hive/client/models/feature_size.py +36 -0
  77. crypticorn/hive/client/models/http_validation_error.py +99 -0
  78. crypticorn/hive/client/models/model.py +133 -0
  79. crypticorn/hive/client/models/model_create.py +93 -0
  80. crypticorn/hive/client/models/model_status.py +35 -0
  81. crypticorn/hive/client/models/model_update.py +83 -0
  82. crypticorn/hive/client/models/target.py +36 -0
  83. crypticorn/hive/client/models/target_type.py +35 -0
  84. crypticorn/hive/client/models/validation_error.py +105 -0
  85. crypticorn/hive/client/models/validation_error_loc_inner.py +159 -0
  86. crypticorn/hive/client/py.typed +0 -0
  87. crypticorn/hive/client/rest.py +195 -0
  88. crypticorn/hive/main.py +27 -100
  89. crypticorn/klines/client/__init__.py +21 -7
  90. crypticorn/klines/client/api/__init__.py +0 -1
  91. crypticorn/klines/client/api/funding_rates_api.py +90 -79
  92. crypticorn/klines/client/api/health_check_api.py +29 -45
  93. crypticorn/klines/client/api/ohlcv_data_api.py +104 -87
  94. crypticorn/klines/client/api/symbols_api.py +36 -54
  95. crypticorn/klines/client/api/udf_api.py +228 -352
  96. crypticorn/klines/client/api_client.py +106 -148
  97. crypticorn/klines/client/api_response.py +2 -3
  98. crypticorn/klines/client/configuration.py +64 -50
  99. crypticorn/klines/client/exceptions.py +20 -16
  100. crypticorn/klines/client/models/__init__.py +21 -7
  101. crypticorn/klines/client/models/base_response_health_check_response.py +21 -15
  102. crypticorn/klines/client/models/base_response_list_funding_rate_response.py +21 -15
  103. crypticorn/klines/client/models/base_response_list_str.py +16 -14
  104. crypticorn/klines/client/models/base_response_ohlcv_response.py +21 -15
  105. crypticorn/klines/client/models/error_response.py +23 -15
  106. crypticorn/klines/client/models/exchange.py +11 -11
  107. crypticorn/klines/client/models/funding_rate_response.py +11 -11
  108. crypticorn/klines/client/models/health_check_response.py +14 -12
  109. crypticorn/klines/client/models/history_error_response.py +11 -11
  110. crypticorn/klines/client/models/history_no_data_response.py +16 -16
  111. crypticorn/klines/client/models/history_success_response.py +16 -16
  112. crypticorn/klines/client/models/http_validation_error.py +14 -10
  113. crypticorn/klines/client/models/market.py +2 -4
  114. crypticorn/klines/client/models/ohlcv_response.py +22 -15
  115. crypticorn/klines/client/models/resolution.py +5 -7
  116. crypticorn/klines/client/models/response_get_history_udf_history_get.py +71 -22
  117. crypticorn/klines/client/models/search_symbol_response.py +22 -15
  118. crypticorn/klines/client/models/sort_direction.py +2 -4
  119. crypticorn/klines/client/models/symbol_group_response.py +5 -9
  120. crypticorn/klines/client/models/symbol_info_response.py +40 -24
  121. crypticorn/klines/client/models/symbol_type.py +5 -10
  122. crypticorn/klines/client/models/timeframe.py +5 -7
  123. crypticorn/klines/client/models/udf_config_response.py +60 -21
  124. crypticorn/klines/client/models/validation_error.py +19 -13
  125. crypticorn/klines/client/models/validation_error_loc_inner.py +32 -11
  126. crypticorn/klines/client/rest.py +30 -41
  127. crypticorn/klines/main.py +52 -15
  128. crypticorn/pay/__init__.py +2 -0
  129. crypticorn/pay/client/__init__.py +52 -0
  130. crypticorn/pay/client/api/__init__.py +7 -0
  131. crypticorn/pay/client/api/now_payments_api.py +813 -0
  132. crypticorn/pay/client/api/payments_api.py +799 -0
  133. crypticorn/pay/client/api/products_api.py +891 -0
  134. crypticorn/pay/client/api/status_api.py +260 -0
  135. crypticorn/pay/client/api_client.py +758 -0
  136. crypticorn/pay/client/api_response.py +20 -0
  137. crypticorn/pay/client/configuration.py +612 -0
  138. crypticorn/pay/client/exceptions.py +220 -0
  139. crypticorn/pay/client/models/__init__.py +32 -0
  140. crypticorn/pay/client/models/api_status_res.py +83 -0
  141. crypticorn/pay/client/models/combined_payment_history.py +101 -0
  142. crypticorn/pay/client/models/create_invoice_req.py +188 -0
  143. crypticorn/pay/client/models/create_invoice_res.py +188 -0
  144. crypticorn/pay/client/models/currency.py +165 -0
  145. crypticorn/pay/client/models/estimate_price_req.py +91 -0
  146. crypticorn/pay/client/models/estimate_price_res.py +102 -0
  147. crypticorn/pay/client/models/get_currencies_res.py +99 -0
  148. crypticorn/pay/client/models/get_payment_status_res.py +222 -0
  149. crypticorn/pay/client/models/get_payments_list_res.py +109 -0
  150. crypticorn/pay/client/models/http_validation_error.py +99 -0
  151. crypticorn/pay/client/models/min_amount_req.py +124 -0
  152. crypticorn/pay/client/models/min_amount_res.py +105 -0
  153. crypticorn/pay/client/models/now_api_status_res.py +83 -0
  154. crypticorn/pay/client/models/now_create_invoice_req.py +188 -0
  155. crypticorn/pay/client/models/now_create_invoice_res.py +188 -0
  156. crypticorn/pay/client/models/now_fee_structure.py +104 -0
  157. crypticorn/pay/client/models/now_payment_model.py +124 -0
  158. crypticorn/pay/client/models/now_payment_status.py +42 -0
  159. crypticorn/pay/client/models/now_webhook_payload.py +181 -0
  160. crypticorn/pay/client/models/payment.py +231 -0
  161. crypticorn/pay/client/models/payment_status.py +40 -0
  162. crypticorn/pay/client/models/product.py +87 -0
  163. crypticorn/pay/client/models/product_model.py +119 -0
  164. crypticorn/pay/client/models/product_subs_model.py +108 -0
  165. crypticorn/pay/client/models/services.py +34 -0
  166. crypticorn/pay/client/models/unified_payment_model.py +112 -0
  167. crypticorn/pay/client/models/validation_error.py +105 -0
  168. crypticorn/pay/client/models/validation_error_loc_inner.py +159 -0
  169. crypticorn/pay/client/py.typed +0 -0
  170. crypticorn/pay/client/rest.py +195 -0
  171. crypticorn/pay/main.py +35 -0
  172. crypticorn/trade/client/__init__.py +9 -4
  173. crypticorn/trade/client/api/__init__.py +0 -1
  174. crypticorn/trade/client/api/api_keys_api.py +203 -304
  175. crypticorn/trade/client/api/bots_api.py +177 -250
  176. crypticorn/trade/client/api/exchanges_api.py +38 -57
  177. crypticorn/trade/client/api/futures_trading_panel_api.py +223 -321
  178. crypticorn/trade/client/api/notifications_api.py +247 -364
  179. crypticorn/trade/client/api/orders_api.py +44 -63
  180. crypticorn/trade/client/api/status_api.py +35 -53
  181. crypticorn/trade/client/api/strategies_api.py +852 -64
  182. crypticorn/trade/client/api/trading_actions_api.py +126 -203
  183. crypticorn/trade/client/api_client.py +115 -154
  184. crypticorn/trade/client/api_response.py +2 -3
  185. crypticorn/trade/client/configuration.py +128 -90
  186. crypticorn/trade/client/exceptions.py +21 -17
  187. crypticorn/trade/client/models/__init__.py +9 -4
  188. crypticorn/trade/client/models/action_model.py +114 -50
  189. crypticorn/trade/client/models/api_error_identifier.py +60 -51
  190. crypticorn/trade/client/models/api_error_level.py +37 -0
  191. crypticorn/trade/client/models/api_error_type.py +37 -0
  192. crypticorn/trade/client/models/api_key_model.py +49 -28
  193. crypticorn/trade/client/models/bot_model.py +76 -31
  194. crypticorn/trade/client/models/bot_status.py +37 -0
  195. crypticorn/trade/client/models/exchange.py +3 -5
  196. crypticorn/trade/client/models/execution_ids.py +14 -14
  197. crypticorn/trade/client/models/futures_balance.py +39 -23
  198. crypticorn/trade/client/models/futures_trading_action.py +98 -46
  199. crypticorn/trade/client/models/http_validation_error.py +15 -11
  200. crypticorn/trade/client/models/margin_mode.py +3 -5
  201. crypticorn/trade/client/models/market_type.py +3 -5
  202. crypticorn/trade/client/models/notification_model.py +60 -27
  203. crypticorn/trade/client/models/notification_type.py +4 -6
  204. crypticorn/trade/client/models/order_model.py +125 -65
  205. crypticorn/trade/client/models/order_status.py +6 -8
  206. crypticorn/trade/client/models/post_futures_action.py +16 -12
  207. crypticorn/trade/client/models/strategy_exchange_info.py +11 -12
  208. crypticorn/trade/client/models/strategy_model.py +66 -27
  209. crypticorn/trade/client/models/strategy_model_input.py +160 -0
  210. crypticorn/trade/client/models/strategy_model_output.py +160 -0
  211. crypticorn/trade/client/models/tpsl.py +35 -21
  212. crypticorn/trade/client/models/trading_action_type.py +5 -7
  213. crypticorn/trade/client/models/update_notification.py +17 -13
  214. crypticorn/trade/client/models/validation_error.py +20 -14
  215. crypticorn/trade/client/models/validation_error_loc_inner.py +33 -12
  216. crypticorn/trade/client/rest.py +108 -170
  217. crypticorn/trade/main.py +26 -19
  218. crypticorn-2.0.0.dist-info/METADATA +74 -0
  219. crypticorn-2.0.0.dist-info/RECORD +226 -0
  220. {crypticorn-1.0.2rc3.dist-info → crypticorn-2.0.0.dist-info}/WHEEL +1 -1
  221. crypticorn/hive/requirements.txt +0 -4
  222. crypticorn/hive/utils.py +0 -109
  223. crypticorn/klines/requirements.txt +0 -4
  224. crypticorn/klines/test/test_base_response_health_check_response.py +0 -56
  225. crypticorn/klines/test/test_base_response_list_funding_rate_response.py +0 -59
  226. crypticorn/klines/test/test_base_response_list_str.py +0 -56
  227. crypticorn/klines/test/test_base_response_ohlcv_response.py +0 -72
  228. crypticorn/klines/test/test_error_response.py +0 -57
  229. crypticorn/klines/test/test_exchange.py +0 -56
  230. crypticorn/klines/test/test_funding_rate_response.py +0 -56
  231. crypticorn/klines/test/test_funding_rates_api.py +0 -38
  232. crypticorn/klines/test/test_health_check_api.py +0 -38
  233. crypticorn/klines/test/test_health_check_response.py +0 -52
  234. crypticorn/klines/test/test_history_error_response.py +0 -53
  235. crypticorn/klines/test/test_history_no_data_response.py +0 -69
  236. crypticorn/klines/test/test_history_success_response.py +0 -87
  237. crypticorn/klines/test/test_http_validation_error.py +0 -58
  238. crypticorn/klines/test/test_market.py +0 -33
  239. crypticorn/klines/test/test_ohlcv_data_api.py +0 -38
  240. crypticorn/klines/test/test_ohlcv_response.py +0 -86
  241. crypticorn/klines/test/test_resolution.py +0 -33
  242. crypticorn/klines/test/test_response_get_history_udf_history_get.py +0 -89
  243. crypticorn/klines/test/test_search_symbol_response.py +0 -62
  244. crypticorn/klines/test/test_sort_direction.py +0 -33
  245. crypticorn/klines/test/test_symbol_group_response.py +0 -53
  246. crypticorn/klines/test/test_symbol_info_response.py +0 -84
  247. crypticorn/klines/test/test_symbol_type.py +0 -54
  248. crypticorn/klines/test/test_symbols_api.py +0 -38
  249. crypticorn/klines/test/test_timeframe.py +0 -33
  250. crypticorn/klines/test/test_udf_api.py +0 -80
  251. crypticorn/klines/test/test_udf_config_response.py +0 -95
  252. crypticorn/klines/test/test_validation_error.py +0 -60
  253. crypticorn/klines/test/test_validation_error_loc_inner.py +0 -50
  254. crypticorn/trade/requirements.txt +0 -4
  255. crypticorn-1.0.2rc3.dist-info/LICENSE.md +0 -19
  256. crypticorn-1.0.2rc3.dist-info/METADATA +0 -47
  257. crypticorn-1.0.2rc3.dist-info/RECORD +0 -128
  258. /crypticorn/{klines/test/__init__.py → auth/client/py.typed} +0 -0
  259. {crypticorn-1.0.2rc3.dist-info → crypticorn-2.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,45 @@
1
+ from crypticorn.auth import (
2
+ ApiClient,
3
+ Configuration,
4
+ AdminApi,
5
+ ServiceApi,
6
+ UserApi,
7
+ WalletApi,
8
+ AuthApi,
9
+ )
10
+ from crypticorn.common import BaseURL, ApiVersion, Service, apikey_header as aph
11
+
12
+
13
+ class AuthClient:
14
+ """
15
+ A client for interacting with the Crypticorn Auth API.
16
+ """
17
+
18
+ def __init__(
19
+ self,
20
+ base_url: BaseURL,
21
+ api_version: ApiVersion,
22
+ api_key: str = None,
23
+ jwt: str = None,
24
+ ):
25
+ self.host = f"{base_url.value}/{api_version.value}/{Service.AUTH.value}"
26
+ self.config = Configuration(
27
+ host=self.host,
28
+ access_token=jwt,
29
+ api_key={aph.scheme_name: api_key} if api_key else None,
30
+ api_key_prefix=({aph.scheme_name: aph.model.name} if api_key else None),
31
+ debug=True,
32
+ )
33
+ self.base_client = ApiClient(configuration=self.config)
34
+ # Instantiate all the endpoint clients
35
+ self.admin = AdminApi(self.base_client)
36
+ self.service = ServiceApi(self.base_client)
37
+ self.user = UserApi(self.base_client)
38
+ self.wallet = WalletApi(self.base_client)
39
+ self.login = AuthApi(self.base_client)
40
+
41
+ async def __aenter__(self):
42
+ return self
43
+
44
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
45
+ await self.base_client.close()
crypticorn/client.py CHANGED
@@ -1,16 +1,54 @@
1
1
  from crypticorn.hive import HiveClient
2
- from crypticorn.trade import TradeClient
3
2
  from crypticorn.klines import KlinesClient
3
+ from crypticorn.pay import PayClient
4
+ from crypticorn.trade import TradeClient
5
+ from crypticorn.common import BaseURL, ApiVersion
6
+
7
+
8
+ class ApiClient:
9
+ """
10
+ The official client for interacting with the Crypticorn API.
11
+
12
+ It is consisting of multiple microservices covering the whole stack of the Crypticorn project.
13
+ """
4
14
 
5
- class CrypticornClient:
6
15
  def __init__(
7
- self, base_url: str = "https://api.crypticorn.com", api_key: str = None, jwt: str = None
16
+ self,
17
+ base_url: BaseURL = BaseURL.PROD,
18
+ api_key: str = None,
19
+ jwt: str = None,
20
+ hive_version: ApiVersion = ApiVersion.V1,
21
+ klines_version: ApiVersion = ApiVersion.V1,
22
+ pay_version: ApiVersion = ApiVersion.V1,
23
+ trade_version: ApiVersion = ApiVersion.V1,
24
+ auth_version: ApiVersion = ApiVersion.V1,
8
25
  ):
9
26
  self.base_url = base_url
10
27
  self.api_key = api_key
11
28
  self.jwt = jwt
12
-
13
- # Initialize service clients
14
- self.hive = HiveClient(base_url, api_key, jwt)
15
- self.trade = TradeClient(base_url, api_key, jwt)
16
- self.klines = KlinesClient(base_url, api_key, jwt)
29
+ self.hive = HiveClient(base_url, hive_version, api_key, jwt)
30
+ self.trade = TradeClient(base_url, trade_version, api_key, jwt)
31
+ self.klines = KlinesClient(base_url, klines_version, api_key, jwt)
32
+ self.pay = PayClient(base_url, pay_version, api_key, jwt)
33
+ # currently not working due to circular import since the AUTH_Handler
34
+ # is also using the ApiClient
35
+ # self.auth = AuthClient(base_url, auth_version, api_key, jwt)
36
+
37
+ async def __aenter__(self):
38
+ return self
39
+
40
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
41
+ await self.close()
42
+
43
+ async def close(self):
44
+ """Close all client sessions."""
45
+ clients = [
46
+ self.hive.base_client,
47
+ self.trade.base_client,
48
+ self.klines.base_client,
49
+ self.pay.base_client,
50
+ ]
51
+
52
+ for client in clients:
53
+ if hasattr(client, "close"):
54
+ await client.close()
@@ -0,0 +1,5 @@
1
+ from crypticorn.common.errors import *
2
+ from crypticorn.common.scopes import *
3
+ from crypticorn.common.urls import *
4
+ from crypticorn.common.auth import *
5
+ from crypticorn.common.auth_client import *
@@ -0,0 +1,43 @@
1
+ from typing import Optional, Callable, Any, Annotated
2
+ from typing_extensions import Doc
3
+ from enum import Enum
4
+
5
+ from fastapi import params
6
+ from fastapi.security import APIKeyHeader, HTTPBearer
7
+
8
+ from crypticorn.common import ApiScope
9
+
10
+
11
+ http_bearer = HTTPBearer(
12
+ bearerFormat="JWT",
13
+ auto_error=False,
14
+ description="The JWT to use for authentication.",
15
+ )
16
+
17
+ apikey_header = APIKeyHeader(
18
+ name="X-API-Key",
19
+ auto_error=False,
20
+ description="The API key to use for authentication.",
21
+ )
22
+
23
+
24
+ def Security( # noqa: N802
25
+ dependency: Annotated[
26
+ Optional[Callable[..., Any]], Doc("A dependable callable (like a function).")
27
+ ] = None,
28
+ scopes: Annotated[
29
+ Optional[list[ApiScope]], # Optional[Sequence[Union[str, APIScope]]],
30
+ Doc("OAuth2 scopes required for the *path operation*."),
31
+ ] = None,
32
+ use_cache: Annotated[
33
+ bool, Doc("Whether to cache the dependency during a single request.")
34
+ ] = True,
35
+ ) -> Any:
36
+ # Convert Enum scopes to string
37
+ scopes_str = (
38
+ [s.value if isinstance(s, Enum) else s for s in scopes] if scopes else None
39
+ )
40
+
41
+ return params.Security(
42
+ dependency=dependency, scopes=scopes_str, use_cache=use_cache
43
+ )
@@ -0,0 +1,163 @@
1
+ from fastapi import Depends, HTTPException, status
2
+ from fastapi.security import HTTPAuthorizationCredentials
3
+ from typing_extensions import Annotated, Doc
4
+
5
+ from crypticorn.auth import AuthClient, Verify200Response
6
+ from crypticorn.auth.client.exceptions import UnauthorizedException
7
+ from crypticorn.common import (
8
+ ApiError,
9
+ ApiScope,
10
+ ApiVersion,
11
+ BaseURL,
12
+ Domain,
13
+ apikey_header,
14
+ http_bearer,
15
+ )
16
+
17
+
18
+ class AuthHandler:
19
+ """
20
+ Middleware for verifying API requests. Verifies the validity of the authentication token, allowed scopes, etc.
21
+ """
22
+
23
+ def __init__(
24
+ self,
25
+ base_url: Annotated[
26
+ BaseURL, Doc("The base URL for the auth service.")
27
+ ] = BaseURL.PROD,
28
+ api_version: Annotated[
29
+ ApiVersion, Doc("The API version of the auth service.")
30
+ ] = ApiVersion.V1,
31
+ whitelist: Annotated[
32
+ list[Domain],
33
+ Doc(
34
+ "The domains of which requests are allowed full access to the service."
35
+ ),
36
+ ] = [
37
+ Domain.PROD,
38
+ Domain.DEV,
39
+ ], # TODO: decide whether this is needed, else omit
40
+ ):
41
+ self.whitelist = whitelist
42
+ self.auth_client = AuthClient(base_url=base_url, api_version=api_version)
43
+
44
+ self.invalid_scopes_exception = HTTPException(
45
+ status_code=status.HTTP_403_FORBIDDEN,
46
+ detail=ApiError.INSUFFICIENT_SCOPES.identifier,
47
+ )
48
+ self.no_credentials_exception = HTTPException(
49
+ status_code=status.HTTP_401_UNAUTHORIZED,
50
+ detail=ApiError.NO_CREDENTIALS.identifier,
51
+ )
52
+ self.invalid_api_key_exception = HTTPException(
53
+ status_code=status.HTTP_401_UNAUTHORIZED,
54
+ detail=ApiError.INVALID_API_KEY.identifier,
55
+ )
56
+ self.invalid_bearer_exception = HTTPException(
57
+ status_code=status.HTTP_401_UNAUTHORIZED,
58
+ detail=ApiError.INVALID_BEARER.identifier,
59
+ )
60
+
61
+ async def _verify_api_key(self, api_key: str) -> None:
62
+ """
63
+ Verifies the API key.
64
+ """
65
+ # TODO: Implement in auth service
66
+ return NotImplementedError()
67
+
68
+ async def _verify_bearer(
69
+ self, bearer: HTTPAuthorizationCredentials
70
+ ) -> Verify200Response:
71
+ """
72
+ Verifies the bearer token.
73
+ """
74
+ self.auth_client.config.access_token = bearer.credentials
75
+ return await self.auth_client.login.verify()
76
+
77
+ async def _check_scopes(
78
+ self, api_scopes: list[ApiScope], user_scopes: list[ApiScope]
79
+ ) -> bool:
80
+ """
81
+ Checks if the user scopes are a subset of the API scopes.
82
+ """
83
+ return set(api_scopes).issubset(user_scopes)
84
+
85
+ async def api_key_auth(
86
+ self,
87
+ api_key: Annotated[str | None, Depends(apikey_header)] = None,
88
+ scopes: list[ApiScope] = [],
89
+ ) -> Verify200Response:
90
+ """
91
+ Verifies the API key and checks if the user scopes are a subset of the API scopes.
92
+ """
93
+ if not api_key:
94
+ raise self.no_credentials_exception
95
+ try:
96
+ res = await self._verify_api_key(api_key)
97
+ except UnauthorizedException as e:
98
+ raise self.invalid_api_key_exception
99
+ valid_scopes = await self._check_scopes(scopes, res.scopes)
100
+ if not valid_scopes:
101
+ raise self.invalid_scopes_exception
102
+ return res
103
+
104
+ async def bearer_auth(
105
+ self,
106
+ bearer: Annotated[
107
+ HTTPAuthorizationCredentials | None,
108
+ Depends(http_bearer),
109
+ ] = None,
110
+ scopes: list[ApiScope] = [],
111
+ ) -> Verify200Response:
112
+ """
113
+ Verifies the bearer token and checks if the user scopes are a subset of the API scopes.
114
+ """
115
+ if not bearer:
116
+ raise self.no_credentials_exception
117
+
118
+ try:
119
+ res = await self._verify_bearer(bearer)
120
+ except UnauthorizedException as e:
121
+ raise self.invalid_bearer_exception
122
+ valid_scopes = await self._check_scopes(scopes, res.scopes)
123
+ if not valid_scopes:
124
+ raise self.invalid_scopes_exception
125
+ return res
126
+
127
+ async def combined_auth(
128
+ self,
129
+ bearer: Annotated[
130
+ HTTPAuthorizationCredentials | None, Depends(http_bearer)
131
+ ] = None,
132
+ api_key: Annotated[str | None, Depends(apikey_header)] = None,
133
+ scopes: list[ApiScope] = [],
134
+ ) -> Verify200Response:
135
+ """
136
+ Verifies the bearer token and API key and checks if the user scopes are a subset of the API scopes.
137
+ Returns early on the first successful verification, otherwise tries all available tokens.
138
+ """
139
+ tokens = [bearer, api_key]
140
+
141
+ last_error = None
142
+ for token in tokens:
143
+ try:
144
+ if token is None:
145
+ continue
146
+ res = None
147
+ if isinstance(token, str):
148
+ res = await self._verify_api_key(token)
149
+ elif isinstance(token, HTTPAuthorizationCredentials):
150
+ res = await self._verify_bearer(token)
151
+ if res is None:
152
+ continue
153
+ if scopes:
154
+ valid_scopes = await self._check_scopes(scopes, res.scopes)
155
+ if not valid_scopes:
156
+ raise self.invalid_scopes_exception
157
+ return res
158
+
159
+ except UnauthorizedException as e:
160
+ last_error = e
161
+ continue
162
+
163
+ raise last_error or self.no_credentials_exception