cornflow 1.0.8a3__py3-none-any.whl → 1.0.10a1__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.
cornflow/cli/__init__.py CHANGED
@@ -3,6 +3,7 @@ init file to have this as a module
3
3
  """
4
4
 
5
5
  import click
6
+
6
7
  from cornflow.cli.actions import actions
7
8
  from cornflow.cli.config import config
8
9
  from cornflow.cli.migrations import migrations
cornflow/cli/actions.py CHANGED
@@ -1,6 +1,5 @@
1
- import os
2
-
3
1
  import click
2
+
4
3
  from cornflow.cli.utils import get_app
5
4
  from cornflow.commands import register_actions_command
6
5
  from .arguments import verbose
cornflow/cli/users.py CHANGED
@@ -1,8 +1,15 @@
1
1
  import click
2
+
2
3
  from cornflow.cli.arguments import username, password, email, verbose
3
4
  from cornflow.cli.utils import get_app
4
5
  from cornflow.commands import create_user_with_role
5
- from cornflow.shared.const import SERVICE_ROLE
6
+ from cornflow.models import UserModel
7
+ from cornflow.shared.authentication.auth import BIAuth
8
+ from cornflow.shared.const import SERVICE_ROLE, VIEWER_ROLE
9
+ from cornflow.shared.exceptions import (
10
+ ObjectDoesNotExist,
11
+ NoPermission,
12
+ )
6
13
 
7
14
 
8
15
  @click.group(name="users", help="Commands to manage the users")
@@ -26,7 +33,45 @@ users.add_command(create)
26
33
  def create_service_user(username, password, email, verbose):
27
34
  app = get_app()
28
35
  with app.app_context():
29
-
30
36
  create_user_with_role(
31
37
  username, email, password, "service user", SERVICE_ROLE, verbose=verbose
32
38
  )
39
+
40
+
41
+ @create.command(name="viewer", help="Create a viewer user")
42
+ @username
43
+ @password
44
+ @email
45
+ @verbose
46
+ def create_viewer_user(username, password, email, verbose):
47
+ app = get_app()
48
+ with app.app_context():
49
+ create_user_with_role(
50
+ username, email, password, "viewer user", VIEWER_ROLE, verbose=verbose
51
+ )
52
+
53
+
54
+ @create.command(
55
+ name="token",
56
+ help="Creates a token for a user that is never going to expire. This token can only be used on BI endpoints",
57
+ )
58
+ @click.option(
59
+ "--idx", "-i", type=int, help="The id of the user to generate the token for"
60
+ )
61
+ @username
62
+ @password
63
+ def create_unexpiring_token(idx, username, password):
64
+ app = get_app()
65
+ with app.app_context():
66
+ user = UserModel.get_one_object(id=idx)
67
+ asking_user = UserModel.get_one_user_by_username(username)
68
+
69
+ if not asking_user.check_hash(password) or not asking_user.is_service_user():
70
+ raise NoPermission("The asking user has no permissions to generate tokens")
71
+
72
+ if not user:
73
+ raise ObjectDoesNotExist("User does not exist")
74
+
75
+ token = BIAuth.generate_token(idx)
76
+ click.echo(token)
77
+ return True
cornflow/config.py CHANGED
@@ -6,7 +6,8 @@ from apispec.ext.marshmallow import MarshmallowPlugin
6
6
 
7
7
  class DefaultConfig(object):
8
8
  SERVICE_NAME = os.getenv("SERVICE_NAME", "Cornflow")
9
- SECRET_KEY = os.getenv("SECRET_KEY")
9
+ SECRET_TOKEN_KEY = os.getenv("SECRET_KEY")
10
+ SECRET_BI_KEY = os.getenv("SECRET_BI_KEY")
10
11
  SQLALCHEMY_DATABASE_URI = os.getenv("DATABASE_URL", "sqlite:///cornflow.db")
11
12
  AIRFLOW_URL = os.getenv("AIRFLOW_URL")
12
13
  AIRFLOW_USER = os.getenv("AIRFLOW_USER")
@@ -91,7 +92,8 @@ class Testing(DefaultConfig):
91
92
  DEBUG = False
92
93
  TESTING = True
93
94
  PROPAGATE_EXCEPTIONS = True
94
- SECRET_KEY = "TESTINGSECRETKEY"
95
+ SECRET_TOKEN_KEY = "TESTINGSECRETKEY"
96
+ SECRET_BI_KEY = "THISISANOTHERKEY"
95
97
  SQLALCHEMY_DATABASE_URI = os.getenv("DATABASE_URL", "sqlite:///cornflow_test.db")
96
98
  AIRFLOW_URL = os.getenv("AIRFLOW_URL", "http://localhost:8080")
97
99
  PRESERVE_CONTEXT_ON_EXCEPTION = False
@@ -99,8 +99,7 @@ class Auth:
99
99
  if user_id is None:
100
100
  err = "The user id passed to generate the token is not valid."
101
101
  raise InvalidUsage(
102
- err,
103
- log_txt="Error while trying to generate token. " + err
102
+ err, log_txt="Error while trying to generate token. " + err
104
103
  )
105
104
 
106
105
  payload = {
@@ -109,7 +108,9 @@ class Auth:
109
108
  "sub": user_id,
110
109
  }
111
110
 
112
- return jwt.encode(payload, current_app.config["SECRET_KEY"], algorithm="HS256")
111
+ return jwt.encode(
112
+ payload, current_app.config["SECRET_TOKEN_KEY"], algorithm="HS256"
113
+ )
113
114
 
114
115
  @staticmethod
115
116
  def decode_token(token: str = None) -> dict:
@@ -123,27 +124,26 @@ class Auth:
123
124
  if token is None:
124
125
  err = "The provided token is not valid."
125
126
  raise InvalidUsage(
126
- err,
127
- log_txt="Error while trying to decode token. " + err
127
+ err, log_txt="Error while trying to decode token. " + err
128
128
  )
129
129
  try:
130
130
  payload = jwt.decode(
131
- token, current_app.config["SECRET_KEY"], algorithms="HS256"
131
+ token, current_app.config["SECRET_TOKEN_KEY"], algorithms="HS256"
132
132
  )
133
133
  return {"user_id": payload["sub"]}
134
134
  except jwt.ExpiredSignatureError:
135
135
  raise InvalidCredentials(
136
136
  "The token has expired, please login again",
137
- log_txt="Error while trying to decode token. The token has expired."
137
+ log_txt="Error while trying to decode token. The token has expired.",
138
138
  )
139
139
  except jwt.InvalidTokenError:
140
140
  raise InvalidCredentials(
141
141
  "Invalid token, please try again with a new token",
142
- log_txt="Error while trying to decode token. The token is invalid."
142
+ log_txt="Error while trying to decode token. The token is invalid.",
143
143
  )
144
144
 
145
145
  def validate_oid_token(
146
- self, token: str, client_id: str, tenant_id: str, issuer: str, provider: int
146
+ self, token: str, client_id: str, tenant_id: str, issuer: str, provider: int
147
147
  ) -> dict:
148
148
  """
149
149
  This method takes a token issued by an OID provider, the relevant information about the OID provider
@@ -172,12 +172,12 @@ class Auth:
172
172
  except jwt.ExpiredSignatureError:
173
173
  raise InvalidCredentials(
174
174
  "The token has expired, please login again",
175
- log_txt="Error while trying to validate a token. The token has expired. "
175
+ log_txt="Error while trying to validate a token. The token has expired.",
176
176
  )
177
177
  except jwt.InvalidTokenError:
178
178
  raise InvalidCredentials(
179
179
  "Invalid token, please try again with a new token",
180
- log_txt="Error while trying to validate a token. The token is not valid. "
180
+ log_txt="Error while trying to validate a token. The token is not valid.",
181
181
  )
182
182
 
183
183
  @staticmethod
@@ -191,12 +191,14 @@ class Auth:
191
191
  :rtype: str
192
192
  """
193
193
  if headers is None:
194
- raise InvalidUsage(log_txt="Error while trying to get a token from header. The header is invalid.")
194
+ raise InvalidUsage(
195
+ log_txt="Error while trying to get a token from header. The header is invalid."
196
+ )
195
197
 
196
198
  if "Authorization" not in headers:
197
199
  raise InvalidCredentials(
198
200
  "Auth token is not available",
199
- log_txt="Error while trying to get a token from header. The auth token is not available."
201
+ log_txt="Error while trying to get a token from header. The auth token is not available.",
200
202
  )
201
203
  auth_header = headers.get("Authorization")
202
204
  if not auth_header:
@@ -206,8 +208,7 @@ class Auth:
206
208
  except Exception as e:
207
209
  err = f"The authorization header has a bad syntax: {e}"
208
210
  raise InvalidCredentials(
209
- err,
210
- log_txt=f"Error while trying to get a token from header. " + err
211
+ err, log_txt=f"Error while trying to get a token from header. " + err
211
212
  )
212
213
 
213
214
  def get_user_from_header(self, headers: Headers = None) -> UserModel:
@@ -222,8 +223,7 @@ class Auth:
222
223
  if headers is None:
223
224
  err = "Headers are missing from the request. Authentication was not possible to perform."
224
225
  raise InvalidUsage(
225
- err,
226
- log_txt="Error while trying to get user from header. " + err
226
+ err, log_txt="Error while trying to get user from header. " + err
227
227
  )
228
228
  token = self.get_token_from_header(headers)
229
229
  data = self.decode_token(token)
@@ -232,8 +232,7 @@ class Auth:
232
232
  if user is None:
233
233
  err = "User does not exist, invalid token."
234
234
  raise ObjectDoesNotExist(
235
- err,
236
- log_txt="Error while trying to get user from header. " + err
235
+ err, log_txt="Error while trying to get user from header. " + err
237
236
  )
238
237
  return user
239
238
 
@@ -460,3 +459,57 @@ class Auth:
460
459
  kid = self._get_key_id(token)
461
460
  jwk = self._get_jwk(kid, tenant_id, provider)
462
461
  return self._rsa_pem_from_jwk(jwk)
462
+
463
+
464
+ class BIAuth(Auth):
465
+ def __init__(self, user_model=UserModel):
466
+ super().__init__(user_model)
467
+
468
+ @staticmethod
469
+ def decode_token(token: str = None) -> dict:
470
+ """
471
+ Decodes a given JSON Web token and extracts the sub from it to give it back.
472
+
473
+ :param str token: the given JSON Web Token
474
+ :return: the sub field of the token as the user_id
475
+ :rtype: dict
476
+ """
477
+ if token is None:
478
+ err = "The provided token is not valid."
479
+ raise InvalidUsage(
480
+ err, log_txt="Error while trying to decode token. " + err
481
+ )
482
+ try:
483
+ payload = jwt.decode(
484
+ token, current_app.config["SECRET_BI_KEY"], algorithms="HS256"
485
+ )
486
+ return {"user_id": payload["sub"]}
487
+ except jwt.InvalidTokenError:
488
+ raise InvalidCredentials(
489
+ "Invalid token, please try again with a new token",
490
+ log_txt="Error while trying to decode token. The token is invalid.",
491
+ )
492
+
493
+ @staticmethod
494
+ def generate_token(user_id: int = None) -> str:
495
+ """
496
+ Generates a token given a user_id with a duration of one day
497
+
498
+ :param int user_id: user code to be encoded in the token to identify the user afterward.
499
+ :return: the generated token
500
+ :rtype: str
501
+ """
502
+ if user_id is None:
503
+ err = "The user id passed to generate the token is not valid."
504
+ raise InvalidUsage(
505
+ err, log_txt="Error while trying to generate token. " + err
506
+ )
507
+
508
+ payload = {
509
+ "iat": datetime.utcnow(),
510
+ "sub": user_id,
511
+ }
512
+
513
+ return jwt.encode(
514
+ payload, current_app.config["SECRET_BI_KEY"], algorithm="HS256"
515
+ )
@@ -65,6 +65,7 @@ def get_licenses_summary():
65
65
  :return: a list of dicts with library, license, version, author, description, home page and license text.
66
66
  """
67
67
  license_list = []
68
+ # TODO: pkg_resources.working_set is deprecated, find a better way to get the list of packages
68
69
  for pkg in sorted(pkg_resources.working_set, key=lambda x: str(x).lower()):
69
70
  license_list += [
70
71
  {
@@ -101,7 +101,6 @@ class CustomTestCase(TestCase):
101
101
 
102
102
  @staticmethod
103
103
  def assign_role(user_id, role_id):
104
-
105
104
  if UserRoleModel.check_if_role_assigned(user_id, role_id):
106
105
  user_role = UserRoleModel.query.filter_by(
107
106
  user_id=user_id, role_id=role_id
@@ -288,7 +287,6 @@ class CustomTestCase(TestCase):
288
287
  self.assertEqual(payload_to_check["solution"], row.json["solution"])
289
288
 
290
289
  def delete_row(self, url):
291
-
292
290
  response = self.client.delete(
293
291
  url, follow_redirects=True, headers=self.get_header_with_auth(self.token)
294
292
  )
@@ -733,7 +731,7 @@ class LoginTestCases:
733
731
  self.assertEqual(str, type(self.response.json["token"]))
734
732
  decoded_token = jwt.decode(
735
733
  self.response.json["token"],
736
- current_app.config["SECRET_KEY"],
734
+ current_app.config["SECRET_TOKEN_KEY"],
737
735
  algorithms="HS256",
738
736
  )
739
737
 
@@ -2,17 +2,19 @@ import configparser
2
2
  import os
3
3
 
4
4
  from click.testing import CliRunner
5
+ from flask_testing import TestCase
6
+
5
7
  from cornflow.app import create_app
6
8
  from cornflow.cli import cli
7
- from cornflow.models import UserModel
8
9
  from cornflow.models import (
9
10
  ActionModel,
10
11
  RoleModel,
11
12
  ViewModel,
12
13
  PermissionViewRoleModel,
13
14
  )
15
+ from cornflow.models import UserModel
14
16
  from cornflow.shared import db
15
- from flask_testing import TestCase
17
+ from cornflow.shared.exceptions import NoPermission, ObjectDoesNotExist
16
18
 
17
19
 
18
20
  class CLITests(TestCase):
@@ -208,6 +210,7 @@ class CLITests(TestCase):
208
210
 
209
211
  def test_service_user_command(self):
210
212
  runner = CliRunner()
213
+ self.test_roles_init_command()
211
214
  result = runner.invoke(
212
215
  cli,
213
216
  [
@@ -222,7 +225,144 @@ class CLITests(TestCase):
222
225
  "test@test.org",
223
226
  ],
224
227
  )
225
- self.assertEqual(result.exit_code, 1)
228
+ self.assertEqual(result.exit_code, 0)
226
229
  user = UserModel.get_one_user_by_email("test@test.org")
227
230
  self.assertEqual(user.username, "test")
228
231
  self.assertEqual(user.email, "test@test.org")
232
+ self.assertEqual(user.roles, {4: "service"})
233
+ self.assertTrue(user.is_service_user())
234
+
235
+ def test_viewer_user_command(self):
236
+ runner = CliRunner()
237
+ self.test_roles_init_command()
238
+ result = runner.invoke(
239
+ cli,
240
+ [
241
+ "users",
242
+ "create",
243
+ "viewer",
244
+ "-u",
245
+ "test",
246
+ "-p",
247
+ "testPassword1!",
248
+ "-e",
249
+ "test@test.org",
250
+ ],
251
+ )
252
+
253
+ self.assertEqual(result.exit_code, 0)
254
+ user = UserModel.get_one_user_by_email("test@test.org")
255
+ self.assertEqual(user.username, "test")
256
+ self.assertEqual(user.email, "test@test.org")
257
+ self.assertEqual(user.roles, {1: "viewer"})
258
+ self.assertFalse(user.is_service_user())
259
+
260
+ def test_generate_token(self):
261
+ runner = CliRunner()
262
+
263
+ self.test_roles_init_command()
264
+
265
+ result = runner.invoke(
266
+ cli,
267
+ [
268
+ "users",
269
+ "create",
270
+ "viewer",
271
+ "-u",
272
+ "viewer_user",
273
+ "-p",
274
+ "testPassword1!",
275
+ "-e",
276
+ "viewer@test.org",
277
+ ],
278
+ )
279
+
280
+ self.assertEqual(result.exit_code, 0)
281
+
282
+ user_id = UserModel.get_one_user_by_username("viewer_user").id
283
+
284
+ result = runner.invoke(
285
+ cli,
286
+ [
287
+ "users",
288
+ "create",
289
+ "service",
290
+ "-u",
291
+ "test",
292
+ "-p",
293
+ "testPassword1!",
294
+ "-e",
295
+ "test@test.org",
296
+ ],
297
+ )
298
+
299
+ self.assertEqual(result.exit_code, 0)
300
+
301
+ result = runner.invoke(
302
+ cli,
303
+ [
304
+ "users",
305
+ "create",
306
+ "token",
307
+ "-i",
308
+ user_id,
309
+ "-u",
310
+ "test",
311
+ "-p",
312
+ "testPassword1!",
313
+ ],
314
+ )
315
+
316
+ self.assertIn("ey", result.output)
317
+
318
+ result = runner.invoke(
319
+ cli,
320
+ [
321
+ "users",
322
+ "create",
323
+ "token",
324
+ "-i",
325
+ user_id,
326
+ "-u",
327
+ "test",
328
+ "-p",
329
+ "Otherpassword",
330
+ ],
331
+ )
332
+
333
+ self.assertEqual(result.exit_code, 1)
334
+ self.assertIsInstance(result.exception, NoPermission)
335
+
336
+ result = runner.invoke(
337
+ cli,
338
+ [
339
+ "users",
340
+ "create",
341
+ "token",
342
+ "-i",
343
+ user_id,
344
+ "-u",
345
+ "viewer_user",
346
+ "-p",
347
+ "testPassword1!",
348
+ ],
349
+ )
350
+
351
+ self.assertIsInstance(result.exception, NoPermission)
352
+
353
+ result = runner.invoke(
354
+ cli,
355
+ [
356
+ "users",
357
+ "create",
358
+ "token",
359
+ "-i",
360
+ 100,
361
+ "-u",
362
+ "test",
363
+ "-p",
364
+ "testPassword1!",
365
+ ],
366
+ )
367
+
368
+ self.assertIsInstance(result.exception, ObjectDoesNotExist)
@@ -11,9 +11,9 @@ class TestLicensesListEndpoint(CustomTestCase):
11
11
  requirements = content.split("\n")
12
12
 
13
13
  requirements = [
14
- r.split("=")[0].split(">")[0].split("<")[0].lower()
14
+ r.split("=")[0].split(">")[0].split("<")[0].split("@")[0].lower()
15
15
  for r in requirements
16
- if r != ""
16
+ if r != "" and not r.startswith("#")
17
17
  ]
18
18
  return requirements
19
19
 
@@ -1,16 +1,16 @@
1
1
  """
2
2
  Unit test for the token endpoint
3
3
  """
4
+ import json
4
5
 
5
- # Import from libraries
6
6
  from flask import current_app
7
- import json
8
7
 
9
- # Import from internal modules
10
8
  from cornflow.models import UserModel
11
9
  from cornflow.shared import db
12
- from cornflow.tests.custom_test_case import CheckTokenTestCase
10
+ from cornflow.shared.authentication.auth import BIAuth, Auth
11
+ from cornflow.shared.exceptions import InvalidUsage
13
12
  from cornflow.tests.const import LOGIN_URL
13
+ from cornflow.tests.custom_test_case import CheckTokenTestCase, CustomTestCase
14
14
 
15
15
 
16
16
  class TestCheckToken(CheckTokenTestCase.TokenEndpoint):
@@ -64,3 +64,34 @@ class TestCheckToken(CheckTokenTestCase.TokenEndpoint):
64
64
  self.get_check_token()
65
65
  self.assertEqual(200, self.response.status_code)
66
66
  self.assertEqual(0, self.response.json["valid"])
67
+
68
+
69
+ class TestUnexpiringToken(CustomTestCase):
70
+ def test_token_unexpiring(self):
71
+ auth = BIAuth()
72
+
73
+ token = auth.generate_token(1)
74
+
75
+ response = auth.decode_token(token)
76
+ self.assertEqual(response, {"user_id": 1})
77
+
78
+ token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MDM1OTI1OTIsInN1YiI6MX0.Plvmi02FMfZOTn6bxArELEmDeyuP-2X794c5VtAFgCg"
79
+
80
+ response = auth.decode_token(token)
81
+ self.assertEqual(response, {"user_id": 1})
82
+
83
+ def test_user_not_valid(self):
84
+ auth = BIAuth()
85
+ self.assertRaises(InvalidUsage, auth.generate_token, None)
86
+
87
+ auth = Auth()
88
+ self.assertRaises(InvalidUsage, auth.generate_token, None)
89
+
90
+ def test_token_not_valid(self):
91
+ auth = BIAuth()
92
+ self.assertRaises(InvalidUsage, auth.decode_token, None)
93
+ token = ""
94
+ self.assertRaises(InvalidUsage, auth.decode_token, token)
95
+
96
+ auth = Auth()
97
+ self.assertRaises(InvalidUsage, auth.decode_token, None)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cornflow
3
- Version: 1.0.8a3
3
+ Version: 1.0.10a1
4
4
  Summary: Cornflow is an open source multi-solver optimization server with a REST API built using flask.
5
5
  Home-page: https://github.com/baobabsoluciones/cornflow
6
6
  Author: baobab soluciones
@@ -15,8 +15,8 @@ Requires-Python: >=3.8
15
15
  Requires-Dist: alembic ==1.9.2
16
16
  Requires-Dist: apispec <=6.2.0
17
17
  Requires-Dist: click <=8.1.3
18
- Requires-Dist: cornflow-client ==1.0.16a2
19
- Requires-Dist: cryptography <=39.0.2
18
+ Requires-Dist: cornflow-client <=1.0.16
19
+ Requires-Dist: cryptography <=42.0.5
20
20
  Requires-Dist: disposable-email-domains >=0.0.86
21
21
  Requires-Dist: Flask ==2.3.2
22
22
  Requires-Dist: flask-apispec <=0.11.4
@@ -28,7 +28,7 @@ Requires-Dist: Flask-Migrate <=4.0.4
28
28
  Requires-Dist: Flask-RESTful <=0.3.9
29
29
  Requires-Dist: Flask-SQLAlchemy ==2.5.1
30
30
  Requires-Dist: gevent ==23.9.1
31
- Requires-Dist: gunicorn <=20.1.0
31
+ Requires-Dist: gunicorn <=22.0.0
32
32
  Requires-Dist: jsonpatch <=1.32
33
33
  Requires-Dist: ldap3 <=2.9.1
34
34
  Requires-Dist: marshmallow <=3.19.0
@@ -36,10 +36,10 @@ Requires-Dist: PuLP <=2.7.0
36
36
  Requires-Dist: psycopg2 <=2.95
37
37
  Requires-Dist: PyJWT <=2.6.0
38
38
  Requires-Dist: pytups >=0.86.2
39
- Requires-Dist: requests <=2.29.0
39
+ Requires-Dist: requests <=2.31.0
40
40
  Requires-Dist: SQLAlchemy ==1.3.21
41
41
  Requires-Dist: webargs <=8.2.0
42
- Requires-Dist: Werkzeug <=2.3.3
42
+ Requires-Dist: Werkzeug <=2.3.8
43
43
  Requires-Dist: greenlet <=2.0.2 ; python_version < "3.11"
44
44
  Requires-Dist: greenlet ==3.0.0 ; python_version >= "3.11"
45
45
 
@@ -6,10 +6,10 @@ airflow_config/plugins/XCom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
6
6
  airflow_config/plugins/XCom/gce_xcom_backend.py,sha256=vCGvF2jbfZt5bOv-pk5Q_kUR6LomFUojIymimSJmj3o,1795
7
7
  cornflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  cornflow/app.py,sha256=3tj9aMOLFzBGS4QFPD0Zt2A7N0C5lssvy8NZ-G4sU9E,6997
9
- cornflow/config.py,sha256=GBG7xMgSxl4paNu05aSb1vQ3iN9gsOXYulRkW_j-jQY,4458
9
+ cornflow/config.py,sha256=mZyCr8BDMfLhR0J0UlYZDulYRYJs-ApTzvXed0wyZfE,4556
10
10
  cornflow/gunicorn.py,sha256=uO-Yk7w7nvQSWh12iDxsVvlG-_2BiKIIjm2UiTk4P9E,480
11
- cornflow/cli/__init__.py,sha256=17qqoMjrkvKm4AV3WA-XApyVbkMrH6md_Ri7nVEKPEE,742
12
- cornflow/cli/actions.py,sha256=VYoQTK0RusGluEbuYwGX1AeCumLZIDo9rWPLw3Ggp9A,424
11
+ cornflow/cli/__init__.py,sha256=5jBmSMpaE1S9rDaQjS8VHJ6x4FfJG8MhKzMzfw7G4Zc,743
12
+ cornflow/cli/actions.py,sha256=BdTFucT6gZ0QJqo96Zu0C2G9acZ578tLkktKSfTybJ8,414
13
13
  cornflow/cli/arguments.py,sha256=9EEyyny5cJJ1t3WAs6zMgTDvTre0JdQ2N_oZfFQmixs,657
14
14
  cornflow/cli/config.py,sha256=_7Y6tDo5uu4riymkzMYHnTR9IYxBG_FsjwmB84Du90U,1148
15
15
  cornflow/cli/migrations.py,sha256=Stc8H99rG8vgo3yRJcck11zBY_EA4WqyVybglfl8zJE,1624
@@ -17,7 +17,7 @@ cornflow/cli/permissions.py,sha256=4KXKysH4g8YYQIZcPuXFS2g0xEErp-e8I_FAqMGaV7U,1
17
17
  cornflow/cli/roles.py,sha256=NFG__qrlyOT0h4L4nwo9FSV4DKjGtMVh3gwiJxwM37w,411
18
18
  cornflow/cli/schemas.py,sha256=sxuJOZf12SBZAXDiAYNPB-n9LSxzSwkB3xyhgS_4K9A,6086
19
19
  cornflow/cli/service.py,sha256=yhQT860oXmQFU_maHHL276tYubKUs6YcwheWv7x7oh0,9135
20
- cornflow/cli/users.py,sha256=QvvFJw_tjgYB4GjwXmGTAH_FT_Z6nATg9gmkCbcyLJ8,748
20
+ cornflow/cli/users.py,sha256=nPnu8rQNLtwmeXLwYtJ_hjlsa_24XOnQLgBJRBP9bJw,2104
21
21
  cornflow/cli/utils.py,sha256=0tF41gTt6LL9XGOizTQg2GXuOXbqLg6gapCr-HWjJ0Q,733
22
22
  cornflow/cli/views.py,sha256=Xyx2l-Sm7panxQEfR3qksCIUoqF7woMKsYgZALkxUXM,636
23
23
  cornflow/cli/tools/__init__.py,sha256=JtyhYfUfirw78BV1mCsdeY0W25fDPWTmZhNBWdDh0wA,19
@@ -121,20 +121,20 @@ cornflow/shared/compress.py,sha256=pohQaGs1xbH8CN6URIH6BAHA--pFq7Hmjz8oI3c3B5c,1
121
121
  cornflow/shared/const.py,sha256=nRZElCjbuJIpjzVlCfZjTN4mAbqDTXIyAbSMlkNL3n8,3440
122
122
  cornflow/shared/email.py,sha256=QNDDMv86LZObkevSCyUbLQeR2UD3zWScPIr82NDzYHQ,3437
123
123
  cornflow/shared/exceptions.py,sha256=BNbC5hzAoC9vDQ3NLM9uLqI14nwCEP1AT3UjeFghnY0,6979
124
- cornflow/shared/licenses.py,sha256=CWuXNETLrDGhODG5v5zHOltRLRXLIrgvVFEBr_1IcOA,2235
124
+ cornflow/shared/licenses.py,sha256=t3RpNEmmyY_dHel9Ss2FpCn_jD2g7-GYVS5jWpnA5Rc,2334
125
125
  cornflow/shared/log_config.py,sha256=FM2ajjp2MB4BlFbUHklnWInT7-LLjtrqQ0mo3k_HRmE,621
126
126
  cornflow/shared/query_tools.py,sha256=6yGLCWjv-I2a_ZU4A0IymyJq67fZPZdRcCGOGQQpSXg,1199
127
127
  cornflow/shared/utils.py,sha256=g2ZsD3SwsqIHXZ7GWVAVB0F9gX7mT9dQkPgR2Ahsh6M,860
128
128
  cornflow/shared/utils_tables.py,sha256=A7bjpt7Metkb0FP7tKXMqOkak2fgi3O9dejYvoJBpb0,2236
129
129
  cornflow/shared/validators.py,sha256=aFKAAJ2diElUA8WdDyCcXJI6r3FV7HFVzoOTC6t4f8Y,4803
130
130
  cornflow/shared/authentication/__init__.py,sha256=cJIChk5X6hbA_16usEvfHr8g4JDFI6WKo0GPVOOiYHA,137
131
- cornflow/shared/authentication/auth.py,sha256=VuM8kmjSi2G_4BR9sop6vn13SFEHiixFBOhB_Pm196M,16840
131
+ cornflow/shared/authentication/auth.py,sha256=N0L074N7HxZ7TOhmPxIy8WSa7J4wofCbYVVCANI-Pn0,18621
132
132
  cornflow/shared/authentication/decorators.py,sha256=_QpwOU1kYzpaK85Dl0Btdj5hG8Ps47PFgySp_gqhlgk,1276
133
133
  cornflow/shared/authentication/ldap.py,sha256=QfdC2X_ZMcIJabKC5pYWDGMhS5pIOJJvdZXuuiruq-M,4853
134
134
  cornflow/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
135
135
  cornflow/tests/const.py,sha256=_5BYFGN42Xg0PXMR8UU5DBL6dYmYn5rgRBgPyptrKso,2499
136
136
  cornflow/tests/custom_liveServer.py,sha256=I_0YNrcKIwVmRov3zCQMWwcCWkMe5V246Hpa4gS8AZE,3079
137
- cornflow/tests/custom_test_case.py,sha256=3nHflrRoBXSaws0XSdbBaV21mTYL8LzDJs6wOyFBdmQ,24773
137
+ cornflow/tests/custom_test_case.py,sha256=6CJvYVlv6pdac1M--W80kUQ3qWEWIiGXNTvgYFaLRjw,24777
138
138
  cornflow/tests/integration/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
139
139
  cornflow/tests/integration/test_commands.py,sha256=FZcoEM-05D4MBMe0L0V-0sxk_L0zMbzQxb9UCd7iBe0,695
140
140
  cornflow/tests/integration/test_cornflowclient.py,sha256=f77tPUmURdIdDoJCCVIom4PXsX95UARsBIPAtSy_AGc,20646
@@ -145,7 +145,7 @@ cornflow/tests/unit/test_actions.py,sha256=Fg33PyzNTK4B4NjARelMxLQpQl9nE9Jppolkk
145
145
  cornflow/tests/unit/test_alarms.py,sha256=J4Hp2xIZqpFrojx_RvQCSU1ilDk-iC0vm2z8wZNXwIw,1343
146
146
  cornflow/tests/unit/test_apiview.py,sha256=G5DpUPaKVXgbCOaXXTCjBAeGeCtfR8niltp7B0NNB6o,2107
147
147
  cornflow/tests/unit/test_cases.py,sha256=Wz6AQj4sEyRKu0WT-si11gR7H9p6JT_f2Sz44mG7eTU,23985
148
- cornflow/tests/unit/test_cli.py,sha256=kQgXEY86rtJqmNu63bJEyILnZaftKoPRenDa0-wE1-Q,9150
148
+ cornflow/tests/unit/test_cli.py,sha256=IK7nhPpVy2dSXGRSies_Lh9DImNaL2SFCXb-gxKUmDI,12578
149
149
  cornflow/tests/unit/test_commands.py,sha256=QwGHTOxBOwiIYYQg8wcmSR11lKQk0I8Ltr3sbFERufw,8776
150
150
  cornflow/tests/unit/test_dags.py,sha256=Vww3_TDmHUnPvX2BJac6fXMp4y4UOWqnkYuzdVkBa4k,9034
151
151
  cornflow/tests/unit/test_data_checks.py,sha256=VjB3AAQOHlqnaRT2jI9L2mNLDAcda6llpiZWkW7nnkk,5471
@@ -155,7 +155,7 @@ cornflow/tests/unit/test_generate_from_schema.py,sha256=L1EdnASbDJ8SjrX1V4WnUKKw
155
155
  cornflow/tests/unit/test_health.py,sha256=0E0HXMb63_Z8drbLZdxnJwtTbQyaZS9ZEHut6qsDbh8,1033
156
156
  cornflow/tests/unit/test_instances.py,sha256=FWNxeP9LMkeCAoniEHioSFN2W_4yqbI-69pAMLkh-sY,9650
157
157
  cornflow/tests/unit/test_instances_file.py,sha256=zXxSlOM_MMkFvpWNX-iatD40xoIAOGQkinCLf1txb0M,1986
158
- cornflow/tests/unit/test_licenses.py,sha256=9vYkJZw7Ubza2_pvpgrbyiat6hpLsglgoCPdOzdammo,1494
158
+ cornflow/tests/unit/test_licenses.py,sha256=jgnfE4UMFooGn44HK_KspJXIpmLjUpK_WgsBBeTO5eI,1534
159
159
  cornflow/tests/unit/test_log_in.py,sha256=zwVCNO0sGQhpVcUaJnh8cVv2z-qPEYCdI98y61CNyfE,979
160
160
  cornflow/tests/unit/test_main_alarms.py,sha256=H1LWQETlSQMsC6Rb3BB9ds7xL705t9aiPyp4gCWMwDQ,1970
161
161
  cornflow/tests/unit/test_permissions.py,sha256=4mLj3GI0Bvhy927eXu_RyAmK8i2XD7raYc6W8lyAO04,8782
@@ -164,11 +164,11 @@ cornflow/tests/unit/test_schema_from_models.py,sha256=7IfycOGO3U06baX8I-OPJfu-3Z
164
164
  cornflow/tests/unit/test_schemas.py,sha256=nX78H0QhG3fThnw8CqS7EhDyuXWwMiA0pXVy52qhlUk,7177
165
165
  cornflow/tests/unit/test_sign_up.py,sha256=-i6VO9z1FwqRHFvaSrpWAzOZx6qa8mHUEmmsjuMXjn8,3481
166
166
  cornflow/tests/unit/test_tables.py,sha256=dY55YgaCkyqwJnqn0LbZHNeXBoL4ZxXWwKkCoTF4WVE,8947
167
- cornflow/tests/unit/test_token.py,sha256=r0hzozPTlYmmWsxbFXQ5QovXIg5sES6b2--2dzlEreI,2068
167
+ cornflow/tests/unit/test_token.py,sha256=QrEoBMeNvljKTmXxrTi4bS5GIVOyYLxhm3vbASD5GdI,3132
168
168
  cornflow/tests/unit/test_users.py,sha256=_GGnT3ltVEDWJcBUulUflhEx2eKIjbSFi74zY211k1I,21677
169
169
  cornflow/tests/unit/tools.py,sha256=ag3sWv2WLi498R1GL5AOUnXqSsszD3UugzLZLC5NqAw,585
170
- cornflow-1.0.8a3.dist-info/METADATA,sha256=T9VLnGFTefHRhHzbLhnr63uwAn6XiEDDuLN8aYhvTuU,9404
171
- cornflow-1.0.8a3.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
172
- cornflow-1.0.8a3.dist-info/entry_points.txt,sha256=r5wKLHpuyVLMUIZ5I29_tpqYf-RuP-3w_8DhFi8_blQ,47
173
- cornflow-1.0.8a3.dist-info/top_level.txt,sha256=Qj9kLFJW1PLb-ZV2s_aCkQ-Wi5W6KC6fFR-LTBrx-rU,24
174
- cornflow-1.0.8a3.dist-info/RECORD,,
170
+ cornflow-1.0.10a1.dist-info/METADATA,sha256=CH8RdzjxUfYIKOAp8mKl9dp_8PrNLbHzDPpFjou0Dq0,9403
171
+ cornflow-1.0.10a1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
172
+ cornflow-1.0.10a1.dist-info/entry_points.txt,sha256=r5wKLHpuyVLMUIZ5I29_tpqYf-RuP-3w_8DhFi8_blQ,47
173
+ cornflow-1.0.10a1.dist-info/top_level.txt,sha256=Qj9kLFJW1PLb-ZV2s_aCkQ-Wi5W6KC6fFR-LTBrx-rU,24
174
+ cornflow-1.0.10a1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.2)
2
+ Generator: bdist_wheel (0.43.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5