cornflow 1.1.5a1__py3-none-any.whl → 1.2.0a1__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.
@@ -27,12 +27,12 @@ import jwt
27
27
 
28
28
  # Import from internal modules
29
29
  from cornflow.app import create_app
30
- from cornflow.models import UserRoleModel
30
+ from cornflow.models import UserRoleModel, UserModel
31
31
  from cornflow.commands.access import access_init_command
32
32
  from cornflow.commands.dag import register_deployed_dags_command_test
33
33
  from cornflow.commands.permissions import register_dag_permissions_command
34
34
  from cornflow.shared.authentication import Auth
35
- from cornflow.shared.const import ADMIN_ROLE, PLANNER_ROLE, SERVICE_ROLE
35
+ from cornflow.shared.const import ADMIN_ROLE, PLANNER_ROLE, SERVICE_ROLE, INTERNAL_TOKEN_ISSUER
36
36
  from cornflow.shared import db
37
37
  from cornflow.tests.const import (
38
38
  LOGIN_URL,
@@ -121,7 +121,8 @@ class CustomTestCase(TestCase):
121
121
  headers={"Content-Type": "application/json"},
122
122
  ).json["token"]
123
123
 
124
- self.user = Auth.return_user_from_token(self.token)
124
+ data = Auth().decode_token(self.token)
125
+ self.user = UserModel.get_one_object(username=data["username"])
125
126
  self.url = None
126
127
  self.model = None
127
128
  self.copied_items = set()
@@ -828,7 +829,7 @@ class CheckTokenTestCase:
828
829
  follow_redirects=True,
829
830
  headers={
830
831
  "Content-Type": "application/json",
831
- "Authorization": "Bearer " + self.token,
832
+ "Authorization": f"Bearer {self.token}",
832
833
  },
833
834
  )
834
835
  else:
@@ -982,34 +983,45 @@ class LoginTestCases:
982
983
  """
983
984
  Tests using an expired token.
984
985
  """
985
- token = (
986
- "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MTA1MzYwNjUsImlhdCI6MTYxMDQ0OTY2NSwic3ViIjoxfQ"
987
- ".QEfmO-hh55PjtecnJ1RJT3aW2brGLadkg5ClH9yrRnc "
988
- )
989
-
986
+ # First log in to get a valid user
990
987
  payload = self.data
991
-
992
988
  response = self.client.post(
993
989
  LOGIN_URL,
994
990
  data=json.dumps(payload),
995
991
  follow_redirects=True,
996
992
  headers={"Content-Type": "application/json"},
997
993
  )
998
-
999
994
  self.idx = response.json["id"]
1000
995
 
996
+ # Generate an expired token
997
+ expired_payload = {
998
+ # Token expired 1 hour ago
999
+ "exp": datetime.utcnow() - timedelta(hours=1),
1000
+ # Token created 2 hours ago
1001
+ "iat": datetime.utcnow() - timedelta(hours=2),
1002
+ "sub": self.idx,
1003
+ "iss": INTERNAL_TOKEN_ISSUER,
1004
+ }
1005
+ expired_token = jwt.encode(
1006
+ expired_payload,
1007
+ current_app.config["SECRET_TOKEN_KEY"],
1008
+ algorithm="HS256"
1009
+ )
1010
+
1011
+ # Try to use the expired token
1001
1012
  response = self.client.get(
1002
1013
  USER_URL + str(self.idx) + "/",
1003
1014
  follow_redirects=True,
1004
1015
  headers={
1005
1016
  "Content-Type": "application/json",
1006
- "Authorization": "Bearer " + token,
1017
+ "Authorization": f"Bearer {expired_token}",
1007
1018
  },
1008
1019
  )
1009
1020
 
1010
1021
  self.assertEqual(400, response.status_code)
1011
1022
  self.assertEqual(
1012
- "The token has expired, please login again", response.json["error"]
1023
+ "The token has expired, please login again",
1024
+ response.json["error"]
1013
1025
  )
1014
1026
 
1015
1027
  def test_bad_format_token(self):
@@ -1040,13 +1052,8 @@ class LoginTestCases:
1040
1052
  """
1041
1053
  Tests using an invalid token.
1042
1054
  """
1043
- token = (
1044
- "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2MTA1Mzk5NTMsImlhdCI6MTYxMDQ1MzU1Mywic3ViIjoxfQ"
1045
- ".g3Gh7k7twXZ4K2MnQpgpSr76Sl9VX6TkDWusX5YzImo"
1046
- )
1047
-
1055
+ # First log in to get a valid user
1048
1056
  payload = self.data
1049
-
1050
1057
  response = self.client.post(
1051
1058
  LOGIN_URL,
1052
1059
  data=json.dumps(payload),
@@ -1055,18 +1062,32 @@ class LoginTestCases:
1055
1062
  )
1056
1063
  self.idx = response.json["id"]
1057
1064
 
1065
+ # Generate an invalid token with wrong issuer
1066
+ invalid_payload = {
1067
+ "exp": datetime.utcnow() + timedelta(hours=1),
1068
+ "iat": datetime.utcnow(),
1069
+ "sub": self.idx,
1070
+ "iss": "invalid_issuer",
1071
+ }
1072
+ invalid_token = jwt.encode(
1073
+ invalid_payload,
1074
+ current_app.config["SECRET_TOKEN_KEY"],
1075
+ algorithm="HS256"
1076
+ )
1077
+
1078
+ # Try to use the invalid token
1058
1079
  response = self.client.get(
1059
1080
  USER_URL + str(self.idx) + "/",
1060
1081
  follow_redirects=True,
1061
1082
  headers={
1062
1083
  "Content-Type": "application/json",
1063
- "Authorization": "Bearer " + token,
1084
+ "Authorization": f"Bearer {invalid_token}",
1064
1085
  },
1065
1086
  )
1066
1087
 
1067
1088
  self.assertEqual(400, response.status_code)
1068
1089
  self.assertEqual(
1069
- "Invalid token, please try again with a new token",
1090
+ "Invalid token issuer. Token must be issued by a valid provider",
1070
1091
  response.json["error"],
1071
1092
  )
1072
1093
 
@@ -67,7 +67,7 @@ class TestActionsListEndpoint(CustomTestCase):
67
67
  follow_redirects=True,
68
68
  headers={
69
69
  "Content-Type": "application/json",
70
- "Authorization": "Bearer " + self.token,
70
+ "Authorization": f"Bearer {self.token}",
71
71
  },
72
72
  )
73
73
  self.assertEqual(200, response.status_code)
@@ -89,7 +89,7 @@ class TestActionsListEndpoint(CustomTestCase):
89
89
  follow_redirects=True,
90
90
  headers={
91
91
  "Content-Type": "application/json",
92
- "Authorization": "Bearer " + self.token,
92
+ "Authorization": f"Bearer {self.token}",
93
93
  },
94
94
  )
95
95
 
@@ -71,7 +71,7 @@ class TestApiViewListEndpoint(CustomTestCase):
71
71
  follow_redirects=True,
72
72
  headers={
73
73
  "Content-Type": "application/json",
74
- "Authorization": "Bearer " + self.token,
74
+ "Authorization": f"Bearer {self.token}",
75
75
  },
76
76
  )
77
77
  self.assertEqual(200, response.status_code)
@@ -97,7 +97,7 @@ class TestApiViewListEndpoint(CustomTestCase):
97
97
  follow_redirects=True,
98
98
  headers={
99
99
  "Content-Type": "application/json",
100
- "Authorization": "Bearer " + self.token,
100
+ "Authorization": f"Bearer {self.token}",
101
101
  },
102
102
  )
103
103
 
@@ -90,11 +90,10 @@ class TestCasesModels(CustomTestCase):
90
90
  self.payload = load_file(INSTANCE_PATH)
91
91
  self.payloads = [load_file(f) for f in INSTANCES_LIST]
92
92
  parents = [None, 1, 1, 3, 3, 3, 1, 7, 7, 9, 7]
93
- user = UserModel.get_one_user(self.user)
94
- data = {**self.payload, **dict(user_id=user.id)}
93
+ data = {**self.payload, **dict(user_id=self.user.id)}
95
94
  for parent in parents:
96
95
  if parent is not None:
97
- parent = CaseModel.get_one_object(user=user, idx=parent)
96
+ parent = CaseModel.get_one_object(user=self.user, idx=parent)
98
97
  node = CaseModel(data=data, parent=parent)
99
98
  node.save()
100
99
 
@@ -106,10 +105,9 @@ class TestCasesModels(CustomTestCase):
106
105
  - Correct path generation for cases
107
106
  - Proper parent-child relationships
108
107
  """
109
- user = UserModel.get_one_user(self.user)
110
- case = CaseModel.get_one_object(user=user, idx=6)
108
+ case = CaseModel.get_one_object(user=self.user, idx=6)
111
109
  self.assertEqual(case.path, "1/3/")
112
- case = CaseModel.get_one_object(user=user, idx=11)
110
+ case = CaseModel.get_one_object(user=self.user, idx=11)
113
111
  self.assertEqual(case.path, "1/7/")
114
112
 
115
113
  def test_move_case(self):
@@ -120,9 +118,8 @@ class TestCasesModels(CustomTestCase):
120
118
  - Cases can be moved to new parents
121
119
  - Path updates correctly after move
122
120
  """
123
- user = UserModel.get_one_user(self.user)
124
- case6 = CaseModel.get_one_object(user=user, idx=6)
125
- case11 = CaseModel.get_one_object(user=user, idx=11)
121
+ case6 = CaseModel.get_one_object(user=self.user, idx=6)
122
+ case11 = CaseModel.get_one_object(user=self.user, idx=11)
126
123
  case6.move_to(case11)
127
124
  self.assertEqual(case6.path, "1/7/11/")
128
125
 
@@ -135,11 +132,10 @@ class TestCasesModels(CustomTestCase):
135
132
  - Nested path updates
136
133
  - Path integrity after moves
137
134
  """
138
- user = UserModel.get_one_user(self.user)
139
- case3 = CaseModel.get_one_object(user=user, idx=3)
140
- case11 = CaseModel.get_one_object(user=user, idx=11)
141
- case9 = CaseModel.get_one_object(user=user, idx=9)
142
- case10 = CaseModel.get_one_object(user=user, idx=10)
135
+ case3 = CaseModel.get_one_object(user=self.user, idx=3)
136
+ case11 = CaseModel.get_one_object(user=self.user, idx=11)
137
+ case9 = CaseModel.get_one_object(user=self.user, idx=9)
138
+ case10 = CaseModel.get_one_object(user=self.user, idx=10)
143
139
  case3.move_to(case11)
144
140
  case9.move_to(case3)
145
141
  self.assertEqual(case10.path, "1/7/11/3/9/")
@@ -152,10 +148,9 @@ class TestCasesModels(CustomTestCase):
152
148
  - Case deletion removes the case
153
149
  - Child cases are properly handled
154
150
  """
155
- user = UserModel.get_one_user(self.user)
156
- case7 = CaseModel.get_one_object(user=user, idx=7)
151
+ case7 = CaseModel.get_one_object(user=self.user, idx=7)
157
152
  case7.delete()
158
- case11 = CaseModel.get_one_object(user=user, idx=11)
153
+ case11 = CaseModel.get_one_object(user=self.user, idx=11)
159
154
  self.assertIsNone(case11)
160
155
 
161
156
  def test_descendants(self):
@@ -166,8 +161,7 @@ class TestCasesModels(CustomTestCase):
166
161
  - Correct counting of descendants
167
162
  - Proper descendant relationships
168
163
  """
169
- user = UserModel.get_one_user(self.user)
170
- case7 = CaseModel.get_one_object(user=user, idx=7)
164
+ case7 = CaseModel.get_one_object(user=self.user, idx=7)
171
165
  self.assertEqual(len(case7.descendants), 4)
172
166
 
173
167
  def test_depth(self):
@@ -178,8 +172,7 @@ class TestCasesModels(CustomTestCase):
178
172
  - Correct depth calculation in case tree
179
173
  - Proper nesting level determination
180
174
  """
181
- user = UserModel.get_one_user(self.user)
182
- case10 = CaseModel.get_one_object(user=user, idx=10)
175
+ case10 = CaseModel.get_one_object(user=self.user, idx=10)
183
176
  self.assertEqual(case10.depth, 4)
184
177
 
185
178
 
@@ -234,12 +227,11 @@ class TestCasesFromInstanceExecutionEndpoint(CustomTestCase):
234
227
  "execution_id": execution_id,
235
228
  "schema": "solve_model_dag",
236
229
  }
237
- self.user_object = UserModel.get_one_user(self.user)
238
230
  self.instance = InstanceModel.get_one_object(
239
- user=self.user_object, idx=instance_id
231
+ user=self.user, idx=instance_id
240
232
  )
241
233
  self.execution = ExecutionModel.get_one_object(
242
- user=self.user_object, idx=execution_id
234
+ user=self.user, idx=execution_id
243
235
  )
244
236
 
245
237
  def test_new_case_execution(self):
@@ -266,7 +258,7 @@ class TestCasesFromInstanceExecutionEndpoint(CustomTestCase):
266
258
  self.payload["schema"] = self.instance.schema
267
259
  self.payload["solution"] = self.execution.data
268
260
  self.payload["solution_hash"] = self.execution.data_hash
269
- self.payload["user_id"] = self.user
261
+ self.payload["user_id"] = self.user.id
270
262
  self.payload["indicators"] = ""
271
263
 
272
264
  for key in self.response_items:
@@ -293,7 +285,7 @@ class TestCasesFromInstanceExecutionEndpoint(CustomTestCase):
293
285
  self.payload["data"] = self.instance.data
294
286
  self.payload["data_hash"] = self.instance.data_hash
295
287
  self.payload["schema"] = self.instance.schema
296
- self.payload["user_id"] = self.user
288
+ self.payload["user_id"] = self.user.id
297
289
  self.payload["solution"] = None
298
290
  self.payload["solution_hash"] = hash_json_256(None)
299
291
  self.payload["indicators"] = ""
@@ -488,10 +480,9 @@ class TestCaseCopyEndpoint(CustomTestCase):
488
480
  new_case = self.create_new_row(
489
481
  self.url + str(self.case_id) + "/copy/", self.model, {}, check_payload=False
490
482
  )
491
- user = UserModel.get_one_user(self.user)
492
483
 
493
- original_case = CaseModel.get_one_object(user=user, idx=self.case_id)
494
- new_case = CaseModel.get_one_object(user=user, idx=new_case["id"])
484
+ original_case = CaseModel.get_one_object(user=self.user, idx=self.case_id)
485
+ new_case = CaseModel.get_one_object(user=self.user, idx=new_case["id"])
495
486
 
496
487
  for key in self.copied_items:
497
488
  self.assertEqual(getattr(original_case, key), getattr(new_case, key))
@@ -372,7 +372,7 @@ class TestDeployedDAG(TestCase):
372
372
  follow_redirects=True,
373
373
  headers={
374
374
  "Content-Type": "application/json",
375
- "Authorization": "Bearer " + self.token,
375
+ "Authorization": f"Bearer {self.token}",
376
376
  },
377
377
  )
378
378
 
@@ -403,7 +403,7 @@ class TestDeployedDAG(TestCase):
403
403
  follow_redirects=True,
404
404
  headers={
405
405
  "Content-Type": "application/json",
406
- "Authorization": "Bearer " + self.token,
406
+ "Authorization": f"Bearer {self.token}",
407
407
  },
408
408
  )
409
409
 
@@ -428,7 +428,7 @@ class TestDeployedDAG(TestCase):
428
428
  follow_redirects=True,
429
429
  headers={
430
430
  "Content-Type": "application/json",
431
- "Authorization": "Bearer " + self.token,
431
+ "Authorization": f"Bearer {self.token}",
432
432
  },
433
433
  )
434
434
 
@@ -440,7 +440,7 @@ class TestDeployedDAG(TestCase):
440
440
  follow_redirects=True,
441
441
  headers={
442
442
  "Content-Type": "application/json",
443
- "Authorization": "Bearer " + self.token,
443
+ "Authorization": f"Bearer {self.token}",
444
444
  },
445
445
  )
446
446
 
@@ -455,7 +455,7 @@ class TestDeployedDAG(TestCase):
455
455
  follow_redirects=True,
456
456
  headers={
457
457
  "Content-Type": "application/json",
458
- "Authorization": "Bearer " + self.token,
458
+ "Authorization": f"Bearer {self.token}",
459
459
  },
460
460
  )
461
461
  self.assertEqual(response.status_code, 400)
@@ -67,7 +67,7 @@ class TestInstancesListEndpoint(BaseTestCases.ListFilters):
67
67
  follow_redirects=True,
68
68
  headers={
69
69
  "Content-Type": "application/json",
70
- "Authorization": "Bearer " + self.token,
70
+ "Authorization": f"Bearer {self.token}",
71
71
  },
72
72
  )
73
73
  self.assertEqual(400, response.status_code)
@@ -203,7 +203,7 @@ class TestInstancesDataEndpoint(TestInstancesDetailEndpointBase):
203
203
  idx = self.create_new_row(self.url, self.model, self.payload)
204
204
  headers = {
205
205
  "Content-Type": "application/json",
206
- "Authorization": "Bearer " + self.token,
206
+ "Authorization": f"Bearer {self.token}",
207
207
  "Accept-Encoding": "gzip",
208
208
  }
209
209
  response = self.client.get(INSTANCE_URL + idx + "/data/", headers=headers)
@@ -28,7 +28,7 @@ class TestInstances(CustomTestCase):
28
28
  follow_redirects=True,
29
29
  headers={
30
30
  "Content-Type": "multipart/form-data",
31
- "Authorization": "Bearer " + self.token,
31
+ "Authorization": f"Bearer {self.token}",
32
32
  },
33
33
  )
34
34
 
@@ -33,7 +33,7 @@ class TestLicensesListEndpoint(CustomTestCase):
33
33
  follow_redirects=True,
34
34
  headers={
35
35
  "Content-Type": "application/json",
36
- "Authorization": "Bearer " + self.token,
36
+ "Authorization": f"Bearer {self.token}",
37
37
  },
38
38
  )
39
39