cornflow 1.2.0a1__tar.gz → 1.2.0a2__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 (186) hide show
  1. {cornflow-1.2.0a1/cornflow.egg-info → cornflow-1.2.0a2}/PKG-INFO +13 -13
  2. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/README.rst +9 -9
  3. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/airflow_config/airflow_local_settings.py +1 -1
  4. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/cli/migrations.py +23 -3
  5. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/cli/service.py +3 -9
  6. cornflow-1.2.0a2/cornflow/cli/utils.py +41 -0
  7. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/config.py +1 -1
  8. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/__init__.py +7 -1
  9. cornflow-1.2.0a2/cornflow/endpoints/alarms.py +123 -0
  10. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/login.py +59 -38
  11. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/meta_resource.py +11 -3
  12. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/models/base_data_model.py +4 -32
  13. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/models/meta_models.py +28 -22
  14. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/models/user.py +7 -10
  15. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/alarms.py +8 -0
  16. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/query.py +2 -1
  17. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/user.py +2 -3
  18. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/shared/authentication/auth.py +19 -39
  19. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/const.py +1 -0
  20. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/custom_test_case.py +42 -12
  21. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_alarms.py +55 -1
  22. cornflow-1.2.0a2/cornflow/tests/unit/test_apiview.py +209 -0
  23. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_cli.py +6 -5
  24. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_dags.py +0 -1
  25. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_instances.py +12 -0
  26. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_log_in.py +8 -5
  27. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_roles.py +38 -0
  28. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_token.py +11 -3
  29. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_users.py +22 -6
  30. {cornflow-1.2.0a1 → cornflow-1.2.0a2/cornflow.egg-info}/PKG-INFO +13 -13
  31. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow.egg-info/requires.txt +2 -2
  32. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/requirements.txt +3 -3
  33. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/setup.py +2 -2
  34. cornflow-1.2.0a1/cornflow/cli/utils.py +0 -26
  35. cornflow-1.2.0a1/cornflow/endpoints/alarms.py +0 -59
  36. cornflow-1.2.0a1/cornflow/tests/unit/test_apiview.py +0 -104
  37. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/MANIFEST.in +0 -0
  38. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/airflow_config/__init__.py +0 -0
  39. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/airflow_config/plugins/XCom/__init__.py +0 -0
  40. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/airflow_config/plugins/XCom/gce_xcom_backend.py +0 -0
  41. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/airflow_config/plugins/__init__.py +0 -0
  42. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/airflow_config/webserver_ldap.py +0 -0
  43. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/__init__.py +0 -0
  44. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/app.py +0 -0
  45. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/cli/__init__.py +0 -0
  46. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/cli/actions.py +0 -0
  47. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/cli/arguments.py +0 -0
  48. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/cli/config.py +0 -0
  49. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/cli/permissions.py +0 -0
  50. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/cli/roles.py +0 -0
  51. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/cli/schemas.py +0 -0
  52. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/cli/tools/__init__.py +0 -0
  53. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/cli/tools/api_generator.py +0 -0
  54. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/cli/tools/endpoint_tools.py +0 -0
  55. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/cli/tools/models_tools.py +0 -0
  56. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/cli/tools/schema_generator.py +0 -0
  57. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/cli/tools/schemas_tools.py +0 -0
  58. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/cli/tools/tools.py +0 -0
  59. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/cli/users.py +0 -0
  60. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/cli/views.py +0 -0
  61. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/commands/__init__.py +0 -0
  62. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/commands/access.py +0 -0
  63. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/commands/actions.py +0 -0
  64. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/commands/cleanup.py +0 -0
  65. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/commands/dag.py +0 -0
  66. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/commands/permissions.py +0 -0
  67. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/commands/roles.py +0 -0
  68. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/commands/schemas.py +0 -0
  69. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/commands/users.py +0 -0
  70. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/commands/views.py +0 -0
  71. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/action.py +0 -0
  72. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/apiview.py +0 -0
  73. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/case.py +0 -0
  74. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/dag.py +0 -0
  75. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/data_check.py +0 -0
  76. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/example_data.py +0 -0
  77. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/execution.py +0 -0
  78. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/health.py +0 -0
  79. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/instance.py +0 -0
  80. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/licenses.py +0 -0
  81. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/main_alarms.py +0 -0
  82. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/permission.py +0 -0
  83. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/roles.py +0 -0
  84. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/schemas.py +0 -0
  85. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/signup.py +0 -0
  86. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/tables.py +0 -0
  87. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/token.py +0 -0
  88. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/user.py +0 -0
  89. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/endpoints/user_role.py +0 -0
  90. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/gunicorn.py +0 -0
  91. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/migrations/README +0 -0
  92. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/migrations/alembic.ini +0 -0
  93. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/migrations/env.py +0 -0
  94. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/migrations/script.py.mako +0 -0
  95. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/migrations/versions/00757b557b02_.py +0 -0
  96. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/migrations/versions/1af47a419bbd_.py +0 -0
  97. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/migrations/versions/4aac5e0c6e66_.py +0 -0
  98. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/migrations/versions/7c3ea5ab5501_.py +0 -0
  99. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/migrations/versions/991b98e24225_.py +0 -0
  100. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/migrations/versions/a472b5ad50b7_.py +0 -0
  101. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/migrations/versions/c2db9409cb5f_.py +0 -0
  102. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/migrations/versions/c8a6c762e818_.py +0 -0
  103. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/migrations/versions/ca449af8034c_.py +0 -0
  104. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/migrations/versions/d0e0700dcd8e_.py +0 -0
  105. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/migrations/versions/d1b5be1f0549_.py +0 -0
  106. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/migrations/versions/e1a50dae1ac9_.py +0 -0
  107. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/migrations/versions/e937a5234ce4_.py +0 -0
  108. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/migrations/versions/ebdd955fcc5e_.py +0 -0
  109. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/migrations/versions/f3bee20314a2_.py +0 -0
  110. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/models/__init__.py +0 -0
  111. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/models/action.py +0 -0
  112. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/models/alarms.py +0 -0
  113. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/models/case.py +0 -0
  114. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/models/dag.py +0 -0
  115. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/models/dag_permissions.py +0 -0
  116. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/models/execution.py +0 -0
  117. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/models/instance.py +0 -0
  118. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/models/main_alarms.py +0 -0
  119. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/models/permissions.py +0 -0
  120. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/models/role.py +0 -0
  121. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/models/user_role.py +0 -0
  122. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/models/view.py +0 -0
  123. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/__init__.py +0 -0
  124. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/action.py +0 -0
  125. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/case.py +0 -0
  126. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/common.py +0 -0
  127. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/dag.py +0 -0
  128. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/example_data.py +0 -0
  129. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/execution.py +0 -0
  130. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/health.py +0 -0
  131. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/instance.py +0 -0
  132. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/main_alarms.py +0 -0
  133. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/model_json.py +0 -0
  134. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/patch.py +0 -0
  135. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/permissions.py +0 -0
  136. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/role.py +0 -0
  137. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/schemas.py +0 -0
  138. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/solution_log.py +0 -0
  139. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/tables.py +0 -0
  140. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/user_role.py +0 -0
  141. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/schemas/view.py +0 -0
  142. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/shared/__init__.py +0 -0
  143. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/shared/authentication/__init__.py +0 -0
  144. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/shared/authentication/decorators.py +0 -0
  145. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/shared/authentication/ldap.py +0 -0
  146. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/shared/compress.py +0 -0
  147. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/shared/const.py +0 -0
  148. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/shared/email.py +0 -0
  149. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/shared/exceptions.py +0 -0
  150. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/shared/licenses.py +0 -0
  151. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/shared/log_config.py +0 -0
  152. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/shared/query_tools.py +0 -0
  153. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/shared/utils.py +0 -0
  154. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/shared/utils_tables.py +0 -0
  155. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/shared/validators.py +0 -0
  156. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/__init__.py +0 -0
  157. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/custom_liveServer.py +0 -0
  158. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/integration/__init__.py +0 -0
  159. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/integration/test_commands.py +0 -0
  160. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/integration/test_cornflowclient.py +0 -0
  161. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/ldap/__init__.py +0 -0
  162. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/ldap/test_ldap_authentication.py +0 -0
  163. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/__init__.py +0 -0
  164. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_actions.py +0 -0
  165. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_application.py +0 -0
  166. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_cases.py +0 -0
  167. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_commands.py +0 -0
  168. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_data_checks.py +0 -0
  169. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_example_data.py +0 -0
  170. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_executions.py +0 -0
  171. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_generate_from_schema.py +0 -0
  172. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_health.py +0 -0
  173. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_instances_file.py +0 -0
  174. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_licenses.py +0 -0
  175. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_main_alarms.py +0 -0
  176. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_permissions.py +0 -0
  177. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_schema_from_models.py +0 -0
  178. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_schemas.py +0 -0
  179. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_sign_up.py +0 -0
  180. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/test_tables.py +0 -0
  181. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow/tests/unit/tools.py +0 -0
  182. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow.egg-info/SOURCES.txt +0 -0
  183. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow.egg-info/dependency_links.txt +0 -0
  184. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow.egg-info/entry_points.txt +0 -0
  185. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/cornflow.egg-info/top_level.txt +0 -0
  186. {cornflow-1.2.0a1 → cornflow-1.2.0a2}/setup.cfg +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: cornflow
3
- Version: 1.2.0a1
4
- Summary: Cornflow is an open source multi-solver optimization server with a REST API built using flask.
3
+ Version: 1.2.0a2
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
7
7
  Author-email: cornflow@baobabsoluciones.es
@@ -12,8 +12,9 @@ Classifier: Development Status :: 5 - Production/Stable
12
12
  Requires-Python: >=3.9
13
13
  Requires-Dist: alembic==1.9.2
14
14
  Requires-Dist: apispec<=6.3.0
15
+ Requires-Dist: cachetools==5.3.3
15
16
  Requires-Dist: click<=8.1.7
16
- Requires-Dist: cornflow-client==1.2.0a1
17
+ Requires-Dist: cornflow-client>=1.2.0
17
18
  Requires-Dist: cryptography<=42.0.5
18
19
  Requires-Dist: disposable-email-domains>=0.0.86
19
20
  Requires-Dist: Flask==2.3.2
@@ -40,7 +41,6 @@ Requires-Dist: requests<=2.32.3
40
41
  Requires-Dist: SQLAlchemy==1.3.21
41
42
  Requires-Dist: webargs<=8.3.0
42
43
  Requires-Dist: Werkzeug==3.0.6
43
- Requires-Dist: cachetools==5.3.3
44
44
  Dynamic: author
45
45
  Dynamic: author-email
46
46
  Dynamic: classifier
@@ -50,7 +50,7 @@ Dynamic: requires-dist
50
50
  Dynamic: requires-python
51
51
  Dynamic: summary
52
52
 
53
- Cornflow
53
+ cornflow
54
54
  =========
55
55
 
56
56
  .. image:: https://github.com/baobabsoluciones/cornflow/workflows/build/badge.svg?style=svg
@@ -70,13 +70,13 @@ Cornflow
70
70
 
71
71
  .. image:: https://img.shields.io/badge/License-Apache2.0-blue
72
72
 
73
- Cornflow is an open source multi-solver optimization server with a REST API built using `flask <https://flask.palletsprojects.com>`_, `airflow <https://airflow.apache.org/>`_ and `pulp <https://coin-or.github.io/pulp/>`_.
73
+ cornflow is an open source multi-solver optimization server with a REST API built using `flask <https://flask.palletsprojects.com>`_, `airflow <https://airflow.apache.org/>`_ and `pulp <https://coin-or.github.io/pulp/>`_.
74
74
 
75
- While most deployment servers are based on the solving technique (MIP, CP, NLP, etc.), Cornflow focuses on the optimization problems themselves. However, it does not impose any constraint on the type of problem and solution method to use.
75
+ While most deployment servers are based on the solving technique (MIP, CP, NLP, etc.), cornflow focuses on the optimization problems themselves. However, it does not impose any constraint on the type of problem and solution method to use.
76
76
 
77
- With Cornflow you can deploy a Traveling Salesman Problem solver next to a Knapsack solver or a Nurse Rostering Problem solver. As long as you describe the input and output data, you can upload any solution method for any problem and then use it with any data you want.
77
+ With cornflow you can deploy a Traveling Salesman Problem solver next to a Knapsack solver or a Nurse Rostering Problem solver. As long as you describe the input and output data, you can upload any solution method for any problem and then use it with any data you want.
78
78
 
79
- Cornflow helps you formalize your problem by proposing development guidelines. It also provides a range of functionalities around your deployed solution method, namely:
79
+ cornflow helps you formalize your problem by proposing development guidelines. It also provides a range of functionalities around your deployed solution method, namely:
80
80
 
81
81
  * storage of users, instances, solutions and solution logs.
82
82
  * deployment and maintenance of models, solvers and algorithms.
@@ -92,9 +92,9 @@ Cornflow helps you formalize your problem by proposing development guidelines. I
92
92
  Installation instructions
93
93
  -------------------------------
94
94
 
95
- Cornflow is tested with Ubuntu 20.04, python >= 3.8 and git.
95
+ cornflow is tested with Ubuntu 20.04, python >= 3.8 and git.
96
96
 
97
- Download the Cornflow project and install requirements::
97
+ Download the cornflow project and install requirements::
98
98
 
99
99
  python3 -m venv venv
100
100
  venv/bin/pip3 install cornflow
@@ -110,7 +110,7 @@ initialize the sqlite database::
110
110
  flask create_admin_user -u cornflow -e cornflow_admin@admin.com -p cornflow_admin_password
111
111
 
112
112
 
113
- activate the virtual environment and run Cornflow::
113
+ activate the virtual environment and run cornflow::
114
114
 
115
115
  source venv/bin/activate
116
116
  export FLASK_APP=cornflow.app
@@ -121,7 +121,7 @@ activate the virtual environment and run Cornflow::
121
121
  export AIRFLOW_PWD=airflow_pwd
122
122
  flask run
123
123
 
124
- **Cornflow needs a running installation of Airflow to operate and more configuration**. Check `the installation docs <https://baobabsoluciones.github.io/cornflow/main/install.html>`_ for more details on installing airflow, configuring the application and initializing the database.
124
+ **cornflow needs a running installation of Airflow to operate and more configuration**. Check `the installation docs <https://baobabsoluciones.github.io/cornflow/main/install.html>`_ for more details on installing airflow, configuring the application and initializing the database.
125
125
 
126
126
  Using cornflow to solve a PuLP model
127
127
  ---------------------------------------
@@ -1,4 +1,4 @@
1
- Cornflow
1
+ cornflow
2
2
  =========
3
3
 
4
4
  .. image:: https://github.com/baobabsoluciones/cornflow/workflows/build/badge.svg?style=svg
@@ -18,13 +18,13 @@ Cornflow
18
18
 
19
19
  .. image:: https://img.shields.io/badge/License-Apache2.0-blue
20
20
 
21
- Cornflow is an open source multi-solver optimization server with a REST API built using `flask <https://flask.palletsprojects.com>`_, `airflow <https://airflow.apache.org/>`_ and `pulp <https://coin-or.github.io/pulp/>`_.
21
+ cornflow is an open source multi-solver optimization server with a REST API built using `flask <https://flask.palletsprojects.com>`_, `airflow <https://airflow.apache.org/>`_ and `pulp <https://coin-or.github.io/pulp/>`_.
22
22
 
23
- While most deployment servers are based on the solving technique (MIP, CP, NLP, etc.), Cornflow focuses on the optimization problems themselves. However, it does not impose any constraint on the type of problem and solution method to use.
23
+ While most deployment servers are based on the solving technique (MIP, CP, NLP, etc.), cornflow focuses on the optimization problems themselves. However, it does not impose any constraint on the type of problem and solution method to use.
24
24
 
25
- With Cornflow you can deploy a Traveling Salesman Problem solver next to a Knapsack solver or a Nurse Rostering Problem solver. As long as you describe the input and output data, you can upload any solution method for any problem and then use it with any data you want.
25
+ With cornflow you can deploy a Traveling Salesman Problem solver next to a Knapsack solver or a Nurse Rostering Problem solver. As long as you describe the input and output data, you can upload any solution method for any problem and then use it with any data you want.
26
26
 
27
- Cornflow helps you formalize your problem by proposing development guidelines. It also provides a range of functionalities around your deployed solution method, namely:
27
+ cornflow helps you formalize your problem by proposing development guidelines. It also provides a range of functionalities around your deployed solution method, namely:
28
28
 
29
29
  * storage of users, instances, solutions and solution logs.
30
30
  * deployment and maintenance of models, solvers and algorithms.
@@ -40,9 +40,9 @@ Cornflow helps you formalize your problem by proposing development guidelines. I
40
40
  Installation instructions
41
41
  -------------------------------
42
42
 
43
- Cornflow is tested with Ubuntu 20.04, python >= 3.8 and git.
43
+ cornflow is tested with Ubuntu 20.04, python >= 3.8 and git.
44
44
 
45
- Download the Cornflow project and install requirements::
45
+ Download the cornflow project and install requirements::
46
46
 
47
47
  python3 -m venv venv
48
48
  venv/bin/pip3 install cornflow
@@ -58,7 +58,7 @@ initialize the sqlite database::
58
58
  flask create_admin_user -u cornflow -e cornflow_admin@admin.com -p cornflow_admin_password
59
59
 
60
60
 
61
- activate the virtual environment and run Cornflow::
61
+ activate the virtual environment and run cornflow::
62
62
 
63
63
  source venv/bin/activate
64
64
  export FLASK_APP=cornflow.app
@@ -69,7 +69,7 @@ activate the virtual environment and run Cornflow::
69
69
  export AIRFLOW_PWD=airflow_pwd
70
70
  flask run
71
71
 
72
- **Cornflow needs a running installation of Airflow to operate and more configuration**. Check `the installation docs <https://baobabsoluciones.github.io/cornflow/main/install.html>`_ for more details on installing airflow, configuring the application and initializing the database.
72
+ **cornflow needs a running installation of Airflow to operate and more configuration**. Check `the installation docs <https://baobabsoluciones.github.io/cornflow/main/install.html>`_ for more details on installing airflow, configuring the application and initializing the database.
73
73
 
74
74
  Using cornflow to solve a PuLP model
75
75
  ---------------------------------------
@@ -19,5 +19,5 @@ STATE_COLORS = {
19
19
  from airflow.www.utils import UIAlert
20
20
 
21
21
  DASHBOARD_UIALERTS = [
22
- UIAlert("Welcome! This is the backend of your Cornflow environment. Airflow™ is a platform created by the community to programmatically author, schedule and monitor workflows."),
22
+ UIAlert("Welcome! This is the backend of your cornflow environment. Airflow™ is a platform created by the community to programmatically author, schedule and monitor workflows."),
23
23
  ]
@@ -3,7 +3,7 @@ import os.path
3
3
 
4
4
  import click
5
5
  from cornflow.shared import db
6
- from flask_migrate import Migrate, migrate, upgrade, init
6
+ from flask_migrate import Migrate, migrate, upgrade, downgrade, init
7
7
 
8
8
  from .utils import get_app
9
9
 
@@ -28,7 +28,27 @@ def migrate_migrations():
28
28
 
29
29
 
30
30
  @migrations.command(name="upgrade", help="Apply migrations")
31
- def upgrade_migrations():
31
+ @click.option(
32
+ "-r", "--revision", type=str, help="The revision to upgrade to", default="head"
33
+ )
34
+ def upgrade_migrations(revision="head"):
35
+ app = get_app()
36
+ external = int(os.getenv("EXTERNAL_APP", 0))
37
+ if external == 0:
38
+ path = "./cornflow/migrations"
39
+ else:
40
+ path = f"./{os.getenv('EXTERNAL_APP_MODULE', 'external_app')}/migrations"
41
+
42
+ with app.app_context():
43
+ migration_client = Migrate(app=app, db=db, directory=path)
44
+ upgrade(revision=revision)
45
+
46
+
47
+ @migrations.command(name="downgrade", help="Downgrade migrations")
48
+ @click.option(
49
+ "-r", "--revision", type=str, help="The revision to downgrade to", default="-1"
50
+ )
51
+ def downgrade_migrations(revision="-1"):
32
52
  app = get_app()
33
53
  external = int(os.getenv("EXTERNAL_APP", 0))
34
54
  if external == 0:
@@ -38,7 +58,7 @@ def upgrade_migrations():
38
58
 
39
59
  with app.app_context():
40
60
  migration_client = Migrate(app=app, db=db, directory=path)
41
- upgrade()
61
+ downgrade(revision=revision)
42
62
 
43
63
 
44
64
  @migrations.command(
@@ -6,6 +6,7 @@ from logging import error
6
6
 
7
7
 
8
8
  import click
9
+ from .utils import get_db_conn
9
10
  import cornflow
10
11
  from cornflow.app import create_app
11
12
  from cornflow.commands import (
@@ -56,15 +57,8 @@ def init_cornflow_service():
56
57
  os.environ["SECRET_KEY"] = os.getenv("FERNET_KEY", Fernet.generate_key().decode())
57
58
 
58
59
  # Cornflow db defaults
59
- cornflow_db_host = os.getenv("CORNFLOW_DB_HOST", "cornflow_db")
60
- cornflow_db_port = os.getenv("CORNFLOW_DB_PORT", "5432")
61
- cornflow_db_user = os.getenv("CORNFLOW_DB_USER", "cornflow")
62
- cornflow_db_password = os.getenv("CORNFLOW_DB_PASSWORD", "cornflow")
63
- cornflow_db = os.getenv("CORNFLOW_DB", "cornflow")
64
- cornflow_db_conn = os.getenv(
65
- "cornflow_db_conn",
66
- f"postgresql://{cornflow_db_user}:{cornflow_db_password}@{cornflow_db_host}:{cornflow_db_port}/{cornflow_db}",
67
- )
60
+ os.environ["DEFAULT_POSTGRES"] = "1"
61
+ cornflow_db_conn = get_db_conn()
68
62
  os.environ["DATABASE_URL"] = cornflow_db_conn
69
63
 
70
64
  # Platform auth config and service users
@@ -0,0 +1,41 @@
1
+ import os
2
+ import sys
3
+ from importlib import import_module
4
+ import warnings
5
+
6
+
7
+ def get_app():
8
+ env = os.getenv("FLASK_ENV", "development")
9
+ data_conn = get_db_conn()
10
+ if env == "production":
11
+ warnings.filterwarnings("ignore")
12
+ external = int(os.getenv("EXTERNAL_APP", 0))
13
+ if external == 0:
14
+ from cornflow.app import create_app
15
+ else:
16
+ sys.path.append("./")
17
+ external_app = os.getenv("EXTERNAL_APP_MODULE", "external_app")
18
+ external_module = import_module(external_app)
19
+ create_app = external_module.create_wsgi_app
20
+
21
+ if data_conn is None:
22
+ app = create_app(env)
23
+ else:
24
+ app = create_app(env, data_conn)
25
+
26
+ return app
27
+
28
+
29
+ def get_db_conn():
30
+ if int(os.getenv("DEFAULT_POSTGRES", 0)) == 0:
31
+ return os.getenv("DATABASE_URL", "sqlite:///cornflow.db")
32
+ else:
33
+ cornflow_db_host = os.getenv("CORNFLOW_DB_HOST", "cornflow_db")
34
+ cornflow_db_port = os.getenv("CORNFLOW_DB_PORT", "5432")
35
+ cornflow_db_user = os.getenv("CORNFLOW_DB_USER", "cornflow")
36
+ cornflow_db_password = os.getenv("CORNFLOW_DB_PASSWORD", "cornflow")
37
+ cornflow_db = os.getenv("CORNFLOW_DB", "cornflow")
38
+ return os.getenv(
39
+ "cornflow_db_conn",
40
+ f"postgresql://{cornflow_db_user}:{cornflow_db_password}@{cornflow_db_host}:{cornflow_db_port}/{cornflow_db}",
41
+ )
@@ -65,7 +65,7 @@ class DefaultConfig(object):
65
65
 
66
66
  # APISPEC:
67
67
  APISPEC_SPEC = APISpec(
68
- title="Cornflow API docs",
68
+ title="cornflow API docs",
69
69
  version="v1",
70
70
  plugins=[MarshmallowPlugin()],
71
71
  openapi_version="2.0.0",
@@ -3,8 +3,9 @@ Initialization file for the endpoints module
3
3
  All references to endpoints should be imported from here
4
4
  The login resource gets created on app startup as it depends on configuration
5
5
  """
6
+
6
7
  from .action import ActionListEndpoint
7
- from .alarms import AlarmsEndpoint
8
+ from .alarms import AlarmsEndpoint, AlarmDetailEndpoint
8
9
  from .apiview import ApiViewListEndpoint
9
10
  from .case import (
10
11
  CaseEndpoint,
@@ -225,6 +226,11 @@ alarms_resources = [
225
226
  urls="/alarms/",
226
227
  endpoint="alarms",
227
228
  ),
229
+ dict(
230
+ resource=AlarmDetailEndpoint,
231
+ urls="/alarms/<int:idx>/",
232
+ endpoint="alarms-detail",
233
+ ),
228
234
  dict(
229
235
  resource=MainAlarmsEndpoint,
230
236
  urls="/main-alarms/",
@@ -0,0 +1,123 @@
1
+ # Imports from libraries
2
+ from flask import current_app
3
+ from flask_apispec import doc, marshal_with, use_kwargs
4
+
5
+ # Import from internal modules
6
+ from cornflow.endpoints.meta_resource import BaseMetaResource
7
+ from cornflow.models import AlarmsModel
8
+ from cornflow.schemas.alarms import (
9
+ AlarmEditRequest,
10
+ AlarmsResponse,
11
+ AlarmsPostRequest,
12
+ QueryFiltersAlarms,
13
+ )
14
+ from cornflow.shared.authentication import Auth, authenticate
15
+ from cornflow.shared.exceptions import AirflowError, ObjectDoesNotExist, InvalidData
16
+ from cornflow.shared.const import SERVICE_ROLE
17
+
18
+
19
+ class AlarmsEndpoint(BaseMetaResource):
20
+ """
21
+ Endpoint used to manage the table alarms.
22
+ Available methods: [get, post]
23
+ """
24
+
25
+ def __init__(self):
26
+ super().__init__()
27
+ self.data_model = AlarmsModel
28
+ self.unique = ["id"]
29
+
30
+ @doc(
31
+ description="Get list of all the elements in the table",
32
+ tags=["None"],
33
+ )
34
+ @authenticate(auth_class=Auth())
35
+ @marshal_with(AlarmsResponse(many=True))
36
+ @use_kwargs(QueryFiltersAlarms, location="query")
37
+ def get(self, **kwargs):
38
+ """
39
+ API method to get all the rows of the table.
40
+ It requires authentication to be passed in the form of a token that has to be linked to
41
+ an existing session (login) made by a user.
42
+ :return: A list of objects with the data, and an integer with the HTTP status code.
43
+ :rtype: Tuple(dict, integer)
44
+ """
45
+ return self.get_list(**kwargs)
46
+
47
+ @doc(
48
+ description="Add a new row to the table",
49
+ tags=["None"],
50
+ )
51
+ @authenticate(auth_class=Auth())
52
+ @marshal_with(AlarmsResponse)
53
+ @use_kwargs(AlarmsPostRequest, location="json")
54
+ def post(self, **kwargs):
55
+ """
56
+ API method to add a row to the table.
57
+ It requires authentication to be passed in the form of a token that has to be linked to
58
+ an existing session (login) made by a user.
59
+ :return: An object with the data for the created row,
60
+ and an integer with the HTTP status code.
61
+ :rtype: Tuple(dict, integer)
62
+ """
63
+ return self.post_list(data=kwargs)
64
+
65
+
66
+ class AlarmDetailEndpointBase(BaseMetaResource):
67
+ """
68
+ Endpoint used to get the information of a certain alarm. But not the data!
69
+ """
70
+
71
+ def __init__(self):
72
+ super().__init__()
73
+ self.data_model = AlarmsModel
74
+ self.unique = ["id"]
75
+
76
+
77
+ class AlarmDetailEndpoint(AlarmDetailEndpointBase):
78
+ @doc(description="Get details of an alarm", tags=["None"], inherit=False)
79
+ @authenticate(auth_class=Auth())
80
+ @marshal_with(AlarmsResponse)
81
+ @BaseMetaResource.get_data_or_404
82
+ def get(self, idx):
83
+ """
84
+ API method to get an execution created by the user and its related info.
85
+ It requires authentication to be passed in the form of a token that has to be linked to
86
+ an existing session (login) made by a user.
87
+
88
+ :param str idx: ID of the execution.
89
+ :return: A dictionary with a message (error if authentication failed, or the execution does not exist or
90
+ the data of the execution) and an integer with the HTTP status code.
91
+ :rtype: Tuple(dict, integer)
92
+ """
93
+ current_app.logger.info(
94
+ f"User {self.get_user()} gets details of execution {idx}"
95
+ )
96
+ return self.get_detail(idx=idx)
97
+
98
+ @doc(description="Edit an execution", tags=["Executions"], inherit=False)
99
+ @authenticate(auth_class=Auth())
100
+ @use_kwargs(AlarmEditRequest, location="json")
101
+ def put(self, idx, **data):
102
+ """
103
+ Edit an existing alarm
104
+
105
+ :param string idx: ID of the alarm.
106
+ :return: A dictionary with a message (error if authentication failed, or the alarm does not exist or
107
+ a message) and an integer with the HTTP status code.
108
+ :rtype: Tuple(dict, integer)
109
+ """
110
+ current_app.logger.info(f"User {self.get_user()} edits alarm {idx}")
111
+ return self.put_detail(data, track_user=False, idx=idx)
112
+
113
+ @doc(description="Disable an alarm", tags=["None"])
114
+ @authenticate(auth_class=Auth())
115
+ def delete(self, idx):
116
+ """
117
+ :param int alarm_id: Alarm id.
118
+ :return:
119
+ :rtype: Tuple(dict, integer)
120
+ """
121
+
122
+ current_app.logger.info(f"Alarm {idx} was disabled by user {self.get_user()}")
123
+ return self.disable_detail(idx=idx)
@@ -2,15 +2,16 @@
2
2
  External endpoint for the user to log in to the cornflow webserver
3
3
  """
4
4
 
5
+ from datetime import datetime, timezone, timedelta
6
+
5
7
  # Partial imports
6
8
  from flask import current_app, request
7
9
  from flask_apispec import use_kwargs, doc
8
10
  from sqlalchemy.exc import IntegrityError, DBAPIError
9
- from datetime import datetime, timedelta
10
11
 
11
12
  # Import from internal modules
12
13
  from cornflow.endpoints.meta_resource import BaseMetaResource
13
- from cornflow.models import UserModel, UserRoleModel
14
+ from cornflow.models import UserModel, UserRoleModel, PermissionsDAG
14
15
  from cornflow.schemas.user import LoginEndpointRequest, LoginOpenAuthRequest
15
16
  from cornflow.shared import db
16
17
  from cornflow.shared.authentication import Auth, LDAPBase
@@ -51,22 +52,35 @@ class LoginBaseEndpoint(BaseMetaResource):
51
52
  if auth_type == AUTH_DB:
52
53
  user = self.auth_db_authenticate(**kwargs)
53
54
  response.update({"change_password": check_last_password_change(user)})
54
- current_app.logger.info(f"User {user.id} logged in successfully using database authentication")
55
+ current_app.logger.info(
56
+ f"User {user.id} logged in successfully using database authentication"
57
+ )
55
58
  elif auth_type == AUTH_LDAP:
56
59
  user = self.auth_ldap_authenticate(**kwargs)
57
- current_app.logger.info(f"User {user.id} logged in successfully using LDAP authentication")
60
+ current_app.logger.info(
61
+ f"User {user.id} logged in successfully using LDAP authentication"
62
+ )
58
63
  elif auth_type == AUTH_OID:
59
- if (kwargs.get('username') and kwargs.get('password')):
64
+ if kwargs.get("username") and kwargs.get("password"):
60
65
  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")
66
+ raise InvalidUsage(
67
+ "Must provide a token in Authorization header. Cannot log in with username and password",
68
+ 400,
69
+ )
70
+ user = self.auth_oid_authenticate(
71
+ username=kwargs["username"], password=kwargs["password"]
72
+ )
73
+ current_app.logger.info(
74
+ f"Service user {user.id} logged in successfully using password"
75
+ )
64
76
  token = self.auth_class.generate_token(user.id)
65
77
  else:
66
78
  token = self.auth_class().get_token_from_header(request.headers)
67
79
  user = self.auth_oid_authenticate(token=token)
68
- current_app.logger.info(f"User {user.id} logged in successfully using OpenID authentication")
69
-
80
+ current_app.logger.info(
81
+ f"User {user.id} logged in successfully using OpenID authentication"
82
+ )
83
+
70
84
  response.update({"token": token, "id": user.id})
71
85
  return response, 200
72
86
  else:
@@ -147,7 +161,9 @@ class LoginBaseEndpoint(BaseMetaResource):
147
161
 
148
162
  return user
149
163
 
150
- def auth_oid_authenticate(self, token: str = None, username: str = None, password: str = None):
164
+ def auth_oid_authenticate(
165
+ self, token: str = None, username: str = None, password: str = None
166
+ ):
151
167
  """
152
168
  Method in charge of performing the authentication using OpenID Connect tokens.
153
169
  Supports any OIDC provider configured via provider_url.
@@ -158,33 +174,22 @@ class LoginBaseEndpoint(BaseMetaResource):
158
174
  :return: the user object, or it raises an error if it has not been possible to log in
159
175
  :rtype: :class:`UserModel`
160
176
  """
161
- print("[auth_oid_authenticate] Starting OpenID authentication")
162
177
  if token:
163
- print("[auth_oid_authenticate] Authenticating with token")
178
+
164
179
  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
174
- )
175
180
 
176
- print(f"[auth_oid_authenticate] Looking up user: {username}")
181
+ username = decoded_token.get("sub")
182
+
177
183
  user = self.data_model.get_one_object(username=username)
178
184
 
179
185
  if not user:
180
- print(f"[auth_oid_authenticate] Creating new user: {username}")
181
186
  current_app.logger.info(
182
187
  f"OpenID user {username} does not exist and is created"
183
188
  )
184
189
 
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', '')
190
+ email = decoded_token.get("email", f"{username}@cornflow.org")
191
+ first_name = decoded_token.get("given_name", "")
192
+ last_name = decoded_token.get("family_name", "")
188
193
 
189
194
  data = {
190
195
  "username": username,
@@ -203,13 +208,12 @@ class LoginBaseEndpoint(BaseMetaResource):
203
208
  }
204
209
  )
205
210
  user_role.save()
211
+ if int(current_app.config["OPEN_DEPLOYMENT"]) == 1:
212
+ PermissionsDAG.add_all_permissions_to_user(user.id)
206
213
 
207
214
  return user
208
215
 
209
- elif (
210
- username
211
- and password
212
- ):
216
+ elif username and password:
213
217
  user = self.auth_db_authenticate(username, password)
214
218
  if user.is_service_user():
215
219
  return user
@@ -221,17 +225,34 @@ class LoginBaseEndpoint(BaseMetaResource):
221
225
  def check_last_password_change(user):
222
226
  """
223
227
  Check if the user needs to change their password based on the password rotation time.
224
-
228
+
225
229
  :param user: The user object to check
226
230
  :return: True if password needs to be changed, False otherwise
227
231
  :rtype: bool
228
232
  """
229
233
  if user.pwd_last_change:
230
- if (
231
- user.pwd_last_change
232
- + timedelta(days=int(current_app.config["PWD_ROTATION_TIME"]))
233
- < datetime.utcnow()
234
- ):
234
+ # Handle the case where pwd_last_change is already a datetime object
235
+ if isinstance(user.pwd_last_change, datetime):
236
+ # If it's a naive datetime (no timezone info), make it timezone-aware
237
+ if user.pwd_last_change.tzinfo is None:
238
+ last_change = user.pwd_last_change.replace(tzinfo=timezone.utc)
239
+ else:
240
+ # Already timezone-aware
241
+ last_change = user.pwd_last_change
242
+ else:
243
+ # It's a timestamp (integer), convert to datetime
244
+ last_change = datetime.fromtimestamp(user.pwd_last_change, timezone.utc)
245
+
246
+ # Get current time with UTC timezone for proper comparison
247
+ current_time = datetime.now(timezone.utc)
248
+
249
+ # Calculate the expiration time based on the password rotation setting
250
+ expiration_time = last_change + timedelta(
251
+ days=int(current_app.config["PWD_ROTATION_TIME"])
252
+ )
253
+
254
+ # Compare the timezone-aware datetimes
255
+ if expiration_time < current_time:
235
256
  return True
236
257
  return False
237
258
 
@@ -13,6 +13,7 @@ from cornflow.shared.const import ALL_DEFAULT_ROLES
13
13
  from cornflow.shared.exceptions import InvalidUsage, ObjectDoesNotExist, NoPermission
14
14
 
15
15
 
16
+
16
17
  class BaseMetaResource(Resource, MethodResource):
17
18
  """
18
19
  The base resource from all methods inherit from.
@@ -175,11 +176,18 @@ class BaseMetaResource(Resource, MethodResource):
175
176
  METHODS USED FOR ACTIVATING / DISABLING RECORDS IN CASE WE DO NOT WANT TO DELETE THEM STRAIGHT AWAY
176
177
  """
177
178
 
178
- def disable_detail(self):
179
+ def disable_detail(self, idx):
179
180
  """
180
- Method not implemented yet
181
+ Method to DISABLE an object from the database
182
+
183
+ :param idx: the idx which identifies the object
184
+ :return: the object and a status code.
181
185
  """
182
- raise NotImplemented
186
+ row = self.data_model.query.get(idx)
187
+ if row is None:
188
+ raise ObjectDoesNotExist(f"Object with id {idx} not found.")
189
+ row.disable()
190
+ return {"message": "Object marked as disabled"}, 200
183
191
 
184
192
  def activate_detail(self, **kwargs):
185
193
  """