qmenta-core 5.2.0.dev728__tar.gz → 6.0.0.dev730__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.
- {qmenta_core-5.2.0.dev728 → qmenta_core-6.0.0.dev730}/PKG-INFO +1 -1
- {qmenta_core-5.2.0.dev728 → qmenta_core-6.0.0.dev730}/pyproject.toml +1 -1
- {qmenta_core-5.2.0.dev728 → qmenta_core-6.0.0.dev730}/src/qmenta/core/auth.py +77 -51
- {qmenta_core-5.2.0.dev728 → qmenta_core-6.0.0.dev730}/README.md +0 -0
- {qmenta_core-5.2.0.dev728 → qmenta_core-6.0.0.dev730}/src/qmenta/__init__.py +0 -0
- {qmenta_core-5.2.0.dev728 → qmenta_core-6.0.0.dev730}/src/qmenta/core/.gitignore +0 -0
- {qmenta_core-5.2.0.dev728 → qmenta_core-6.0.0.dev730}/src/qmenta/core/__init__.py +0 -0
- {qmenta_core-5.2.0.dev728 → qmenta_core-6.0.0.dev730}/src/qmenta/core/errors.py +0 -0
- {qmenta_core-5.2.0.dev728 → qmenta_core-6.0.0.dev730}/src/qmenta/core/platform.py +0 -0
- {qmenta_core-5.2.0.dev728 → qmenta_core-6.0.0.dev730}/src/qmenta/core/upload/__init__.py +0 -0
- {qmenta_core-5.2.0.dev728 → qmenta_core-6.0.0.dev730}/src/qmenta/core/upload/multi.py +0 -0
- {qmenta_core-5.2.0.dev728 → qmenta_core-6.0.0.dev730}/src/qmenta/core/upload/prepare.py +0 -0
- {qmenta_core-5.2.0.dev728 → qmenta_core-6.0.0.dev730}/src/qmenta/core/upload/single.py +0 -0
- {qmenta_core-5.2.0.dev728 → qmenta_core-6.0.0.dev730}/src/qmenta/py.typed +0 -0
|
@@ -2,6 +2,7 @@ import argparse
|
|
|
2
2
|
from enum import Enum, unique
|
|
3
3
|
import os
|
|
4
4
|
import requests
|
|
5
|
+
import sys
|
|
5
6
|
from getpass import getpass
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
from typing import Optional, Dict
|
|
@@ -28,7 +29,9 @@ class InvalidLoginError(ActionFailedError):
|
|
|
28
29
|
|
|
29
30
|
class Needs2FAError(ActionFailedError):
|
|
30
31
|
"""
|
|
31
|
-
When
|
|
32
|
+
When the account has two-factor authentication enabled.
|
|
33
|
+
Username/password login with a 2FA code is no longer supported;
|
|
34
|
+
generate an API key instead and log in with it.
|
|
32
35
|
"""
|
|
33
36
|
pass
|
|
34
37
|
|
|
@@ -114,25 +117,18 @@ class Auth:
|
|
|
114
117
|
def login(cls,
|
|
115
118
|
username: Optional[str] = None,
|
|
116
119
|
password: Optional[str] = None,
|
|
117
|
-
code_2fa: Optional[str] = None,
|
|
118
120
|
*,
|
|
119
121
|
api_key: Optional[str] = None,
|
|
120
|
-
ask_for_2fa_input: bool = False,
|
|
121
122
|
base_url: str = PlatformURL.platform.value) -> 'Auth':
|
|
122
123
|
"""
|
|
123
124
|
Authenticate to the platform using username and password,
|
|
124
125
|
or an API key.
|
|
125
126
|
|
|
126
|
-
``api_key
|
|
127
|
-
as keyword arguments.
|
|
127
|
+
``api_key`` and ``base_url`` must be passed as keyword arguments.
|
|
128
128
|
|
|
129
129
|
Valid parameter combinations:
|
|
130
130
|
|
|
131
131
|
- ``username``, ``password``: standard login.
|
|
132
|
-
- ``username``, ``password``, ``code_2fa``: login with a
|
|
133
|
-
pre-supplied 2FA code.
|
|
134
|
-
- ``username``, ``password``, ``ask_for_2fa_input=True``: login
|
|
135
|
-
and prompt for the 2FA code interactively when required.
|
|
136
132
|
- ``api_key``: login using an API key.
|
|
137
133
|
|
|
138
134
|
``base_url`` can be combined with any of the above to target a
|
|
@@ -140,6 +136,11 @@ class Auth:
|
|
|
140
136
|
When ``username`` + ``password`` and ``api_key`` are all provided,
|
|
141
137
|
``username`` + ``password`` takes precedence.
|
|
142
138
|
|
|
139
|
+
Accounts with two-factor authentication (2FA) enabled can no
|
|
140
|
+
longer log in with username and password: generate an API key
|
|
141
|
+
in the platform (My profile -> Credentials) and log in with
|
|
142
|
+
``api_key`` instead.
|
|
143
|
+
|
|
143
144
|
Parameters
|
|
144
145
|
----------
|
|
145
146
|
username : str
|
|
@@ -148,20 +149,10 @@ class Auth:
|
|
|
148
149
|
Example: 'example@qmenta.com'
|
|
149
150
|
password : str
|
|
150
151
|
The QMENTA platform password of the user.
|
|
151
|
-
code_2fa : str
|
|
152
|
-
The 2FA code that was sent to your phone (optional).
|
|
153
152
|
api_key : str
|
|
154
153
|
A QMENTA API key. See the platform manual to learn how to
|
|
155
154
|
generate one. When provided together with ``username`` and
|
|
156
155
|
``password``, the username/password credentials take precedence.
|
|
157
|
-
ask_for_2fa_input: bool
|
|
158
|
-
When set to True, the user is asked input the 2FA code
|
|
159
|
-
in the command-line interface when it is needed. If the user does
|
|
160
|
-
not have 2FA enabled, no input is requested.
|
|
161
|
-
This is useful for scripts.
|
|
162
|
-
When set to False, a Needs2FAError exception is raised when
|
|
163
|
-
a 2FA code is needed. This is useful for GUIs.
|
|
164
|
-
Default value: False
|
|
165
156
|
base_url : str
|
|
166
157
|
The URL of the platform to connect to.
|
|
167
158
|
Default value: 'https://platform.qmenta.com'
|
|
@@ -181,20 +172,15 @@ class Auth:
|
|
|
181
172
|
InvalidLoginError
|
|
182
173
|
If the login was invalid. This can happen when the
|
|
183
174
|
username/password combination is incorrect, when the api_key is
|
|
184
|
-
invalid, when the account is not active
|
|
185
|
-
|
|
186
|
-
are provided.
|
|
175
|
+
invalid, when the account is not active. Also raised when
|
|
176
|
+
neither credentials nor api_key are provided.
|
|
187
177
|
Needs2FAError
|
|
188
|
-
When
|
|
189
|
-
|
|
190
|
-
in the next call to the login function.
|
|
178
|
+
When the account has two-factor authentication enabled.
|
|
179
|
+
Generate an API key and log in with it instead.
|
|
191
180
|
"""
|
|
192
181
|
url: str = urljoin(base_url, '/login')
|
|
193
182
|
if username and password:
|
|
194
|
-
request_data = {
|
|
195
|
-
'username': username, 'password': password,
|
|
196
|
-
'code_2fa': code_2fa
|
|
197
|
-
}
|
|
183
|
+
request_data = {'username': username, 'password': password}
|
|
198
184
|
headers = {}
|
|
199
185
|
elif api_key:
|
|
200
186
|
headers = {
|
|
@@ -231,17 +217,15 @@ class Auth:
|
|
|
231
217
|
if data["success"] != 1:
|
|
232
218
|
# Login was not successful
|
|
233
219
|
if data.get("account_state", "") == '2fa_need':
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
'or set the ask_for_2fa_input parameter'
|
|
244
|
-
)
|
|
220
|
+
raise Needs2FAError(
|
|
221
|
+
'This account has two-factor authentication '
|
|
222
|
+
'enabled, which is no longer supported for '
|
|
223
|
+
'username/password login. Generate an API key '
|
|
224
|
+
'in the platform (My profile -> Credentials) '
|
|
225
|
+
'and log in using an API key instead: pass it '
|
|
226
|
+
'as the api_key parameter, or to the '
|
|
227
|
+
'qmenta-auth --api-key command-line option.'
|
|
228
|
+
)
|
|
245
229
|
else:
|
|
246
230
|
raise InvalidLoginError(data['error'])
|
|
247
231
|
|
|
@@ -344,36 +328,78 @@ def validate_url(url: str) -> None:
|
|
|
344
328
|
|
|
345
329
|
|
|
346
330
|
def main() -> None:
|
|
331
|
+
"""
|
|
332
|
+
Entry point of the ``qmenta-auth`` command-line tool.
|
|
333
|
+
"""
|
|
347
334
|
parser = argparse.ArgumentParser(
|
|
348
335
|
description=(
|
|
349
336
|
'Log in on QMENTA platform and store the authentication '
|
|
350
337
|
'token in a .env file. Username and password may be '
|
|
351
338
|
'provided as parameters, or will be asked as user input. '
|
|
339
|
+
'Alternatively, provide an API key, which is required for '
|
|
340
|
+
'accounts with two-factor authentication enabled.'
|
|
352
341
|
)
|
|
353
342
|
)
|
|
354
343
|
parser.add_argument('--username', help='Username to login',)
|
|
355
344
|
parser.add_argument('--password', help='Password')
|
|
345
|
+
api_key_group = parser.add_mutually_exclusive_group()
|
|
346
|
+
api_key_group.add_argument(
|
|
347
|
+
'--api-key',
|
|
348
|
+
help=(
|
|
349
|
+
'API key to login. See the platform manual to learn how '
|
|
350
|
+
'to generate one. Required if the account has two-factor '
|
|
351
|
+
'authentication enabled.'
|
|
352
|
+
)
|
|
353
|
+
)
|
|
354
|
+
api_key_group.add_argument(
|
|
355
|
+
'--ask-api-key', action='store_true',
|
|
356
|
+
help=(
|
|
357
|
+
'Ask for the API key as user input instead of passing it on '
|
|
358
|
+
'the command line, so that the key is not stored in the '
|
|
359
|
+
'shell history and is not visible in the process list. The '
|
|
360
|
+
'QMENTA_API_KEY environment variable is used when neither '
|
|
361
|
+
'option nor any username or password is given.'
|
|
362
|
+
)
|
|
363
|
+
)
|
|
356
364
|
parser.add_argument(
|
|
357
365
|
'url', help='Platform URL, for example: https://platform.qmenta.com')
|
|
358
366
|
args = parser.parse_args()
|
|
359
367
|
|
|
368
|
+
if (args.api_key or args.ask_api_key) and (
|
|
369
|
+
args.username or args.password):
|
|
370
|
+
print(
|
|
371
|
+
'Provide either --username/--password or '
|
|
372
|
+
'--api-key/--ask-api-key, not both.',
|
|
373
|
+
file=sys.stderr
|
|
374
|
+
)
|
|
375
|
+
sys.exit(1)
|
|
376
|
+
|
|
377
|
+
api_key: Optional[str] = args.api_key
|
|
378
|
+
if api_key is None and not (
|
|
379
|
+
args.ask_api_key or args.username or args.password):
|
|
380
|
+
api_key = os.environ.get('QMENTA_API_KEY') or None
|
|
381
|
+
|
|
360
382
|
try:
|
|
361
383
|
validate_url(args.url)
|
|
362
384
|
except ValueError as err:
|
|
363
|
-
print(err)
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
username = args.username or input("Username: ")
|
|
367
|
-
password = args.password or getpass()
|
|
385
|
+
print(err, file=sys.stderr)
|
|
386
|
+
sys.exit(1)
|
|
368
387
|
|
|
369
388
|
try:
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
389
|
+
if args.ask_api_key:
|
|
390
|
+
api_key = getpass('API key: ')
|
|
391
|
+
|
|
392
|
+
if api_key is not None:
|
|
393
|
+
auth = Auth.login(api_key=api_key, base_url=args.url)
|
|
394
|
+
else:
|
|
395
|
+
username = args.username or input("Username: ")
|
|
396
|
+
password = args.password or getpass()
|
|
397
|
+
auth = Auth.login(
|
|
398
|
+
username=username, password=password, base_url=args.url
|
|
399
|
+
)
|
|
374
400
|
except PlatformError as err:
|
|
375
|
-
print(err)
|
|
376
|
-
|
|
401
|
+
print(err, file=sys.stderr)
|
|
402
|
+
sys.exit(1)
|
|
377
403
|
|
|
378
404
|
write_dot_env_file(token=auth.token, url=auth.base_url)
|
|
379
405
|
|
|
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
|