cornflow 1.2.3a1__py3-none-any.whl → 1.2.3a3__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.
@@ -4,8 +4,14 @@ from importlib import import_module
4
4
  from cornflow.shared.const import (
5
5
  BASE_PERMISSION_ASSIGNATION,
6
6
  EXTRA_PERMISSION_ASSIGNATION,
7
+ ROLES_MAP,
8
+ GET_ACTION,
9
+ PATCH_ACTION,
10
+ POST_ACTION,
11
+ PUT_ACTION,
12
+ DELETE_ACTION,
7
13
  )
8
- from cornflow.models import ViewModel, PermissionViewRoleModel
14
+ from cornflow.models import ViewModel, PermissionViewRoleModel, RoleModel
9
15
  from cornflow.shared import db
10
16
  from flask import current_app
11
17
  from sqlalchemy.exc import DBAPIError, IntegrityError
@@ -14,6 +20,7 @@ from sqlalchemy.exc import DBAPIError, IntegrityError
14
20
  def register_base_permissions_command(external_app: str = None, verbose: bool = False):
15
21
  if external_app is None:
16
22
  from cornflow.endpoints import resources, alarms_resources
23
+
17
24
  resources_to_register = resources
18
25
  if current_app.config["ALARMS_ENDPOINTS"]:
19
26
  resources_to_register = resources + alarms_resources
@@ -31,6 +38,23 @@ def register_base_permissions_command(external_app: str = None, verbose: bool =
31
38
  (perm.role_id, perm.action_id, perm.api_view_id) for perm in permissions_in_db
32
39
  ]
33
40
  resources_names = [resource["endpoint"] for resource in resources_to_register]
41
+ roles_in_db = [role.id for role in RoleModel.get_all_objects()]
42
+ # Check which roles are not in ROLES_MAP
43
+ roles_not_in_map = [
44
+ role_id for role_id in roles_in_db if role_id not in ROLES_MAP.keys()
45
+ ]
46
+ complete_base_assignation = BASE_PERMISSION_ASSIGNATION.copy()
47
+ if len(roles_not_in_map) > 0:
48
+ # We add to the complete_base_assignation the roles that are not in ROLES_MAP
49
+ for role_id in roles_not_in_map:
50
+ for action in [
51
+ GET_ACTION,
52
+ PATCH_ACTION,
53
+ POST_ACTION,
54
+ PUT_ACTION,
55
+ DELETE_ACTION,
56
+ ]:
57
+ complete_base_assignation.append((role_id, action))
34
58
 
35
59
  # Create base permissions
36
60
  permissions_in_app = [
@@ -41,7 +65,7 @@ def register_base_permissions_command(external_app: str = None, verbose: bool =
41
65
  "api_view_id": views_in_db[view["endpoint"]],
42
66
  }
43
67
  )
44
- for role, action in BASE_PERMISSION_ASSIGNATION
68
+ for role, action in complete_base_assignation
45
69
  for view in resources_to_register
46
70
  if role in view["resource"].ROLES_WITH_ACCESS
47
71
  ] + [
cornflow/shared/const.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """
2
2
  In this file we import the values for different constants on cornflow server
3
3
  """
4
- CORNFLOW_VERSION = "1.2.3a1"
4
+ CORNFLOW_VERSION = "1.2.3a3"
5
5
  INTERNAL_TOKEN_ISSUER = "cornflow"
6
6
 
7
7
  # endpoints responses for health check
@@ -584,3 +584,106 @@ class TestCommands(TestCase):
584
584
  finally:
585
585
  # Restore original ROLES_WITH_ACCESS to avoid affecting other tests
586
586
  ExampleDataListEndpoint.ROLES_WITH_ACCESS = original_roles
587
+
588
+ def test_custom_role_permissions_are_preserved(self):
589
+ """
590
+ Test that custom roles (not in ROLES_MAP) are automatically detected
591
+ and assigned complete permissions during synchronization.
592
+
593
+ This test verifies that when permissions are synchronized:
594
+ 1. Custom roles in the database are automatically detected
595
+ 2. They are assigned all actions (GET, PATCH, POST, PUT, DELETE)
596
+ 3. These permissions are created for all endpoints where the role has access
597
+ """
598
+ # First, initialize the access system normally
599
+ self.runner.invoke(access_init)
600
+
601
+ # Get a view to work with
602
+ from cornflow.models import ViewModel, PermissionViewRoleModel, RoleModel
603
+
604
+ view = ViewModel.query.filter_by(name="example-data").first()
605
+ self.assertIsNotNone(view, "example-data view should exist")
606
+
607
+ # Create a custom role that is NOT in ROLES_MAP
608
+ custom_role_id = 999 # Use an ID that's not in ROLES_MAP
609
+ custom_role = RoleModel({"id": custom_role_id, "name": "custom_role"})
610
+ custom_role.save()
611
+
612
+ # Temporarily add the custom role to ExampleDataListEndpoint ROLES_WITH_ACCESS
613
+ from cornflow.endpoints.example_data import ExampleDataListEndpoint
614
+
615
+ original_roles = ExampleDataListEndpoint.ROLES_WITH_ACCESS.copy()
616
+ ExampleDataListEndpoint.ROLES_WITH_ACCESS = original_roles + [custom_role_id]
617
+
618
+ try:
619
+ # Count permissions before synchronization
620
+ perms_before = PermissionViewRoleModel.query.filter_by(
621
+ role_id=custom_role_id, api_view_id=view.id
622
+ ).count()
623
+ self.assertEqual(
624
+ 0, perms_before, "No custom permissions should exist initially"
625
+ )
626
+
627
+ # Run permission synchronization - this should auto-detect the custom role
628
+ self.runner.invoke(register_base_assignations, ["-v"])
629
+
630
+ # Verify that ALL actions have been assigned to the custom role
631
+ from cornflow.shared.const import (
632
+ GET_ACTION,
633
+ PATCH_ACTION,
634
+ POST_ACTION,
635
+ PUT_ACTION,
636
+ DELETE_ACTION,
637
+ )
638
+
639
+ expected_actions = [
640
+ GET_ACTION,
641
+ PATCH_ACTION,
642
+ POST_ACTION,
643
+ PUT_ACTION,
644
+ DELETE_ACTION,
645
+ ]
646
+
647
+ for action in expected_actions:
648
+ permission = PermissionViewRoleModel.query.filter_by(
649
+ role_id=custom_role_id, action_id=action, api_view_id=view.id
650
+ ).first()
651
+ self.assertIsNotNone(
652
+ permission,
653
+ f"Custom role should have permission for action {action}. "
654
+ f"The system should auto-detect custom roles and assign complete permissions.",
655
+ )
656
+
657
+ # Verify total count of permissions for custom role
658
+ total_perms = PermissionViewRoleModel.query.filter_by(
659
+ role_id=custom_role_id, api_view_id=view.id
660
+ ).count()
661
+ self.assertEqual(
662
+ len(expected_actions),
663
+ total_perms,
664
+ f"Custom role should have {len(expected_actions)} permissions (all actions)",
665
+ )
666
+
667
+ # Test that running sync again doesn't duplicate permissions
668
+ self.runner.invoke(register_base_assignations, ["-v"])
669
+
670
+ total_perms_after_second_sync = PermissionViewRoleModel.query.filter_by(
671
+ role_id=custom_role_id, api_view_id=view.id
672
+ ).count()
673
+ self.assertEqual(
674
+ len(expected_actions),
675
+ total_perms_after_second_sync,
676
+ "Permissions should not be duplicated on subsequent syncs",
677
+ )
678
+
679
+ finally:
680
+ # Clean up
681
+ # Delete permissions first (due to foreign key constraints)
682
+ permissions_to_delete = PermissionViewRoleModel.query.filter_by(
683
+ role_id=custom_role_id
684
+ ).all()
685
+ for perm in permissions_to_delete:
686
+ perm.delete()
687
+ custom_role.delete()
688
+ # Restore original ROLES_WITH_ACCESS
689
+ ExampleDataListEndpoint.ROLES_WITH_ACCESS = original_roles
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cornflow
3
- Version: 1.2.3a1
3
+ Version: 1.2.3a3
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
@@ -32,7 +32,7 @@ cornflow/commands/access.py,sha256=NTZJFF9la8TDuMcD_ISQtJTj-wtM2p1dddokQJHtkj0,7
32
32
  cornflow/commands/actions.py,sha256=4AwgAmyI6VeaugkISvTlNGrIzMMU_-ZB3MhwDD_CIEA,1544
33
33
  cornflow/commands/cleanup.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
34
  cornflow/commands/dag.py,sha256=AtagFGnB_ucfO0qUawDgd4iRoBCVc-RiOs08DqXSwXM,3786
35
- cornflow/commands/permissions.py,sha256=iNa8I1jsuBBdA1XqoNz_tbm7YAb1-Oc6nitepYLNPRg,6621
35
+ cornflow/commands/permissions.py,sha256=f6UMB1dj4KBXhrCXalcOdiQlDUldbbmslI6yTrK-_VM,7439
36
36
  cornflow/commands/roles.py,sha256=Oux-UkswkQ74zqaMEJYIEsZpQZGBcGaSahVzx9feAHU,1516
37
37
  cornflow/commands/schemas.py,sha256=40dZSJ2nEqBa7Crb6DbFmnclT5e8ljAIjscOgHr9lhk,1970
38
38
  cornflow/commands/users.py,sha256=2YTbNYY5kZL6ujxGP4fyYgqtv5uuVGdkLR31n7OFFaE,2477
@@ -119,7 +119,7 @@ cornflow/schemas/user_role.py,sha256=e5y6RgdZZtLqD-h2B3sa5WokI5-pT78tWw85IG34I74
119
119
  cornflow/schemas/view.py,sha256=ctq9Y1TmjrWdyOqgDYeEx7qbbuNLKfSiNOlFTlXmpaw,429
120
120
  cornflow/shared/__init__.py,sha256=1ahcBwWOsSjGI4FEm77JBQjitBdBszOncKcEMjzwGYE,29
121
121
  cornflow/shared/compress.py,sha256=pohQaGs1xbH8CN6URIH6BAHA--pFq7Hmjz8oI3c3B5c,1347
122
- cornflow/shared/const.py,sha256=nepftaeiyR-pmAXnHwws5MqCARoTqWPG_-TtCw3KufM,3555
122
+ cornflow/shared/const.py,sha256=06VjXJ1MxSc7MrmWMZD95N0VCLjiNqXIE3o1a-xd9n0,3555
123
123
  cornflow/shared/email.py,sha256=QNDDMv86LZObkevSCyUbLQeR2UD3zWScPIr82NDzYHQ,3437
124
124
  cornflow/shared/exceptions.py,sha256=E82488IiwTXCv8iwrnGvkTonhJcwbeE5ARO4Zsmhl2c,6966
125
125
  cornflow/shared/licenses.py,sha256=Lc71Jw2NxVTFWtoXdQ9wJX_o3BDfYg1xVoehDXvnCkQ,1328
@@ -148,7 +148,7 @@ cornflow/tests/unit/test_apiview.py,sha256=03M1GsQRVK7zqmslhOJXr4lLDLY2gMAgg86nk
148
148
  cornflow/tests/unit/test_application.py,sha256=ZVmTQDUOkPRxHqt6mWU9G_lQ3jJNMJR0cx7IkLMFGrU,1715
149
149
  cornflow/tests/unit/test_cases.py,sha256=Ez9dxlZL-SUf9DW9b_A_qPowHqUZ-TA73DMOzeBeLIU,37718
150
150
  cornflow/tests/unit/test_cli.py,sha256=E2w-Lzgx_k__0mYwlbg2z80_z9nwPZKI0CbgyGmpQRY,18775
151
- cornflow/tests/unit/test_commands.py,sha256=ZajFnltdPlw4o6B5Cf7ZSYhZb6fM9z6tARhFOhKMug8,18695
151
+ cornflow/tests/unit/test_commands.py,sha256=JaV_RX9VNaYj_3_QKHZmxct5irfA9GnQLKmpO002P1Y,23002
152
152
  cornflow/tests/unit/test_dags.py,sha256=XsOi5bBJQdQz3DmYAVJf1myoAsRyBBdmku-xBr0Bku0,13386
153
153
  cornflow/tests/unit/test_data_checks.py,sha256=6s50d1iuRTUcAYn14oEcRS39ZZ6E9ussU4YpkpYhtC4,8612
154
154
  cornflow/tests/unit/test_example_data.py,sha256=D-Tgnqw7NZlnBXaDcUU0reNhAca5JlJP2Sdn3KdS4Sw,4127
@@ -169,8 +169,8 @@ cornflow/tests/unit/test_tables.py,sha256=SW_K8LRLwR1nB0uH8CPQCjeN8Gei-TasAgkOin
169
169
  cornflow/tests/unit/test_token.py,sha256=PZ11b46UCQpCESsRiAPhpgWkGAsAwKCVNxVQai_kxXM,4199
170
170
  cornflow/tests/unit/test_users.py,sha256=N5tcF5nSncD0F_ZlBxGuS87p6kNS4hUzRLr3_AcnK-o,22802
171
171
  cornflow/tests/unit/tools.py,sha256=ag3sWv2WLi498R1GL5AOUnXqSsszD3UugzLZLC5NqAw,585
172
- cornflow-1.2.3a1.dist-info/METADATA,sha256=zrEdE7w5XdzXENv06QWMxLNDmS0Zfcvpq5zsaJtuaGs,9529
173
- cornflow-1.2.3a1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
174
- cornflow-1.2.3a1.dist-info/entry_points.txt,sha256=q9cPKAFBsmHkERCqQ2JcOTM-tVBLHTl-DGxwCXowAWM,46
175
- cornflow-1.2.3a1.dist-info/top_level.txt,sha256=Qj9kLFJW1PLb-ZV2s_aCkQ-Wi5W6KC6fFR-LTBrx-rU,24
176
- cornflow-1.2.3a1.dist-info/RECORD,,
172
+ cornflow-1.2.3a3.dist-info/METADATA,sha256=lwa0AelaIIcefbhB5bES98yk1h4R_r5p17FzhqvIpdE,9529
173
+ cornflow-1.2.3a3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
174
+ cornflow-1.2.3a3.dist-info/entry_points.txt,sha256=q9cPKAFBsmHkERCqQ2JcOTM-tVBLHTl-DGxwCXowAWM,46
175
+ cornflow-1.2.3a3.dist-info/top_level.txt,sha256=Qj9kLFJW1PLb-ZV2s_aCkQ-Wi5W6KC6fFR-LTBrx-rU,24
176
+ cornflow-1.2.3a3.dist-info/RECORD,,