cornflow 1.1.5__py3-none-any.whl → 1.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- airflow_config/airflow_local_settings.py +1 -1
- cornflow/app.py +8 -3
- cornflow/cli/migrations.py +23 -3
- cornflow/cli/service.py +17 -15
- cornflow/cli/utils.py +16 -1
- cornflow/config.py +10 -11
- cornflow/endpoints/__init__.py +7 -1
- cornflow/endpoints/alarms.py +66 -2
- cornflow/endpoints/login.py +81 -63
- cornflow/endpoints/meta_resource.py +11 -3
- cornflow/models/base_data_model.py +4 -32
- cornflow/models/meta_models.py +28 -22
- cornflow/models/user.py +7 -10
- cornflow/schemas/alarms.py +8 -0
- cornflow/schemas/query.py +2 -1
- cornflow/schemas/user.py +5 -20
- cornflow/shared/authentication/auth.py +201 -264
- cornflow/shared/const.py +3 -14
- cornflow/tests/const.py +1 -0
- cornflow/tests/custom_test_case.py +77 -26
- cornflow/tests/unit/test_actions.py +2 -2
- cornflow/tests/unit/test_alarms.py +55 -1
- cornflow/tests/unit/test_apiview.py +108 -3
- cornflow/tests/unit/test_cases.py +20 -29
- cornflow/tests/unit/test_cli.py +6 -5
- cornflow/tests/unit/test_dags.py +5 -6
- cornflow/tests/unit/test_instances.py +14 -2
- cornflow/tests/unit/test_instances_file.py +1 -1
- cornflow/tests/unit/test_licenses.py +1 -1
- cornflow/tests/unit/test_log_in.py +230 -207
- cornflow/tests/unit/test_permissions.py +8 -8
- cornflow/tests/unit/test_roles.py +48 -10
- cornflow/tests/unit/test_tables.py +7 -7
- cornflow/tests/unit/test_token.py +19 -5
- cornflow/tests/unit/test_users.py +22 -6
- {cornflow-1.1.5.dist-info → cornflow-1.2.0.dist-info}/METADATA +13 -12
- {cornflow-1.1.5.dist-info → cornflow-1.2.0.dist-info}/RECORD +40 -40
- {cornflow-1.1.5.dist-info → cornflow-1.2.0.dist-info}/WHEEL +1 -1
- {cornflow-1.1.5.dist-info → cornflow-1.2.0.dist-info}/entry_points.txt +0 -0
- {cornflow-1.1.5.dist-info → cornflow-1.2.0.dist-info}/top_level.txt +0 -0
@@ -1,14 +1,13 @@
|
|
1
1
|
"""
|
2
2
|
|
3
3
|
"""
|
4
|
-
|
4
|
+
|
5
5
|
from flask import current_app
|
6
6
|
from sqlalchemy import desc
|
7
7
|
from sqlalchemy.dialects.postgresql import JSON
|
8
8
|
from sqlalchemy.dialects.postgresql import TEXT
|
9
9
|
from sqlalchemy.ext.declarative import declared_attr
|
10
10
|
|
11
|
-
# Import from internal modules
|
12
11
|
from cornflow.models.meta_models import TraceAttributesModel
|
13
12
|
from cornflow.shared import db
|
14
13
|
from cornflow.shared.utils import hash_json_256
|
@@ -39,7 +38,7 @@ class BaseDataModel(TraceAttributesModel):
|
|
39
38
|
|
40
39
|
def __init__(self, data):
|
41
40
|
self.user_id = data.get("user_id")
|
42
|
-
self.data = data.get("data")
|
41
|
+
self.data = data.get("data")
|
43
42
|
self.data_hash = hash_json_256(self.data)
|
44
43
|
self.name = data.get("name")
|
45
44
|
self.description = data.get("description")
|
@@ -50,23 +49,16 @@ class BaseDataModel(TraceAttributesModel):
|
|
50
49
|
@classmethod
|
51
50
|
def get_all_objects(
|
52
51
|
cls,
|
53
|
-
user,
|
54
52
|
schema=None,
|
55
53
|
creation_date_gte=None,
|
56
54
|
creation_date_lte=None,
|
57
55
|
deletion_date_gte=None,
|
58
56
|
deletion_date_lte=None,
|
59
|
-
id=None,
|
60
57
|
update_date_gte=None,
|
61
58
|
update_date_lte=None,
|
62
|
-
user_name=None,
|
63
|
-
last_name=None,
|
64
|
-
email=None,
|
65
|
-
role_id=None,
|
66
|
-
url_rule=None,
|
67
|
-
description=None,
|
68
59
|
offset=0,
|
69
60
|
limit=10,
|
61
|
+
user=None,
|
70
62
|
):
|
71
63
|
"""
|
72
64
|
Query to get all objects from a user
|
@@ -77,13 +69,6 @@ class BaseDataModel(TraceAttributesModel):
|
|
77
69
|
:param string creation_date_lte: created_at needs to be smaller or equal to this
|
78
70
|
:param string deletion_date_gte: deletion_at needs to be larger or equal to this
|
79
71
|
:param string deletion_date_lte: deletion_at needs to be smaller or equal to this
|
80
|
-
:param string id: user id
|
81
|
-
:param string user_name: user first name
|
82
|
-
:param string last_name: user last name
|
83
|
-
:param string email: user email
|
84
|
-
:param int role_id: user role id
|
85
|
-
:param string url_rule: url_rule
|
86
|
-
:param string description: description
|
87
72
|
:param string update_date_gte: update_at needs to be larger or equal to this
|
88
73
|
:param string update_date_lte: update_at needs to be smaller or equal to this
|
89
74
|
:param int offset: query offset for pagination
|
@@ -114,20 +99,7 @@ class BaseDataModel(TraceAttributesModel):
|
|
114
99
|
if update_date_gte:
|
115
100
|
query = query.filter(cls.update_at >= update_date_gte)
|
116
101
|
if update_date_lte:
|
117
|
-
query = query.filter(cls.
|
118
|
-
if user_name:
|
119
|
-
query = query.filter(cls.user_name == user_name)
|
120
|
-
if last_name:
|
121
|
-
query = query.filter(cls.last_name == last_name)
|
122
|
-
if email:
|
123
|
-
query = query.filter(cls.email == email)
|
124
|
-
if role_id:
|
125
|
-
query = query.filter(cls.role_id == role_id)
|
126
|
-
if url_rule:
|
127
|
-
query = query.filter(cls.url_rule == url_rule)
|
128
|
-
if description:
|
129
|
-
query = query.filter(cls.description == description)
|
130
|
-
# if airflow they also return total_entries = query.count(), for some reason
|
102
|
+
query = query.filter(cls.update_at <= update_date_lte)
|
131
103
|
|
132
104
|
return query.order_by(desc(cls.created_at)).offset(offset).limit(limit).all()
|
133
105
|
|
cornflow/models/meta_models.py
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
"""
|
2
2
|
This file contains the base abstract models from which the rest of the models inherit
|
3
3
|
"""
|
4
|
-
|
5
|
-
from datetime import datetime
|
4
|
+
|
5
|
+
from datetime import datetime, timezone
|
6
|
+
from typing import Dict, List
|
7
|
+
|
6
8
|
from flask import current_app
|
7
9
|
from sqlalchemy.exc import DBAPIError, IntegrityError
|
8
|
-
from typing import Dict, List
|
9
10
|
|
10
|
-
# Imports from internal modules
|
11
11
|
from cornflow.shared import db
|
12
12
|
from cornflow.shared.exceptions import InvalidData
|
13
13
|
|
@@ -33,7 +33,9 @@ class EmptyBaseModel(db.Model):
|
|
33
33
|
|
34
34
|
try:
|
35
35
|
db.session.commit()
|
36
|
-
current_app.logger.debug(
|
36
|
+
current_app.logger.debug(
|
37
|
+
f"Transaction type: {action}, performed correctly on {self}"
|
38
|
+
)
|
37
39
|
except IntegrityError as err:
|
38
40
|
db.session.rollback()
|
39
41
|
current_app.logger.error(f"Integrity error on {action} data: {err}")
|
@@ -99,7 +101,10 @@ class EmptyBaseModel(db.Model):
|
|
99
101
|
action = "bulk create"
|
100
102
|
try:
|
101
103
|
db.session.commit()
|
102
|
-
current_app.logger.debug(
|
104
|
+
current_app.logger.debug(
|
105
|
+
f"Transaction type: {action}, performed correctly on {cls}"
|
106
|
+
)
|
107
|
+
|
103
108
|
except IntegrityError as err:
|
104
109
|
db.session.rollback()
|
105
110
|
current_app.logger.error(f"Integrity error on {action} data: {err}")
|
@@ -120,7 +125,10 @@ class EmptyBaseModel(db.Model):
|
|
120
125
|
action = "bulk create update"
|
121
126
|
try:
|
122
127
|
db.session.commit()
|
123
|
-
current_app.logger.debug(
|
128
|
+
current_app.logger.debug(
|
129
|
+
f"Transaction type: {action}, performed correctly on {cls}"
|
130
|
+
)
|
131
|
+
|
124
132
|
except IntegrityError as err:
|
125
133
|
db.session.rollback()
|
126
134
|
current_app.logger.error(f"Integrity error on {action} data: {err}")
|
@@ -136,12 +144,7 @@ class EmptyBaseModel(db.Model):
|
|
136
144
|
return instances
|
137
145
|
|
138
146
|
@classmethod
|
139
|
-
def get_all_objects(
|
140
|
-
cls,
|
141
|
-
offset=0,
|
142
|
-
limit=None,
|
143
|
-
**kwargs
|
144
|
-
):
|
147
|
+
def get_all_objects(cls, offset=0, limit=None, **kwargs):
|
145
148
|
"""
|
146
149
|
Method to get all the objects from the database applying the filters passed as keyword arguments
|
147
150
|
|
@@ -154,7 +157,9 @@ class EmptyBaseModel(db.Model):
|
|
154
157
|
"""
|
155
158
|
if "user" in kwargs:
|
156
159
|
kwargs.pop("user")
|
157
|
-
query = cls.query.filter_by(**kwargs)
|
160
|
+
query = cls.query.filter_by(**kwargs)
|
161
|
+
if offset:
|
162
|
+
query = query.offset(offset)
|
158
163
|
if limit:
|
159
164
|
query = query.limit(limit)
|
160
165
|
return query
|
@@ -208,8 +213,8 @@ class TraceAttributesModel(EmptyBaseModel):
|
|
208
213
|
deleted_at = db.Column(db.DateTime, nullable=True)
|
209
214
|
|
210
215
|
def __init__(self):
|
211
|
-
self.created_at = datetime.
|
212
|
-
self.updated_at = datetime.
|
216
|
+
self.created_at = datetime.now(timezone.utc)
|
217
|
+
self.updated_at = datetime.now(timezone.utc)
|
213
218
|
self.deleted_at = None
|
214
219
|
|
215
220
|
def update(self, data):
|
@@ -220,11 +225,11 @@ class TraceAttributesModel(EmptyBaseModel):
|
|
220
225
|
:return: None
|
221
226
|
:rtype: None
|
222
227
|
"""
|
223
|
-
self.updated_at = datetime.
|
228
|
+
self.updated_at = datetime.now(timezone.utc)
|
224
229
|
super().update(data)
|
225
230
|
|
226
231
|
def pre_update(self, data):
|
227
|
-
self.updated_at = datetime.
|
232
|
+
self.updated_at = datetime.now(timezone.utc)
|
228
233
|
super().pre_update(data)
|
229
234
|
|
230
235
|
def disable(self):
|
@@ -234,7 +239,7 @@ class TraceAttributesModel(EmptyBaseModel):
|
|
234
239
|
:return: None
|
235
240
|
:rtype: None
|
236
241
|
"""
|
237
|
-
self.deleted_at = datetime.
|
242
|
+
self.deleted_at = datetime.now(timezone.utc)
|
238
243
|
db.session.add(self)
|
239
244
|
self.commit_changes("disabling")
|
240
245
|
|
@@ -245,7 +250,7 @@ class TraceAttributesModel(EmptyBaseModel):
|
|
245
250
|
:return: None
|
246
251
|
:rtype: None
|
247
252
|
"""
|
248
|
-
self.updated_at = datetime.
|
253
|
+
self.updated_at = datetime.now(timezone.utc)
|
249
254
|
self.deleted_at = None
|
250
255
|
db.session.add(self)
|
251
256
|
self.commit_changes("activating")
|
@@ -261,7 +266,7 @@ class TraceAttributesModel(EmptyBaseModel):
|
|
261
266
|
update_date_lte=None,
|
262
267
|
offset=0,
|
263
268
|
limit=None,
|
264
|
-
**kwargs
|
269
|
+
**kwargs,
|
265
270
|
):
|
266
271
|
"""
|
267
272
|
Method to get all the objects from the database applying the filters passed as keyword arguments
|
@@ -300,7 +305,8 @@ class TraceAttributesModel(EmptyBaseModel):
|
|
300
305
|
if creation_date_lte:
|
301
306
|
query = query.filter(cls.created_at <= creation_date_lte)
|
302
307
|
|
303
|
-
|
308
|
+
if offset:
|
309
|
+
query = query.offset(offset)
|
304
310
|
if limit:
|
305
311
|
query = query.limit(limit)
|
306
312
|
return query
|
cornflow/models/user.py
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
"""
|
2
2
|
This file contains the UserModel
|
3
3
|
"""
|
4
|
+
|
4
5
|
# Imports from external libraries
|
5
6
|
import random
|
6
7
|
import string
|
7
|
-
from datetime import datetime
|
8
|
+
from datetime import datetime, timezone, timedelta
|
8
9
|
|
9
10
|
# Imports from internal modules
|
10
11
|
from cornflow.models.meta_models import TraceAttributesModel
|
@@ -55,9 +56,7 @@ class UserModel(TraceAttributesModel):
|
|
55
56
|
pwd_last_change = db.Column(db.DateTime, nullable=True)
|
56
57
|
email = db.Column(db.String(128), nullable=False, unique=True)
|
57
58
|
|
58
|
-
user_roles = db.relationship(
|
59
|
-
"UserRoleModel", cascade="all,delete", backref="users"
|
60
|
-
)
|
59
|
+
user_roles = db.relationship("UserRoleModel", cascade="all,delete", backref="users")
|
61
60
|
|
62
61
|
instances = db.relationship(
|
63
62
|
"InstanceModel",
|
@@ -95,15 +94,14 @@ class UserModel(TraceAttributesModel):
|
|
95
94
|
self.first_name = data.get("first_name")
|
96
95
|
self.last_name = data.get("last_name")
|
97
96
|
self.username = data.get("username")
|
98
|
-
self.pwd_last_change = datetime.
|
97
|
+
self.pwd_last_change = datetime.now(timezone.utc)
|
99
98
|
# TODO: handle better None passwords that can be found when using ldap
|
100
99
|
check_pass, msg = check_password_pattern(data.get("password"))
|
101
100
|
if check_pass:
|
102
101
|
self.password = self.__generate_hash(data.get("password"))
|
103
102
|
else:
|
104
103
|
raise InvalidCredentials(
|
105
|
-
msg,
|
106
|
-
log_txt="Error while trying to create a new user. " + msg
|
104
|
+
msg, log_txt="Error while trying to create a new user. " + msg
|
107
105
|
)
|
108
106
|
|
109
107
|
check_email, msg = check_email_pattern(data.get("email"))
|
@@ -111,8 +109,7 @@ class UserModel(TraceAttributesModel):
|
|
111
109
|
self.email = data.get("email")
|
112
110
|
else:
|
113
111
|
raise InvalidCredentials(
|
114
|
-
msg,
|
115
|
-
log_txt="Error while trying to create a new user. " + msg
|
112
|
+
msg, log_txt="Error while trying to create a new user. " + msg
|
116
113
|
)
|
117
114
|
|
118
115
|
def update(self, data):
|
@@ -126,7 +123,7 @@ class UserModel(TraceAttributesModel):
|
|
126
123
|
if new_password:
|
127
124
|
new_password = self.__generate_hash(new_password)
|
128
125
|
data["password"] = new_password
|
129
|
-
data["pwd_last_change"] = datetime.
|
126
|
+
data["pwd_last_change"] = datetime.now(timezone.utc)
|
130
127
|
super().update(data)
|
131
128
|
|
132
129
|
def comes_from_external_provider(self):
|
cornflow/schemas/alarms.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
"""
|
2
2
|
This file contains the schemas used for the table alarms defined in the application None
|
3
3
|
"""
|
4
|
+
|
4
5
|
from marshmallow import fields, Schema
|
5
6
|
|
6
7
|
|
@@ -18,3 +19,10 @@ class AlarmsResponse(AlarmsPostRequest):
|
|
18
19
|
class QueryFiltersAlarms(Schema):
|
19
20
|
schema = fields.Str(required=False)
|
20
21
|
criticality = fields.Number(required=False)
|
22
|
+
|
23
|
+
|
24
|
+
class AlarmEditRequest(Schema):
|
25
|
+
name = fields.Str(required=False)
|
26
|
+
criticality = fields.Number(required=False)
|
27
|
+
description = fields.Str(required=False)
|
28
|
+
schema = fields.Str(required=False)
|
cornflow/schemas/query.py
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
"""
|
2
2
|
This file contains the schemas used to query the results of a GET request
|
3
3
|
"""
|
4
|
+
|
4
5
|
from marshmallow import fields, Schema
|
5
6
|
|
6
7
|
|
7
8
|
class BaseQueryFilters(Schema):
|
8
9
|
"""This is the schema of the base query arguments"""
|
9
10
|
|
10
|
-
limit = fields.Int(required=False, dump_default=
|
11
|
+
limit = fields.Int(required=False, dump_default=10)
|
11
12
|
offset = fields.Int(required=False, dump_default=0)
|
12
13
|
creation_date_gte = fields.DateTime(required=False)
|
13
14
|
creation_date_lte = fields.DateTime(required=False)
|
cornflow/schemas/user.py
CHANGED
@@ -27,7 +27,7 @@ class UserEndpointResponse(Schema):
|
|
27
27
|
last_name = fields.Str()
|
28
28
|
email = fields.Str()
|
29
29
|
created_at = fields.Str()
|
30
|
-
pwd_last_change = fields.
|
30
|
+
pwd_last_change = fields.DateTime()
|
31
31
|
|
32
32
|
|
33
33
|
class UserDetailsEndpointResponse(Schema):
|
@@ -36,7 +36,7 @@ class UserDetailsEndpointResponse(Schema):
|
|
36
36
|
last_name = fields.Str()
|
37
37
|
username = fields.Str()
|
38
38
|
email = fields.Str()
|
39
|
-
pwd_last_change = fields.
|
39
|
+
pwd_last_change = fields.DateTime()
|
40
40
|
|
41
41
|
|
42
42
|
class TokenEndpointResponse(Schema):
|
@@ -53,7 +53,6 @@ class UserEditRequest(Schema):
|
|
53
53
|
last_name = fields.Str(required=False)
|
54
54
|
email = fields.Str(required=False)
|
55
55
|
password = fields.Str(required=False)
|
56
|
-
pwd_last_change = fields.DateTime(required=False)
|
57
56
|
|
58
57
|
|
59
58
|
class LoginEndpointRequest(Schema):
|
@@ -67,24 +66,10 @@ class LoginEndpointRequest(Schema):
|
|
67
66
|
|
68
67
|
class LoginOpenAuthRequest(Schema):
|
69
68
|
"""
|
70
|
-
|
71
|
-
Validates that either a token is provided, or both username and password are present
|
69
|
+
Schema for the login request with OpenID authentication
|
72
70
|
"""
|
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")
|
71
|
+
username = fields.String(required=False)
|
72
|
+
password = fields.String(required=False)
|
88
73
|
|
89
74
|
|
90
75
|
class SignupRequest(Schema):
|