crypticorn 2.0.1__py3-none-any.whl → 2.1.1__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.
@@ -1,4 +1,4 @@
1
- from fastapi import Depends, HTTPException, status
1
+ from fastapi import Depends, HTTPException, Query, status
2
2
  from fastapi.security import HTTPAuthorizationCredentials
3
3
  from typing_extensions import Annotated, Doc
4
4
  import json
@@ -51,8 +51,7 @@ class AuthHandler:
51
51
  """
52
52
  Verifies the API key.
53
53
  """
54
- # TODO: Implement in auth service
55
- raise NotImplementedError("API key verification not implemented")
54
+ return await self.auth_client.login.verify_api_key(api_key)
56
55
 
57
56
  async def _verify_bearer(
58
57
  self, bearer: HTTPAuthorizationCredentials
@@ -76,9 +75,9 @@ class AuthHandler:
76
75
  )
77
76
 
78
77
  async def _extract_message(self, e: ApiException) -> str:
79
- '''
78
+ """
80
79
  Tries to extract the message from the body of the exception.
81
- '''
80
+ """
82
81
  try:
83
82
  load = json.loads(e.body)
84
83
  except (json.JSONDecodeError, TypeError):
@@ -91,9 +90,9 @@ class AuthHandler:
91
90
  return load
92
91
 
93
92
  async def _handle_exception(self, e: Exception) -> HTTPException:
94
- '''
93
+ """
95
94
  Handles exceptions and returns a HTTPException with the appropriate status code and detail.
96
- '''
95
+ """
97
96
  if isinstance(e, ApiException):
98
97
  return HTTPException(
99
98
  status_code=e.status,
@@ -104,8 +103,9 @@ class AuthHandler:
104
103
  else:
105
104
  return HTTPException(
106
105
  status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
106
+ detail=str(e),
107
107
  )
108
-
108
+
109
109
  async def api_key_auth(
110
110
  self,
111
111
  api_key: Annotated[str | None, Depends(apikey_header)] = None,
@@ -113,16 +113,9 @@ class AuthHandler:
113
113
  ) -> Verify200Response:
114
114
  """
115
115
  Verifies the API key and checks if the user scopes are a subset of the API scopes.
116
+ Use this function if you only want to allow access via the API key.
116
117
  """
117
- try:
118
- if not api_key:
119
- raise self.no_credentials_exception
120
-
121
- res = await self._verify_api_key(api_key)
122
- await self._validate_scopes(scopes, [Scope.from_str(scope) for scope in res.scopes])
123
- return res
124
- except Exception as e:
125
- raise await self._handle_exception(e)
118
+ return await self.combined_auth(bearer=None, api_key=api_key, scopes=scopes)
126
119
 
127
120
  async def bearer_auth(
128
121
  self,
@@ -134,17 +127,9 @@ class AuthHandler:
134
127
  ) -> Verify200Response:
135
128
  """
136
129
  Verifies the bearer token and checks if the user scopes are a subset of the API scopes.
130
+ Use this function if you only want to allow access via the bearer token.
137
131
  """
138
- if not bearer:
139
- raise self.no_credentials_exception
140
-
141
- try:
142
- res = await self._verify_bearer(bearer)
143
- await self._validate_scopes(scopes, [Scope.from_str(scope) for scope in res.scopes])
144
- return res
145
- except Exception as e:
146
- raise await self._handle_exception(e)
147
-
132
+ return await self.combined_auth(bearer=bearer, api_key=None, scopes=scopes)
148
133
 
149
134
  async def combined_auth(
150
135
  self,
@@ -155,8 +140,9 @@ class AuthHandler:
155
140
  scopes: list[Scope] = [],
156
141
  ) -> Verify200Response:
157
142
  """
158
- Verifies the bearer token and API key and checks if the user scopes are a subset of the API scopes.
143
+ Verifies the bearer token and/or API key and checks if the user scopes are a subset of the API scopes.
159
144
  Returns early on the first successful verification, otherwise tries all available tokens.
145
+ Use this function if you want to allow access via either the bearer token or the API key.
160
146
  """
161
147
  tokens = [bearer, api_key]
162
148
 
@@ -173,7 +159,9 @@ class AuthHandler:
173
159
  if res is None:
174
160
  continue
175
161
  if scopes:
176
- await self._validate_scopes(scopes, [Scope.from_str(scope) for scope in res.scopes])
162
+ await self._validate_scopes(
163
+ scopes, [Scope.from_str(scope) for scope in res.scopes]
164
+ )
177
165
  return res
178
166
 
179
167
  except Exception as e:
@@ -181,3 +169,49 @@ class AuthHandler:
181
169
  continue
182
170
 
183
171
  raise last_error or self.no_credentials_exception
172
+
173
+ async def ws_api_key_auth(
174
+ self,
175
+ api_key: Annotated[str | None, Query()] = None,
176
+ scopes: list[Scope] = [],
177
+ ) -> Verify200Response:
178
+ """
179
+ Verifies the API key and checks if the user scopes are a subset of the API scopes.
180
+ Use this function if you only want to allow access via the API key.
181
+ """
182
+ return await self.api_key_auth(api_key=api_key, scopes=scopes)
183
+
184
+ async def ws_bearer_auth(
185
+ self,
186
+ bearer: Annotated[str | None, Query()] = None,
187
+ scopes: list[Scope] = [],
188
+ ) -> Verify200Response:
189
+ """
190
+ Verifies the bearer token and checks if the user scopes are a subset of the API scopes.
191
+ Use this function if you only want to allow access via the bearer token.
192
+ """
193
+ credentials = (
194
+ HTTPAuthorizationCredentials(scheme="Bearer", credentials=bearer)
195
+ if bearer
196
+ else None
197
+ )
198
+ return await self.bearer_auth(bearer=credentials, scopes=scopes)
199
+
200
+ async def ws_combined_auth(
201
+ self,
202
+ bearer: Annotated[str | None, Query()] = None,
203
+ api_key: Annotated[str | None, Query()] = None,
204
+ scopes: list[Scope] = [],
205
+ ) -> Verify200Response:
206
+ """
207
+ Verifies the bearer token and/or API key and checks if the user scopes are a subset of the API scopes.
208
+ Use this function if you want to allow access via either the bearer token or the API key.
209
+ """
210
+ credentials = (
211
+ HTTPAuthorizationCredentials(scheme="Bearer", credentials=bearer)
212
+ if bearer
213
+ else None
214
+ )
215
+ return await self.combined_auth(
216
+ bearer=credentials, api_key=api_key, scopes=scopes
217
+ )
@@ -6,31 +6,39 @@ class Scope(str, Enum):
6
6
  The permission scopes for the API.
7
7
  """
8
8
 
9
+ # If you update anything here, also update the scopes in the auth-service repository
10
+
9
11
  @classmethod
10
12
  def from_str(cls, value: str) -> "Scope":
11
13
  return cls(value)
12
14
 
13
15
  # Hive scopes
14
- HIVE_MODEL_READ = "hive:model:read"
15
- HIVE_DATA_READ = "hive:data:read"
16
- HIVE_MODEL_WRITE = "hive:model:write"
17
- HIVE_DATA_WRITE = "hive:data:write"
16
+ READ_HIVE_MODEL = "read:hive:model"
17
+ READ_HIVE_DATA = "read:hive:data"
18
+ WRITE_HIVE_MODEL = "write:hive:model"
18
19
 
19
20
  # Trade scopes
20
- TRADE_BOTS_READ = "trade:bots:read"
21
- TRADE_BOTS_WRITE = "trade:bots:write"
22
- TRADE_API_KEYS_READ = "trade:api_keys:read"
23
- TRADE_API_KEYS_WRITE = "trade:api_keys:write"
24
- TRADE_ORDERS_READ = "trade:orders:read"
25
- TRADE_ACTIONS_READ = "trade:actions:read"
26
- TRADE_ACTIONS_WRITE = "trade:actions:write"
27
- TRADE_EXCHANGES_READ = "trade:exchanges:read"
28
- TRADE_FUTURES_READ = "trade:futures:read"
29
- TRADE_FUTURES_WRITE = "trade:futures:write"
30
- TRADE_NOTIFICATIONS_READ = "trade:notifications:read"
31
- TRADE_NOTIFICATIONS_WRITE = "trade:notifications:write"
32
- TRADE_STRATEGIES_READ = "trade:strategies:read"
33
- TRADE_STRATEGIES_WRITE = "trade:strategies:write"
21
+ READ_TRADE_BOTS = "read:trade:bots"
22
+ WRITE_TRADE_BOTS = "write:trade:bots"
23
+ READ_TRADE_APIKEYS = "read:trade:api_keys"
24
+ WRITE_TRADE_APIKEYS = "write:trade:api_keys"
25
+ READ_TRADE_ORDERS = "read:trade:orders"
26
+ READ_TRADE_ACTIONS = "read:trade:actions"
27
+ WRITE_TRADE_ACTIONS = "write:trade:actions"
28
+ READ_TRADE_EXCHANGES = "read:trade:exchanges"
29
+ READ_TRADE_FUTURES = "read:trade:futures"
30
+ WRITE_TRADE_FUTURES = "write:trade:futures"
31
+ READ_TRADE_NOTIFICATIONS = "read:trade:notifications"
32
+ WRITE_TRADE_NOTIFICATIONS = "write:trade:notifications"
33
+ READ_TRADE_STRATEGIES = "read:trade:strategies"
34
+ WRITE_TRADE_STRATEGIES = "write:trade:strategies"
35
+
36
+ # Payment scopes
37
+ READ_PAY_PAYMENTS = "read:pay:payments"
38
+ READ_PAY_PRODUCTS = "read:pay:products"
39
+ WRITE_PAY_PRODUCTS = "write:pay:products"
40
+ READ_PAY_NOW = "read:pay:now"
41
+ WRITE_PAY_NOW = "write:pay:now"
34
42
 
35
43
  # Read projections
36
44
  READ_PREDICTIONS = "read:predictions"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crypticorn
3
- Version: 2.0.1
3
+ Version: 2.1.1
4
4
  Summary: Maximise Your Crypto Trading Profits with AI Predictions
5
5
  Author-email: Crypticorn <timon@crypticorn.com>
6
6
  Project-URL: Homepage, https://crypticorn.com
@@ -15,15 +15,14 @@ Requires-Python: >=3.10
15
15
  Description-Content-Type: text/markdown
16
16
  Requires-Dist: fastapi
17
17
  Requires-Dist: urllib3<3.0.0,>=1.25.3
18
- Requires-Dist: python_dateutil>=2.8.2
19
- Requires-Dist: aiohttp>=3.8.4
20
- Requires-Dist: aiohttp-retry>=2.8.3
21
- Requires-Dist: pydantic>=2
22
- Requires-Dist: typing-extensions>=4.7.1
18
+ Requires-Dist: python_dateutil<3.0.0,>=2.8.2
19
+ Requires-Dist: aiohttp<4.0.0,>=3.8.4
20
+ Requires-Dist: aiohttp-retry<3.0.0,>=2.8.3
21
+ Requires-Dist: pydantic<3.0.0,>=2.0.0
22
+ Requires-Dist: typing-extensions<5.0.0,>=4.7.1
23
23
  Requires-Dist: pandas<3.0.0,>=2.2.0
24
24
  Requires-Dist: requests<3.0.0,>=2.32.0
25
25
  Requires-Dist: tqdm<5.0.0,>=4.67.0
26
- Requires-Dist: pydantic<3.0.0,>=2.0.0
27
26
  Provides-Extra: dev
28
27
  Requires-Dist: streamlit; extra == "dev"
29
28
  Requires-Dist: httpx; extra == "dev"
@@ -32,9 +31,10 @@ Requires-Dist: black; extra == "dev"
32
31
  Requires-Dist: twine; extra == "dev"
33
32
  Requires-Dist: pyflakes; extra == "dev"
34
33
  Provides-Extra: test
35
- Requires-Dist: pytest>=7.2.1; extra == "test"
36
- Requires-Dist: pytest-cov>=2.8.1; extra == "test"
37
- Requires-Dist: pytest-asyncio>=0.21.0; extra == "test"
34
+ Requires-Dist: pytest==8.3.5; extra == "test"
35
+ Requires-Dist: pytest-asyncio==0.26.0; extra == "test"
36
+ Requires-Dist: pytest-cov==6.1.1; extra == "test"
37
+ Requires-Dist: python-dotenv==1.0.1; extra == "test"
38
38
 
39
39
  # What is Crypticorn?
40
40
 
@@ -2,7 +2,7 @@ crypticorn/__init__.py,sha256=TL41V09dmtbd2ee07wmOuG9KJJpyvLMPJi5DEd9bDyU,129
2
2
  crypticorn/client.py,sha256=PHtTaV8hWiR-IXcZhQlwEY38q-PhqQjf-JiqaN2MPWg,1844
3
3
  crypticorn/auth/__init__.py,sha256=JAl1tBLK9pYLr_-YKaj581c-c94PWLoqnatTIVAVvMM,81
4
4
  crypticorn/auth/main.py,sha256=ljPuO27VDiOO-jnNycBn96X8UbVHPgOUglj46ib3OjM,1336
5
- crypticorn/auth/client/__init__.py,sha256=PAAEv-J8dFQ9NfwMonLRNVS64t0gozOq45Z_RoWuP98,4694
5
+ crypticorn/auth/client/__init__.py,sha256=ziGgrJ7judjAwOaSKF-7lgs1zhE-XEZkMYKD0WUkHfs,4990
6
6
  crypticorn/auth/client/api_client.py,sha256=H2jviD8CKiFnK1ZZXWKbSdBszBYcCQNcga9pwgM03D4,26908
7
7
  crypticorn/auth/client/api_response.py,sha256=WhxwYDSMm6wPixp9CegO8dJzjFxDz3JF1yCq9s0ZqKE,639
8
8
  crypticorn/auth/client/configuration.py,sha256=GNyY6hld-m9Dr7YPngRP5RQGg7o90SsSsSsq7VhyGVs,17715
@@ -11,17 +11,20 @@ crypticorn/auth/client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
11
11
  crypticorn/auth/client/rest.py,sha256=RPbNRRbq7GtH9EBsQxaKT6NtSN8pIp1NmO1SKYZ9vVo,6944
12
12
  crypticorn/auth/client/api/__init__.py,sha256=c-MlPvmOXSsk_ragrcUe8WkOuCPTTpJ8maSp2sER0EU,339
13
13
  crypticorn/auth/client/api/admin_api.py,sha256=Bp52mo4XPDSzVsHolqAykAFpsaioNASY8qDfAUInxgU,21223
14
- crypticorn/auth/client/api/auth_api.py,sha256=72xZo02SGYbxNjOOdfrb4Dqiiqksiqkv1X1B10P41_s,85123
14
+ crypticorn/auth/client/api/auth_api.py,sha256=3MS0t5n5_dh_OsnxlxAGocxsA_OxOixoy6CeBwyWWEE,124189
15
15
  crypticorn/auth/client/api/service_api.py,sha256=xMQKbP9p6fNdJmFa3qMq2RXvE_Brt9HmtprBxEzb0Nk,12423
16
16
  crypticorn/auth/client/api/user_api.py,sha256=nJJ_qD2RYDd6yQ9twHjtwhMJ9lEMSSE91IKltpFC3h0,103457
17
17
  crypticorn/auth/client/api/wallet_api.py,sha256=AF6PSfv5bAnHpThe0Mtz1LD9qhWcd99lMOo7TJDGIt0,68927
18
- crypticorn/auth/client/models/__init__.py,sha256=yrOSQbOq_ZQLDgkjJmmuPxn8vz-AeR2NOEYavs9z1e0,3786
18
+ crypticorn/auth/client/models/__init__.py,sha256=T0I3N-rI2A-VQ2BN9o8Nejt2Z5frT4V7fBmbBMceYHM,4082
19
19
  crypticorn/auth/client/models/add_wallet200_response.py,sha256=ibNUhAku-i2Bglo6ibyQ3V-JlFFauvswYy4doZUNiN4,2559
20
20
  crypticorn/auth/client/models/add_wallet_request.py,sha256=w86tiy1vtlymB7ET0opmENQiEKAGAEjXTC-w4T3LHT4,3204
21
21
  crypticorn/auth/client/models/authorize_user200_response.py,sha256=tkhq7TaQK4li03HKXzrH0GzXa-GIkDVJTUKZ-Qp0USE,3381
22
22
  crypticorn/auth/client/models/authorize_user200_response_auth.py,sha256=h1PFbqzF96ssj7t7dMMKiNtRjRu44kPNUydevJm8vKo,3228
23
23
  crypticorn/auth/client/models/authorize_user_request.py,sha256=VZKmGc91nIhWwoKNTbYZ4oFPYuFNcLxEBvWl-HcM0r8,2981
24
+ crypticorn/auth/client/models/create_api_key200_response.py,sha256=dj6G4vqSTDoUY3jT3_RbUxsLdg_2W-BvfYfwP9O9RJE,2479
25
+ crypticorn/auth/client/models/create_api_key_request.py,sha256=UHRtVGudMK0h-ePJ_whlwqO0W6UVTQqNSeCBjo1A6wQ,4707
24
26
  crypticorn/auth/client/models/create_user_request.py,sha256=kqVBJatlPtoYC1-nZnP2Mx2qZP2ATXffod7hTdWtoCY,3501
27
+ crypticorn/auth/client/models/get_api_keys200_response_inner.py,sha256=YH7vEVQBgnrMV4EgD6WdkTJhC3h3l-Z-dNN2DJOSP5c,4845
25
28
  crypticorn/auth/client/models/list_wallets200_response.py,sha256=OB8nKlBflpj8dXhjTeTewxjSRok3LnllZZ5ZKISDRvE,4752
26
29
  crypticorn/auth/client/models/list_wallets200_response_balances_inner.py,sha256=sLebeWVenEeiHpiCgxQR8iCMlAgjtC6K8CKq6v9g-Ok,4043
27
30
  crypticorn/auth/client/models/list_wallets200_response_balances_inner_sale_round.py,sha256=rqXN7WVEhmeJxDKCSK5sxwWXvmn-Gnf810MGWwIUXII,3623
@@ -31,6 +34,8 @@ crypticorn/auth/client/models/list_wallets200_response_data_inner.py,sha256=IxZr
31
34
  crypticorn/auth/client/models/list_wallets200_response_user_value.py,sha256=e0LbNMdI77IVRt9glwquB6C790o6OWfvitlfyuIoupQ,3841
32
35
  crypticorn/auth/client/models/logout_default_response.py,sha256=ioZ8FrV2DDUdCX2XIkS0X1Y2H0ZjLBCl4_5t_OzrnXE,3395
33
36
  crypticorn/auth/client/models/logout_default_response_issues_inner.py,sha256=_aOp_ngnk19-YLM07nSiqClp6-l6Qa5g6u6Habd1BEc,2487
37
+ crypticorn/auth/client/models/oauth_callback200_response.py,sha256=E7NaQVrrlPtkkaFHG-QNJCQWQzFGXfpjotd2sVZ-gE4,3436
38
+ crypticorn/auth/client/models/oauth_callback200_response_user.py,sha256=ABTvVFLHPR8jMOOHJDvy4_fIVLKuL753ImHLifNQzVg,3018
34
39
  crypticorn/auth/client/models/refresh_token_info200_response.py,sha256=YZITc7MeCrKIVewQxtwOv9xcr26fFhwhnl6o59Ry024,3082
35
40
  crypticorn/auth/client/models/refresh_token_info200_response_user_session.py,sha256=QUz7M-lgSDdvLg2RqFYJkTEJuZtvKlD1CB5BfXo4kFo,3101
36
41
  crypticorn/auth/client/models/resend_verification_email_request.py,sha256=Gx5OONaDxeXaXU7Q30NLMIAvX1XkzmdqbuC6EPlxumk,2546
@@ -50,10 +55,10 @@ crypticorn/auth/client/models/verify_wallet_request.py,sha256=b0DAocvhKzPXPjM62D
50
55
  crypticorn/auth/client/models/wallet_verified200_response.py,sha256=QILnTLsCKdI-WdV_fsLBy1UH4ZZU-U-wWJ9ot8v08tI,2465
51
56
  crypticorn/auth/client/models/whoami200_response.py,sha256=uehdq5epgeOphhrIR3tbrseflxcLAzGyKF-VW-o5cY8,2974
52
57
  crypticorn/common/__init__.py,sha256=lY87VMTkIEqto6kcEjC1YWsUvT03QuPmXwxCaeWE854,196
53
- crypticorn/common/auth.py,sha256=SncaDgteLLyypwiRGd1fEF_yvy2mxPy0YNkLgv30ARQ,1204
54
- crypticorn/common/auth_client.py,sha256=uqf4IL0UfnxO8TxZHEhaZb9CzI4vUOKpU2wZXBud-30,6122
58
+ crypticorn/common/auth.py,sha256=kO03VKuFRIoWXMDLPmgnVZ49EeTqOMzgJ2yEXt_SmQM,1223
59
+ crypticorn/common/auth_client.py,sha256=7ptCkWZwuMSIFTx3gajOH4CScPFG-Pn5gLe1m_NY5AQ,7622
55
60
  crypticorn/common/errors.py,sha256=jGAS7TONKhdaRfft7w-0wrJ3BBTzqS6u03susiqAF0w,12723
56
- crypticorn/common/scopes.py,sha256=PkInTfPGelXW3jXJlMiOOZsO5uPW-FDjKDediZh9xqQ,1144
61
+ crypticorn/common/scopes.py,sha256=pRD9RauSRSxVGo8k5b2x66h3Wy2aaVEcluZvu6aMTE0,1418
57
62
  crypticorn/common/urls.py,sha256=_NMhvhZXOsZpDBbgucqu0yboRFox6JVMlOoQq_Y5SGA,432
58
63
  crypticorn/hive/__init__.py,sha256=hRfTlEzEql4msytdUC_04vfaHzVKG5CGZle1M-9QFgY,81
59
64
  crypticorn/hive/main.py,sha256=RmCYSR0jwmfYWTK89dt79DuGPjEaip9XQs_LWNWr_tc,1036
@@ -220,7 +225,7 @@ crypticorn/trade/client/models/trading_action_type.py,sha256=jW0OsNz_ZNXlITxAfh9
220
225
  crypticorn/trade/client/models/update_notification.py,sha256=B9QUuVRNpk1e5G8o0WFgIg3inm-OX7KJAJcjVnRzYx8,3046
221
226
  crypticorn/trade/client/models/validation_error.py,sha256=uTkvsKrOAt-21UC0YPqCdRl_OMsuu7uhPtWuwRSYvv0,3228
222
227
  crypticorn/trade/client/models/validation_error_loc_inner.py,sha256=22ql-H829xTBgfxNQZsqd8fS3zQt9tLW1pj0iobo0jY,5131
223
- crypticorn-2.0.1.dist-info/METADATA,sha256=CCwYU2zydLX-poZzGLMXdQ1QIy6s2Oom8n2GP-O1i4k,3066
224
- crypticorn-2.0.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
225
- crypticorn-2.0.1.dist-info/top_level.txt,sha256=EP3NY216qIBYfmvGl0L2Zc9ItP0DjGSkiYqd9xJwGcM,11
226
- crypticorn-2.0.1.dist-info/RECORD,,
228
+ crypticorn-2.1.1.dist-info/METADATA,sha256=yQ-VVRigGhwWn9thJoC-qZtQMHth2JOF448X_kTAt2o,3120
229
+ crypticorn-2.1.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
230
+ crypticorn-2.1.1.dist-info/top_level.txt,sha256=EP3NY216qIBYfmvGl0L2Zc9ItP0DjGSkiYqd9xJwGcM,11
231
+ crypticorn-2.1.1.dist-info/RECORD,,