qmenta-core 5.1.0.dev727__tar.gz → 6.0.0.dev729__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: qmenta-core
3
- Version: 5.1.0.dev727
3
+ Version: 6.0.0.dev729
4
4
  Summary: QMENTA core library to communicate with the QMENTA platform.
5
5
  License: Proprietary
6
6
  Author: QMENTA
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "qmenta-core"
3
- version = "5.1.0.dev727"
3
+ version = "6.0.0.dev729"
4
4
  description = "QMENTA core library to communicate with the QMENTA platform."
5
5
  license = "Proprietary"
6
6
  authors = ["QMENTA <dev@qmenta.com>"]
@@ -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 a 2FA code must to be provided to log in.
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``, ``ask_for_2fa_input``, and ``base_url`` must be passed
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, or when 2FA is required
185
- to be set up. Also raised when neither credentials nor api_key
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 a login attempt was done without a valid 2FA code.
189
- The 2FA code has been sent to your phone, and must be provided
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
- if ask_for_2fa_input:
235
- input_2fa = input("Please enter your 2FA code: ")
236
- return Auth.login(
237
- username, password, code_2fa=input_2fa,
238
- ask_for_2fa_input=True, base_url=base_url
239
- )
240
- else:
241
- raise Needs2FAError(
242
- 'Provide the 2FA code sent to your phone, '
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
- return
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
- auth = Auth.login(
371
- username=username, password=password,
372
- ask_for_2fa_input=True, base_url=args.url
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
- return
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