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.
- {cornflow-1.1.5/cornflow.egg-info → cornflow-1.2.0a1}/PKG-INFO +3 -2
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/app.py +8 -3
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/service.py +14 -6
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/config.py +9 -10
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/login.py +54 -57
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/user.py +3 -17
- cornflow-1.2.0a1/cornflow/shared/authentication/auth.py +478 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/const.py +3 -14
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/custom_test_case.py +42 -21
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_actions.py +2 -2
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_apiview.py +2 -2
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_cases.py +20 -29
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_dags.py +5 -5
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_instances.py +2 -2
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_instances_file.py +1 -1
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_licenses.py +1 -1
- cornflow-1.2.0a1/cornflow/tests/unit/test_log_in.py +531 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_permissions.py +8 -8
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_roles.py +10 -10
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_tables.py +7 -7
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_token.py +12 -6
- {cornflow-1.1.5 → cornflow-1.2.0a1/cornflow.egg-info}/PKG-INFO +3 -2
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow.egg-info/requires.txt +2 -1
- {cornflow-1.1.5 → cornflow-1.2.0a1}/requirements.txt +3 -2
- {cornflow-1.1.5 → cornflow-1.2.0a1}/setup.py +1 -1
- cornflow-1.1.5/cornflow/shared/authentication/auth.py +0 -521
- cornflow-1.1.5/cornflow/tests/unit/test_log_in.py +0 -511
- {cornflow-1.1.5 → cornflow-1.2.0a1}/MANIFEST.in +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/README.rst +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/airflow_config/__init__.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/airflow_config/airflow_local_settings.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/airflow_config/plugins/XCom/__init__.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/airflow_config/plugins/XCom/gce_xcom_backend.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/airflow_config/plugins/__init__.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/airflow_config/webserver_ldap.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/__init__.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/__init__.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/actions.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/arguments.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/config.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/migrations.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/permissions.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/roles.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/schemas.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/tools/__init__.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/tools/api_generator.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/tools/endpoint_tools.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/tools/models_tools.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/tools/schema_generator.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/tools/schemas_tools.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/tools/tools.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/users.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/utils.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/cli/views.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/commands/__init__.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/commands/access.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/commands/actions.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/commands/cleanup.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/commands/dag.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/commands/permissions.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/commands/roles.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/commands/schemas.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/commands/users.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/commands/views.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/__init__.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/action.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/alarms.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/apiview.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/case.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/dag.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/data_check.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/example_data.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/execution.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/health.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/instance.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/licenses.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/main_alarms.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/meta_resource.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/permission.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/roles.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/schemas.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/signup.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/tables.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/token.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/user.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/endpoints/user_role.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/gunicorn.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/README +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/alembic.ini +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/env.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/script.py.mako +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/00757b557b02_.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/1af47a419bbd_.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/4aac5e0c6e66_.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/7c3ea5ab5501_.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/991b98e24225_.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/a472b5ad50b7_.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/c2db9409cb5f_.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/c8a6c762e818_.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/ca449af8034c_.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/d0e0700dcd8e_.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/d1b5be1f0549_.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/e1a50dae1ac9_.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/e937a5234ce4_.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/ebdd955fcc5e_.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/migrations/versions/f3bee20314a2_.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/__init__.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/action.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/alarms.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/base_data_model.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/case.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/dag.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/dag_permissions.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/execution.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/instance.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/main_alarms.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/meta_models.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/permissions.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/role.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/user.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/user_role.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/models/view.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/__init__.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/action.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/alarms.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/case.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/common.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/dag.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/example_data.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/execution.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/health.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/instance.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/main_alarms.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/model_json.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/patch.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/permissions.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/query.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/role.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/schemas.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/solution_log.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/tables.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/user_role.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/schemas/view.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/__init__.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/authentication/__init__.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/authentication/decorators.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/authentication/ldap.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/compress.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/email.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/exceptions.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/licenses.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/log_config.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/query_tools.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/utils.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/utils_tables.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/shared/validators.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/__init__.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/const.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/custom_liveServer.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/integration/__init__.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/integration/test_commands.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/integration/test_cornflowclient.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/ldap/__init__.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/ldap/test_ldap_authentication.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/__init__.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_alarms.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_application.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_cli.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_commands.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_data_checks.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_example_data.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_executions.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_generate_from_schema.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_health.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_main_alarms.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_schema_from_models.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_schemas.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_sign_up.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/test_users.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow/tests/unit/tools.py +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow.egg-info/SOURCES.txt +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow.egg-info/dependency_links.txt +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow.egg-info/entry_points.txt +0 -0
- {cornflow-1.1.5 → cornflow-1.2.0a1}/cornflow.egg-info/top_level.txt +0 -0
- {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.
|
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
|
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
|
-
|
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,
|
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
|
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",
|
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") ==
|
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
|
-
|
139
|
+
Migrate(app=app, db=db, directory=path)
|
133
140
|
upgrade()
|
134
141
|
access_init_command(verbose=False)
|
135
|
-
if auth ==
|
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 ==
|
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
|
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
|
63
|
-
OID_PROVIDER = os.getenv("OID_PROVIDER"
|
64
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
145
|
-
|
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
|
148
|
-
:param str username:
|
149
|
-
:param str password:
|
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
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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):
|