cornflow 1.1.5__tar.gz → 1.2.0a1__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.
Files changed (185) hide show
  1. {cornflow-1.1.5/cornflow.egg-info → cornflow-1.2.0a1}/PKG-INFO +3 -2
  2. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/app.py +8 -3
  3. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/service.py +14 -6
  4. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/config.py +9 -10
  5. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/login.py +54 -57
  6. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/user.py +3 -17
  7. cornflow-1.2.0a1/cornflow/shared/authentication/auth.py +478 -0
  8. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/const.py +3 -14
  9. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/custom_test_case.py +42 -21
  10. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_actions.py +2 -2
  11. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_apiview.py +2 -2
  12. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_cases.py +20 -29
  13. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_dags.py +5 -5
  14. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_instances.py +2 -2
  15. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_instances_file.py +1 -1
  16. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_licenses.py +1 -1
  17. cornflow-1.2.0a1/cornflow/tests/unit/test_log_in.py +531 -0
  18. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_permissions.py +8 -8
  19. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_roles.py +10 -10
  20. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_tables.py +7 -7
  21. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_token.py +12 -6
  22. {cornflow-1.1.5 → cornflow-1.2.0a1/cornflow.egg-info}/PKG-INFO +3 -2
  23. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow.egg-info/requires.txt +2 -1
  24. {cornflow-1.1.5 → cornflow-1.2.0a1}/requirements.txt +3 -2
  25. {cornflow-1.1.5 → cornflow-1.2.0a1}/setup.py +1 -1
  26. cornflow-1.1.5/cornflow/shared/authentication/auth.py +0 -521
  27. cornflow-1.1.5/cornflow/tests/unit/test_log_in.py +0 -511
  28. {cornflow-1.1.5 → cornflow-1.2.0a1}/MANIFEST.in +0 -0
  29. {cornflow-1.1.5 → cornflow-1.2.0a1}/README.rst +0 -0
  30. {cornflow-1.1.5 → cornflow-1.2.0a1}/airflow_config/__init__.py +0 -0
  31. {cornflow-1.1.5 → cornflow-1.2.0a1}/airflow_config/airflow_local_settings.py +0 -0
  32. {cornflow-1.1.5 → cornflow-1.2.0a1}/airflow_config/plugins/XCom/__init__.py +0 -0
  33. {cornflow-1.1.5 → cornflow-1.2.0a1}/airflow_config/plugins/XCom/gce_xcom_backend.py +0 -0
  34. {cornflow-1.1.5 → cornflow-1.2.0a1}/airflow_config/plugins/__init__.py +0 -0
  35. {cornflow-1.1.5 → cornflow-1.2.0a1}/airflow_config/webserver_ldap.py +0 -0
  36. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/__init__.py +0 -0
  37. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/__init__.py +0 -0
  38. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/actions.py +0 -0
  39. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/arguments.py +0 -0
  40. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/config.py +0 -0
  41. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/migrations.py +0 -0
  42. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/permissions.py +0 -0
  43. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/roles.py +0 -0
  44. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/schemas.py +0 -0
  45. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/tools/__init__.py +0 -0
  46. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/tools/api_generator.py +0 -0
  47. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/tools/endpoint_tools.py +0 -0
  48. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/tools/models_tools.py +0 -0
  49. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/tools/schema_generator.py +0 -0
  50. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/tools/schemas_tools.py +0 -0
  51. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/tools/tools.py +0 -0
  52. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/users.py +0 -0
  53. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/utils.py +0 -0
  54. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/views.py +0 -0
  55. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/commands/__init__.py +0 -0
  56. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/commands/access.py +0 -0
  57. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/commands/actions.py +0 -0
  58. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/commands/cleanup.py +0 -0
  59. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/commands/dag.py +0 -0
  60. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/commands/permissions.py +0 -0
  61. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/commands/roles.py +0 -0
  62. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/commands/schemas.py +0 -0
  63. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/commands/users.py +0 -0
  64. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/commands/views.py +0 -0
  65. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/__init__.py +0 -0
  66. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/action.py +0 -0
  67. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/alarms.py +0 -0
  68. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/apiview.py +0 -0
  69. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/case.py +0 -0
  70. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/dag.py +0 -0
  71. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/data_check.py +0 -0
  72. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/example_data.py +0 -0
  73. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/execution.py +0 -0
  74. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/health.py +0 -0
  75. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/instance.py +0 -0
  76. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/licenses.py +0 -0
  77. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/main_alarms.py +0 -0
  78. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/meta_resource.py +0 -0
  79. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/permission.py +0 -0
  80. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/roles.py +0 -0
  81. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/schemas.py +0 -0
  82. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/signup.py +0 -0
  83. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/tables.py +0 -0
  84. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/token.py +0 -0
  85. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/user.py +0 -0
  86. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/user_role.py +0 -0
  87. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/gunicorn.py +0 -0
  88. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/README +0 -0
  89. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/alembic.ini +0 -0
  90. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/env.py +0 -0
  91. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/script.py.mako +0 -0
  92. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/00757b557b02_.py +0 -0
  93. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/1af47a419bbd_.py +0 -0
  94. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/4aac5e0c6e66_.py +0 -0
  95. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/7c3ea5ab5501_.py +0 -0
  96. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/991b98e24225_.py +0 -0
  97. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/a472b5ad50b7_.py +0 -0
  98. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/c2db9409cb5f_.py +0 -0
  99. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/c8a6c762e818_.py +0 -0
  100. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/ca449af8034c_.py +0 -0
  101. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/d0e0700dcd8e_.py +0 -0
  102. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/d1b5be1f0549_.py +0 -0
  103. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/e1a50dae1ac9_.py +0 -0
  104. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/e937a5234ce4_.py +0 -0
  105. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/ebdd955fcc5e_.py +0 -0
  106. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/f3bee20314a2_.py +0 -0
  107. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/__init__.py +0 -0
  108. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/action.py +0 -0
  109. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/alarms.py +0 -0
  110. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/base_data_model.py +0 -0
  111. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/case.py +0 -0
  112. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/dag.py +0 -0
  113. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/dag_permissions.py +0 -0
  114. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/execution.py +0 -0
  115. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/instance.py +0 -0
  116. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/main_alarms.py +0 -0
  117. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/meta_models.py +0 -0
  118. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/permissions.py +0 -0
  119. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/role.py +0 -0
  120. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/user.py +0 -0
  121. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/user_role.py +0 -0
  122. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/view.py +0 -0
  123. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/__init__.py +0 -0
  124. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/action.py +0 -0
  125. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/alarms.py +0 -0
  126. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/case.py +0 -0
  127. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/common.py +0 -0
  128. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/dag.py +0 -0
  129. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/example_data.py +0 -0
  130. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/execution.py +0 -0
  131. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/health.py +0 -0
  132. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/instance.py +0 -0
  133. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/main_alarms.py +0 -0
  134. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/model_json.py +0 -0
  135. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/patch.py +0 -0
  136. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/permissions.py +0 -0
  137. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/query.py +0 -0
  138. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/role.py +0 -0
  139. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/schemas.py +0 -0
  140. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/solution_log.py +0 -0
  141. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/tables.py +0 -0
  142. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/user_role.py +0 -0
  143. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/view.py +0 -0
  144. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/__init__.py +0 -0
  145. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/authentication/__init__.py +0 -0
  146. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/authentication/decorators.py +0 -0
  147. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/authentication/ldap.py +0 -0
  148. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/compress.py +0 -0
  149. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/email.py +0 -0
  150. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/exceptions.py +0 -0
  151. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/licenses.py +0 -0
  152. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/log_config.py +0 -0
  153. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/query_tools.py +0 -0
  154. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/utils.py +0 -0
  155. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/utils_tables.py +0 -0
  156. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/validators.py +0 -0
  157. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/__init__.py +0 -0
  158. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/const.py +0 -0
  159. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/custom_liveServer.py +0 -0
  160. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/integration/__init__.py +0 -0
  161. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/integration/test_commands.py +0 -0
  162. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/integration/test_cornflowclient.py +0 -0
  163. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/ldap/__init__.py +0 -0
  164. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/ldap/test_ldap_authentication.py +0 -0
  165. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/__init__.py +0 -0
  166. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_alarms.py +0 -0
  167. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_application.py +0 -0
  168. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_cli.py +0 -0
  169. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_commands.py +0 -0
  170. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_data_checks.py +0 -0
  171. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_example_data.py +0 -0
  172. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_executions.py +0 -0
  173. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_generate_from_schema.py +0 -0
  174. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_health.py +0 -0
  175. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_main_alarms.py +0 -0
  176. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_schema_from_models.py +0 -0
  177. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_schemas.py +0 -0
  178. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_sign_up.py +0 -0
  179. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_users.py +0 -0
  180. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/tools.py +0 -0
  181. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow.egg-info/SOURCES.txt +0 -0
  182. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow.egg-info/dependency_links.txt +0 -0
  183. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow.egg-info/entry_points.txt +0 -0
  184. {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow.egg-info/top_level.txt +0 -0
  185. {cornflow-1.1.5 → cornflow-1.2.0a1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: cornflow
3
- Version: 1.1.5
3
+ Version: 1.2.0a1
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
@@ -13,7 +13,7 @@ Requires-Python: >=3.9
13
13
  Requires-Dist: alembic==1.9.2
14
14
  Requires-Dist: apispec<=6.3.0
15
15
  Requires-Dist: click<=8.1.7
16
- Requires-Dist: cornflow-client>=1.1.0
16
+ Requires-Dist: cornflow-client==1.2.0a1
17
17
  Requires-Dist: cryptography<=42.0.5
18
18
  Requires-Dist: disposable-email-domains>=0.0.86
19
19
  Requires-Dist: Flask==2.3.2
@@ -40,6 +40,7 @@ Requires-Dist: requests<=2.32.3
40
40
  Requires-Dist: SQLAlchemy==1.3.21
41
41
  Requires-Dist: webargs<=8.3.0
42
42
  Requires-Dist: Werkzeug==3.0.6
43
+ Requires-Dist: cachetools==5.3.3
43
44
  Dynamic: author
44
45
  Dynamic: author-email
45
46
  Dynamic: classifier
@@ -37,7 +37,7 @@ from cornflow.endpoints.signup import SignUpEndpoint
37
37
  from cornflow.shared import db, bcrypt
38
38
  from cornflow.shared.compress import init_compress
39
39
  from cornflow.shared.const import AUTH_DB, AUTH_LDAP, AUTH_OID
40
- from cornflow.shared.exceptions import initialize_errorhandlers
40
+ from cornflow.shared.exceptions import initialize_errorhandlers, ConfigurationError
41
41
  from cornflow.shared.log_config import log_config
42
42
 
43
43
 
@@ -62,11 +62,11 @@ def create_app(env_name="development", dataconn=None):
62
62
  CORS(app)
63
63
  bcrypt.init_app(app)
64
64
  db.init_app(app)
65
- migrate = Migrate(app=app, db=db)
65
+ Migrate(app=app, db=db)
66
66
 
67
67
  if "sqlite" in app.config["SQLALCHEMY_DATABASE_URI"]:
68
68
 
69
- def _fk_pragma_on_connect(dbapi_con, con_record):
69
+ def _fk_pragma_on_connect(dbapi_con, _con_record):
70
70
  dbapi_con.execute("pragma foreign_keys=ON")
71
71
 
72
72
  with app.app_context():
@@ -100,6 +100,11 @@ def create_app(env_name="development", dataconn=None):
100
100
  api.add_resource(LoginEndpoint, "/login/", endpoint="login")
101
101
  elif auth_type == AUTH_OID:
102
102
  api.add_resource(LoginOpenAuthEndpoint, "/login/", endpoint="login")
103
+ else:
104
+ raise ConfigurationError(
105
+ error="Invalid authentication type",
106
+ log_txt="Error while configuring authentication. The authentication type is not valid."
107
+ )
103
108
 
104
109
  initialize_errorhandlers(app)
105
110
  init_compress(app)
@@ -16,7 +16,14 @@ from cornflow.commands import (
16
16
  update_schemas_command,
17
17
  update_dag_registry_command,
18
18
  )
19
- from cornflow.shared.const import AUTH_DB, ADMIN_ROLE, SERVICE_ROLE
19
+ from cornflow.shared.const import (
20
+ AUTH_DB,
21
+ AUTH_LDAP,
22
+ AUTH_OID,
23
+ ADMIN_ROLE,
24
+ SERVICE_ROLE,
25
+ PLANNER_ROLE,
26
+ )
20
27
  from cornflow.shared import db
21
28
  from cryptography.fernet import Fernet
22
29
  from flask_migrate import Migrate, upgrade
@@ -83,11 +90,11 @@ def init_cornflow_service():
83
90
  os.environ["SIGNUP_ACTIVATED"] = str(signup_activated)
84
91
  user_access_all_objects = os.getenv("USER_ACCESS_ALL_OBJECTS", 0)
85
92
  os.environ["USER_ACCESS_ALL_OBJECTS"] = str(user_access_all_objects)
86
- default_role = os.getenv("DEFAULT_ROLE", 2)
93
+ default_role = int(os.getenv("DEFAULT_ROLE", PLANNER_ROLE))
87
94
  os.environ["DEFAULT_ROLE"] = str(default_role)
88
95
 
89
96
  # Check LDAP parameters for active directory and show message
90
- if os.getenv("AUTH_TYPE") == 2:
97
+ if os.getenv("AUTH_TYPE") == AUTH_LDAP:
91
98
  print(
92
99
  "WARNING: Cornflow will be deployed with LDAP Authorization. Please review your ldap auth configuration."
93
100
  )
@@ -129,10 +136,11 @@ def init_cornflow_service():
129
136
  app = create_app(environment, cornflow_db_conn)
130
137
  with app.app_context():
131
138
  path = f"{os.path.dirname(cornflow.__file__)}/migrations"
132
- migrate = Migrate(app=app, db=db, directory=path)
139
+ Migrate(app=app, db=db, directory=path)
133
140
  upgrade()
134
141
  access_init_command(verbose=False)
135
- if auth == 1 or auth == 0:
142
+ if auth == AUTH_DB or auth == AUTH_OID:
143
+ # create cornflow admin user
136
144
  create_user_with_role(
137
145
  cornflow_admin_user,
138
146
  cornflow_admin_email,
@@ -188,7 +196,7 @@ def init_cornflow_service():
188
196
  migrate = Migrate(app=app, db=db, directory=path)
189
197
  upgrade()
190
198
  access_init_command(verbose=False)
191
- if auth == 1 or auth == 0:
199
+ if auth == AUTH_DB or auth == AUTH_OID:
192
200
  # create cornflow admin user
193
201
  create_user_with_role(
194
202
  cornflow_admin_user,
@@ -1,5 +1,5 @@
1
1
  import os
2
- from .shared.const import AUTH_DB, PLANNER_ROLE
2
+ from .shared.const import AUTH_DB, PLANNER_ROLE, AUTH_OID
3
3
  from apispec import APISpec
4
4
  from apispec.ext.marshmallow import MarshmallowPlugin
5
5
 
@@ -28,7 +28,7 @@ class DefaultConfig(object):
28
28
  SIGNUP_ACTIVATED = int(os.getenv("SIGNUP_ACTIVATED", 1))
29
29
  CORNFLOW_SERVICE_USER = os.getenv("CORNFLOW_SERVICE_USER", "service_user")
30
30
 
31
- # If service user is allow to log with username and password
31
+ # If service user is allowed to log with username and password
32
32
  SERVICE_USER_ALLOW_PASSWORD_LOGIN = int(
33
33
  os.getenv("SERVICE_USER_ALLOW_PASSWORD_LOGIN", 1)
34
34
  )
@@ -59,11 +59,9 @@ class DefaultConfig(object):
59
59
  LDAP_PROTOCOL_VERSION = int(os.getenv("LDAP_PROTOCOL_VERSION", 3))
60
60
  LDAP_USE_TLS = os.getenv("LDAP_USE_TLS", "False")
61
61
 
62
- # OpenID login -> Default Azure
63
- OID_PROVIDER = os.getenv("OID_PROVIDER", 0)
64
- OID_CLIENT_ID = os.getenv("OID_CLIENT_ID")
65
- OID_TENANT_ID = os.getenv("OID_TENANT_ID")
66
- OID_ISSUER = os.getenv("OID_ISSUER")
62
+ # OpenID Connect configuration
63
+ OID_PROVIDER = os.getenv("OID_PROVIDER")
64
+ OID_EXPECTED_AUDIENCE = os.getenv("OID_EXPECTED_AUDIENCE")
67
65
 
68
66
  # APISPEC:
69
67
  APISPEC_SPEC = APISpec(
@@ -127,8 +125,9 @@ class TestingOpenAuth(Testing):
127
125
  """
128
126
  Configuration class for testing some edge cases with Open Auth login
129
127
  """
130
-
131
- AUTH_TYPE = 0
128
+ AUTH_TYPE = AUTH_OID
129
+ OID_PROVIDER = "https://test-provider.example.com"
130
+ OID_EXPECTED_AUDIENCE = "test-audience-id"
132
131
 
133
132
 
134
133
  class TestingApplicationRoot(Testing):
@@ -158,5 +157,5 @@ app_config = {
158
157
  "testing": Testing,
159
158
  "production": Production,
160
159
  "testing-oauth": TestingOpenAuth,
161
- "testing-root": TestingApplicationRoot,
160
+ "testing-root": TestingApplicationRoot
162
161
  }
@@ -1,9 +1,9 @@
1
1
  """
2
- External endpoint for the user to login to the cornflow webserver
2
+ External endpoint for the user to log in to the cornflow webserver
3
3
  """
4
4
 
5
5
  # Partial imports
6
- from flask import current_app
6
+ from flask import current_app, request
7
7
  from flask_apispec import use_kwargs, doc
8
8
  from sqlalchemy.exc import IntegrityError, DBAPIError
9
9
  from datetime import datetime, timedelta
@@ -18,15 +18,11 @@ from cornflow.shared.const import (
18
18
  AUTH_DB,
19
19
  AUTH_LDAP,
20
20
  AUTH_OID,
21
- OID_AZURE,
22
- OID_GOOGLE,
23
- OID_NONE,
24
21
  )
25
22
  from cornflow.shared.exceptions import (
26
23
  ConfigurationError,
27
24
  InvalidCredentials,
28
25
  InvalidUsage,
29
- EndpointNotImplemented,
30
26
  )
31
27
 
32
28
 
@@ -45,7 +41,7 @@ class LoginBaseEndpoint(BaseMetaResource):
45
41
  This method is in charge of performing the log in of the user
46
42
 
47
43
  :param kwargs: keyword arguments passed for the login, these can be username, password or a token
48
- :return: the response of the login or it raises an error. The correct response is a dict
44
+ :return: the response of the login, or it raises an error. The correct response is a dict
49
45
  with the newly issued token and the user id, and a status code of 200
50
46
  :rtype: dict
51
47
  """
@@ -55,10 +51,24 @@ class LoginBaseEndpoint(BaseMetaResource):
55
51
  if auth_type == AUTH_DB:
56
52
  user = self.auth_db_authenticate(**kwargs)
57
53
  response.update({"change_password": check_last_password_change(user)})
54
+ current_app.logger.info(f"User {user.id} logged in successfully using database authentication")
58
55
  elif auth_type == AUTH_LDAP:
59
56
  user = self.auth_ldap_authenticate(**kwargs)
57
+ current_app.logger.info(f"User {user.id} logged in successfully using LDAP authentication")
60
58
  elif auth_type == AUTH_OID:
61
- user = self.auth_oid_authenticate(**kwargs)
59
+ if (kwargs.get('username') and kwargs.get('password')):
60
+ if not current_app.config.get("SERVICE_USER_ALLOW_PASSWORD_LOGIN", 0):
61
+ raise InvalidUsage("Must provide a token in Authorization header. Cannot log in with username and password", 400)
62
+ user = self.auth_oid_authenticate(username=kwargs['username'], password=kwargs['password'])
63
+ current_app.logger.info(f"Service user {user.id} logged in successfully using password")
64
+ token = self.auth_class.generate_token(user.id)
65
+ else:
66
+ token = self.auth_class().get_token_from_header(request.headers)
67
+ user = self.auth_oid_authenticate(token=token)
68
+ current_app.logger.info(f"User {user.id} logged in successfully using OpenID authentication")
69
+
70
+ response.update({"token": token, "id": user.id})
71
+ return response, 200
62
72
  else:
63
73
  raise ConfigurationError()
64
74
 
@@ -77,7 +87,7 @@ class LoginBaseEndpoint(BaseMetaResource):
77
87
 
78
88
  :param str username: the username of the user to log in
79
89
  :param str password: the password of the user to log in
80
- :return: the user object or it raises an error if it has not been possible to log in
90
+ :return: the user object, or it raises an error if it has not been possible to log in
81
91
  :rtype: :class:`UserModel`
82
92
  """
83
93
  user = self.data_model.get_one_object(username=username)
@@ -96,7 +106,7 @@ class LoginBaseEndpoint(BaseMetaResource):
96
106
 
97
107
  :param str username: the username of the user to log in
98
108
  :param str password: the password of the user to log in
99
- :return: the user object or it raises an error if it has not been possible to log in
109
+ :return: the user object, or it raises an error if it has not been possible to log in
100
110
  :rtype: :class:`UserModel`
101
111
  """
102
112
  ldap_obj = self.ldap_class(current_app.config)
@@ -137,61 +147,45 @@ class LoginBaseEndpoint(BaseMetaResource):
137
147
 
138
148
  return user
139
149
 
140
- def auth_oid_authenticate(
141
- self, token: str = None, username: str = None, password: str = None
142
- ):
150
+ def auth_oid_authenticate(self, token: str = None, username: str = None, password: str = None):
143
151
  """
144
- Method in charge of performing the log in with the token issued by an Open ID provider.
145
- It has an exception and thus accepts username and password for service users if needed.
152
+ Method in charge of performing the authentication using OpenID Connect tokens.
153
+ Supports any OIDC provider configured via provider_url.
146
154
 
147
- :param str token: the token that the user has obtained from the Open ID provider
148
- :param str username: the username of the user to log in
149
- :param str password: the password of the user to log in
150
- :return: the user object or it raises an error if it has not been possible to log in
155
+ :param str token: the JWT token from the OIDC provider
156
+ :param str username: username for service users
157
+ :param str password: password for service users
158
+ :return: the user object, or it raises an error if it has not been possible to log in
151
159
  :rtype: :class:`UserModel`
152
160
  """
153
-
161
+ print("[auth_oid_authenticate] Starting OpenID authentication")
154
162
  if token:
155
-
156
- oid_provider = int(current_app.config["OID_PROVIDER"])
157
-
158
- client_id = current_app.config["OID_CLIENT_ID"]
159
- tenant_id = current_app.config["OID_TENANT_ID"]
160
- issuer = current_app.config["OID_ISSUER"]
161
-
162
- if client_id is None or tenant_id is None or issuer is None:
163
- raise ConfigurationError("The OID provider configuration is not valid")
164
-
165
- if oid_provider == OID_AZURE:
166
- decoded_token = self.auth_class().validate_oid_token(
167
- token, client_id, tenant_id, issuer, oid_provider
168
- )
169
-
170
- elif oid_provider == OID_GOOGLE:
171
- raise EndpointNotImplemented(
172
- "The selected OID provider is not implemented"
173
- )
174
- elif oid_provider == OID_NONE:
175
- raise EndpointNotImplemented(
176
- "The OID provider configuration is not valid"
177
- )
178
- else:
179
- raise EndpointNotImplemented(
180
- "The OID provider configuration is not valid"
163
+ print("[auth_oid_authenticate] Authenticating with token")
164
+ decoded_token = self.auth_class().decode_token(token)
165
+ print(f"[auth_oid_authenticate] Token decoded successfully: {decoded_token}")
166
+
167
+ username = decoded_token.get('username')
168
+ if not username:
169
+ print("[auth_oid_authenticate] Missing username in token claims")
170
+ raise InvalidCredentials(
171
+ "Invalid token: missing username claim",
172
+ log_txt="Token validation failed: missing username claim",
173
+ status_code=400
181
174
  )
182
175
 
183
- username = decoded_token["preferred_username"]
184
- email = decoded_token.get("email", f"{username}@test.org")
185
- first_name = decoded_token.get("given_name", "")
186
- last_name = decoded_token.get("family_name", "")
187
-
176
+ print(f"[auth_oid_authenticate] Looking up user: {username}")
188
177
  user = self.data_model.get_one_object(username=username)
189
178
 
190
179
  if not user:
180
+ print(f"[auth_oid_authenticate] Creating new user: {username}")
191
181
  current_app.logger.info(
192
182
  f"OpenID user {username} does not exist and is created"
193
183
  )
194
184
 
185
+ email = decoded_token.get('email', f"{username}@cornflow.org")
186
+ first_name = decoded_token.get('given_name', '')
187
+ last_name = decoded_token.get('family_name', '')
188
+
195
189
  data = {
196
190
  "username": username,
197
191
  "email": email,
@@ -208,27 +202,30 @@ class LoginBaseEndpoint(BaseMetaResource):
208
202
  "role_id": int(current_app.config["DEFAULT_ROLE"]),
209
203
  }
210
204
  )
211
-
212
205
  user_role.save()
213
206
 
214
207
  return user
208
+
215
209
  elif (
216
210
  username
217
211
  and password
218
- and current_app.config["SERVICE_USER_ALLOW_PASSWORD_LOGIN"] == 1
219
212
  ):
220
-
221
213
  user = self.auth_db_authenticate(username, password)
222
-
223
214
  if user.is_service_user():
224
215
  return user
225
- else:
226
- raise InvalidUsage("Invalid request")
216
+ raise InvalidUsage("Invalid request")
227
217
  else:
228
218
  raise InvalidUsage("Invalid request")
229
219
 
230
220
 
231
221
  def check_last_password_change(user):
222
+ """
223
+ Check if the user needs to change their password based on the password rotation time.
224
+
225
+ :param user: The user object to check
226
+ :return: True if password needs to be changed, False otherwise
227
+ :rtype: bool
228
+ """
232
229
  if user.pwd_last_change:
233
230
  if (
234
231
  user.pwd_last_change
@@ -67,24 +67,10 @@ class LoginEndpointRequest(Schema):
67
67
 
68
68
  class LoginOpenAuthRequest(Schema):
69
69
  """
70
- This is the schema used by the login endpoint with Open ID protocol
71
- Validates that either a token is provided, or both username and password are present
70
+ Schema for the login request with OpenID authentication
72
71
  """
73
-
74
- token = fields.Str(required=False)
75
- username = fields.Str(required=False)
76
- password = fields.Str(required=False)
77
-
78
- @validates_schema
79
- def validate_fields(self, data, **kwargs):
80
- if data.get("token") is None:
81
- if not data.get("username") or not data.get("password"):
82
- raise ValidationError(
83
- "A token needs to be provided when using Open ID authentication"
84
- )
85
- else:
86
- if data.get("username") or data.get("password"):
87
- raise ValidationError("The login needs to be done with a token only")
72
+ username = fields.String(required=False)
73
+ password = fields.String(required=False)
88
74
 
89
75
 
90
76
  class SignupRequest(Schema):