apache-airflow-providers-amazon 8.19.0rc1__py3-none-any.whl → 8.20.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/providers/amazon/__init__.py +1 -1
- airflow/providers/amazon/aws/auth_manager/avp/entities.py +4 -2
- airflow/providers/amazon/aws/auth_manager/avp/facade.py +22 -7
- airflow/providers/amazon/aws/auth_manager/{cli → avp}/schema.json +34 -2
- airflow/providers/amazon/aws/auth_manager/aws_auth_manager.py +91 -170
- airflow/providers/amazon/aws/auth_manager/cli/avp_commands.py +7 -32
- airflow/providers/amazon/aws/auth_manager/cli/definition.py +1 -1
- airflow/providers/amazon/aws/auth_manager/cli/idc_commands.py +1 -0
- airflow/providers/amazon/aws/auth_manager/views/auth.py +1 -1
- airflow/providers/amazon/aws/executors/batch/__init__.py +16 -0
- airflow/providers/amazon/aws/executors/batch/batch_executor.py +420 -0
- airflow/providers/amazon/aws/executors/batch/batch_executor_config.py +87 -0
- airflow/providers/amazon/aws/executors/batch/boto_schema.py +67 -0
- airflow/providers/amazon/aws/executors/batch/utils.py +160 -0
- airflow/providers/amazon/aws/executors/ecs/ecs_executor.py +61 -18
- airflow/providers/amazon/aws/executors/ecs/utils.py +8 -13
- airflow/providers/amazon/aws/executors/utils/base_config_keys.py +25 -0
- airflow/providers/amazon/aws/hooks/athena.py +1 -0
- airflow/providers/amazon/aws/hooks/base_aws.py +1 -0
- airflow/providers/amazon/aws/hooks/batch_client.py +4 -3
- airflow/providers/amazon/aws/hooks/batch_waiters.py +1 -0
- airflow/providers/amazon/aws/hooks/bedrock.py +59 -0
- airflow/providers/amazon/aws/hooks/chime.py +1 -0
- airflow/providers/amazon/aws/hooks/cloud_formation.py +1 -0
- airflow/providers/amazon/aws/hooks/datasync.py +1 -0
- airflow/providers/amazon/aws/hooks/dynamodb.py +1 -0
- airflow/providers/amazon/aws/hooks/eks.py +1 -0
- airflow/providers/amazon/aws/hooks/glue.py +13 -5
- airflow/providers/amazon/aws/hooks/glue_catalog.py +1 -0
- airflow/providers/amazon/aws/hooks/kinesis.py +1 -0
- airflow/providers/amazon/aws/hooks/lambda_function.py +1 -0
- airflow/providers/amazon/aws/hooks/rds.py +1 -0
- airflow/providers/amazon/aws/hooks/s3.py +24 -30
- airflow/providers/amazon/aws/hooks/ses.py +1 -0
- airflow/providers/amazon/aws/hooks/sns.py +1 -0
- airflow/providers/amazon/aws/hooks/sqs.py +1 -0
- airflow/providers/amazon/aws/operators/athena.py +2 -2
- airflow/providers/amazon/aws/operators/base_aws.py +4 -1
- airflow/providers/amazon/aws/operators/batch.py +4 -2
- airflow/providers/amazon/aws/operators/bedrock.py +252 -0
- airflow/providers/amazon/aws/operators/cloud_formation.py +1 -0
- airflow/providers/amazon/aws/operators/datasync.py +1 -0
- airflow/providers/amazon/aws/operators/ecs.py +9 -10
- airflow/providers/amazon/aws/operators/eks.py +1 -0
- airflow/providers/amazon/aws/operators/emr.py +57 -7
- airflow/providers/amazon/aws/operators/s3.py +1 -0
- airflow/providers/amazon/aws/operators/sns.py +1 -0
- airflow/providers/amazon/aws/operators/sqs.py +1 -0
- airflow/providers/amazon/aws/secrets/secrets_manager.py +1 -0
- airflow/providers/amazon/aws/secrets/systems_manager.py +1 -0
- airflow/providers/amazon/aws/sensors/base_aws.py +4 -1
- airflow/providers/amazon/aws/sensors/bedrock.py +110 -0
- airflow/providers/amazon/aws/sensors/cloud_formation.py +1 -0
- airflow/providers/amazon/aws/sensors/eks.py +3 -4
- airflow/providers/amazon/aws/sensors/sqs.py +2 -1
- airflow/providers/amazon/aws/transfers/azure_blob_to_s3.py +4 -2
- airflow/providers/amazon/aws/transfers/base.py +1 -0
- airflow/providers/amazon/aws/transfers/exasol_to_s3.py +1 -0
- airflow/providers/amazon/aws/transfers/gcs_to_s3.py +1 -0
- airflow/providers/amazon/aws/transfers/google_api_to_s3.py +1 -0
- airflow/providers/amazon/aws/transfers/hive_to_dynamodb.py +1 -0
- airflow/providers/amazon/aws/transfers/http_to_s3.py +1 -0
- airflow/providers/amazon/aws/transfers/imap_attachment_to_s3.py +1 -0
- airflow/providers/amazon/aws/transfers/redshift_to_s3.py +21 -19
- airflow/providers/amazon/aws/triggers/bedrock.py +61 -0
- airflow/providers/amazon/aws/triggers/eks.py +1 -1
- airflow/providers/amazon/aws/triggers/redshift_cluster.py +1 -0
- airflow/providers/amazon/aws/triggers/s3.py +4 -2
- airflow/providers/amazon/aws/triggers/sagemaker.py +6 -4
- airflow/providers/amazon/aws/utils/emailer.py +1 -0
- airflow/providers/amazon/aws/waiters/bedrock.json +42 -0
- airflow/providers/amazon/get_provider_info.py +86 -1
- {apache_airflow_providers_amazon-8.19.0rc1.dist-info → apache_airflow_providers_amazon-8.20.0.dist-info}/METADATA +10 -9
- {apache_airflow_providers_amazon-8.19.0rc1.dist-info → apache_airflow_providers_amazon-8.20.0.dist-info}/RECORD +77 -66
- /airflow/providers/amazon/aws/executors/{ecs/Dockerfile → Dockerfile} +0 -0
- {apache_airflow_providers_amazon-8.19.0rc1.dist-info → apache_airflow_providers_amazon-8.20.0.dist-info}/WHEEL +0 -0
- {apache_airflow_providers_amazon-8.19.0rc1.dist-info → apache_airflow_providers_amazon-8.20.0.dist-info}/entry_points.txt +0 -0
@@ -29,14 +29,16 @@ class AvpEntities(Enum):
|
|
29
29
|
"""Enum of Amazon Verified Permissions entities."""
|
30
30
|
|
31
31
|
ACTION = "Action"
|
32
|
-
|
32
|
+
GROUP = "Group"
|
33
33
|
USER = "User"
|
34
34
|
|
35
35
|
# Resource types
|
36
36
|
CONFIGURATION = "Configuration"
|
37
37
|
CONNECTION = "Connection"
|
38
|
+
CUSTOM = "Custom"
|
38
39
|
DAG = "Dag"
|
39
40
|
DATASET = "Dataset"
|
41
|
+
MENU = "Menu"
|
40
42
|
POOL = "Pool"
|
41
43
|
VARIABLE = "Variable"
|
42
44
|
VIEW = "View"
|
@@ -48,7 +50,7 @@ def get_entity_type(resource_type: AvpEntities) -> str:
|
|
48
50
|
|
49
51
|
:param resource_type: Resource type.
|
50
52
|
|
51
|
-
Example: Airflow::Action, Airflow::
|
53
|
+
Example: Airflow::Action, Airflow::Group, Airflow::Variable, Airflow::User.
|
52
54
|
"""
|
53
55
|
return AVP_PREFIX_ENTITIES + resource_type.value
|
54
56
|
|
@@ -16,7 +16,9 @@
|
|
16
16
|
# under the License.
|
17
17
|
from __future__ import annotations
|
18
18
|
|
19
|
+
import json
|
19
20
|
from functools import cached_property
|
21
|
+
from pathlib import Path
|
20
22
|
from typing import TYPE_CHECKING, Sequence, TypedDict
|
21
23
|
|
22
24
|
from airflow.configuration import conf
|
@@ -94,7 +96,7 @@ class AwsAuthManagerAmazonVerifiedPermissionsFacade(LoggingMixin):
|
|
94
96
|
if user is None:
|
95
97
|
return False
|
96
98
|
|
97
|
-
entity_list = self.
|
99
|
+
entity_list = self._get_user_group_entities(user)
|
98
100
|
|
99
101
|
self.log.debug(
|
100
102
|
"Making authorization request for user=%s, method=%s, entity_type=%s, entity_id=%s",
|
@@ -144,7 +146,7 @@ class AwsAuthManagerAmazonVerifiedPermissionsFacade(LoggingMixin):
|
|
144
146
|
:param requests: the list of requests containing the method, the entity_type and the entity ID
|
145
147
|
:param user: the user
|
146
148
|
"""
|
147
|
-
entity_list = self.
|
149
|
+
entity_list = self._get_user_group_entities(user)
|
148
150
|
|
149
151
|
self.log.debug("Making batch authorization request for user=%s, requests=%s", user.get_id(), requests)
|
150
152
|
|
@@ -222,20 +224,33 @@ class AwsAuthManagerAmazonVerifiedPermissionsFacade(LoggingMixin):
|
|
222
224
|
)
|
223
225
|
raise AirflowException("Could not find the authorization result.")
|
224
226
|
|
227
|
+
def is_policy_store_schema_up_to_date(self) -> bool:
|
228
|
+
"""Return whether the policy store schema equals the latest version of the schema."""
|
229
|
+
resp = self.avp_client.get_schema(
|
230
|
+
policyStoreId=self.avp_policy_store_id,
|
231
|
+
)
|
232
|
+
policy_store_schema = json.loads(resp["schema"])
|
233
|
+
|
234
|
+
schema_path = Path(__file__).parents[0] / "schema.json"
|
235
|
+
with open(schema_path) as schema_file:
|
236
|
+
latest_schema = json.load(schema_file)
|
237
|
+
|
238
|
+
return policy_store_schema == latest_schema
|
239
|
+
|
225
240
|
@staticmethod
|
226
|
-
def
|
241
|
+
def _get_user_group_entities(user: AwsAuthManagerUser) -> list[dict]:
|
227
242
|
user_entity = {
|
228
243
|
"identifier": {"entityType": get_entity_type(AvpEntities.USER), "entityId": user.get_id()},
|
229
244
|
"parents": [
|
230
|
-
{"entityType": get_entity_type(AvpEntities.
|
245
|
+
{"entityType": get_entity_type(AvpEntities.GROUP), "entityId": group}
|
231
246
|
for group in user.get_groups()
|
232
247
|
],
|
233
248
|
}
|
234
|
-
|
235
|
-
{"identifier": {"entityType": get_entity_type(AvpEntities.
|
249
|
+
group_entities = [
|
250
|
+
{"identifier": {"entityType": get_entity_type(AvpEntities.GROUP), "entityId": group}}
|
236
251
|
for group in user.get_groups()
|
237
252
|
]
|
238
|
-
return [user_entity, *
|
253
|
+
return [user_entity, *group_entities]
|
239
254
|
|
240
255
|
@staticmethod
|
241
256
|
def _build_context(context: dict | None) -> dict | None:
|
@@ -25,6 +25,30 @@
|
|
25
25
|
"resourceTypes": ["Connection"]
|
26
26
|
}
|
27
27
|
},
|
28
|
+
"Custom.DELETE": {
|
29
|
+
"appliesTo": {
|
30
|
+
"principalTypes": ["User"],
|
31
|
+
"resourceTypes": ["Custom"]
|
32
|
+
}
|
33
|
+
},
|
34
|
+
"Custom.GET": {
|
35
|
+
"appliesTo": {
|
36
|
+
"principalTypes": ["User"],
|
37
|
+
"resourceTypes": ["Custom"]
|
38
|
+
}
|
39
|
+
},
|
40
|
+
"Custom.POST": {
|
41
|
+
"appliesTo": {
|
42
|
+
"principalTypes": ["User"],
|
43
|
+
"resourceTypes": ["Custom"]
|
44
|
+
}
|
45
|
+
},
|
46
|
+
"Custom.PUT": {
|
47
|
+
"appliesTo": {
|
48
|
+
"principalTypes": ["User"],
|
49
|
+
"resourceTypes": ["Custom"]
|
50
|
+
}
|
51
|
+
},
|
28
52
|
"Configuration.GET": {
|
29
53
|
"appliesTo": {
|
30
54
|
"principalTypes": ["User"],
|
@@ -97,6 +121,12 @@
|
|
97
121
|
"resourceTypes": ["Dataset"]
|
98
122
|
}
|
99
123
|
},
|
124
|
+
"Menu.MENU": {
|
125
|
+
"appliesTo": {
|
126
|
+
"principalTypes": ["User"],
|
127
|
+
"resourceTypes": ["Menu"]
|
128
|
+
}
|
129
|
+
},
|
100
130
|
"Pool.DELETE": {
|
101
131
|
"appliesTo": {
|
102
132
|
"principalTypes": ["User"],
|
@@ -155,13 +185,15 @@
|
|
155
185
|
"entityTypes": {
|
156
186
|
"Configuration": {},
|
157
187
|
"Connection": {},
|
188
|
+
"Custom": {},
|
158
189
|
"Dag": {},
|
159
190
|
"Dataset": {},
|
191
|
+
"Menu": {},
|
160
192
|
"Pool": {},
|
161
|
-
"
|
193
|
+
"Group": {},
|
162
194
|
"User": {
|
163
195
|
"memberOfTypes": [
|
164
|
-
"
|
196
|
+
"Group"
|
165
197
|
]
|
166
198
|
},
|
167
199
|
"Variable": {},
|
@@ -17,14 +17,15 @@
|
|
17
17
|
from __future__ import annotations
|
18
18
|
|
19
19
|
import argparse
|
20
|
+
from collections import defaultdict
|
20
21
|
from functools import cached_property
|
21
|
-
from typing import TYPE_CHECKING, Sequence, cast
|
22
|
+
from typing import TYPE_CHECKING, Container, Sequence, cast
|
22
23
|
|
23
24
|
from flask import session, url_for
|
24
25
|
|
25
26
|
from airflow.cli.cli_config import CLICommand, DefaultHelpParser, GroupCommand
|
26
27
|
from airflow.configuration import conf
|
27
|
-
from airflow.exceptions import
|
28
|
+
from airflow.exceptions import AirflowOptionalProviderFeatureException
|
28
29
|
from airflow.providers.amazon.aws.auth_manager.avp.entities import AvpEntities
|
29
30
|
from airflow.providers.amazon.aws.auth_manager.avp.facade import (
|
30
31
|
AwsAuthManagerAmazonVerifiedPermissionsFacade,
|
@@ -40,28 +41,6 @@ from airflow.providers.amazon.aws.auth_manager.constants import (
|
|
40
41
|
from airflow.providers.amazon.aws.auth_manager.security_manager.aws_security_manager_override import (
|
41
42
|
AwsSecurityManagerOverride,
|
42
43
|
)
|
43
|
-
from airflow.security.permissions import (
|
44
|
-
RESOURCE_AUDIT_LOG,
|
45
|
-
RESOURCE_CLUSTER_ACTIVITY,
|
46
|
-
RESOURCE_CONFIG,
|
47
|
-
RESOURCE_CONNECTION,
|
48
|
-
RESOURCE_DAG,
|
49
|
-
RESOURCE_DAG_CODE,
|
50
|
-
RESOURCE_DAG_DEPENDENCIES,
|
51
|
-
RESOURCE_DAG_RUN,
|
52
|
-
RESOURCE_DATASET,
|
53
|
-
RESOURCE_DOCS,
|
54
|
-
RESOURCE_JOB,
|
55
|
-
RESOURCE_PLUGIN,
|
56
|
-
RESOURCE_POOL,
|
57
|
-
RESOURCE_PROVIDER,
|
58
|
-
RESOURCE_SLA_MISS,
|
59
|
-
RESOURCE_TASK_INSTANCE,
|
60
|
-
RESOURCE_TASK_RESCHEDULE,
|
61
|
-
RESOURCE_TRIGGER,
|
62
|
-
RESOURCE_VARIABLE,
|
63
|
-
RESOURCE_XCOM,
|
64
|
-
)
|
65
44
|
|
66
45
|
try:
|
67
46
|
from airflow.auth.managers.base_auth_manager import BaseAuthManager, ResourceMethod
|
@@ -96,136 +75,6 @@ if TYPE_CHECKING:
|
|
96
75
|
from airflow.www.extensions.init_appbuilder import AirflowAppBuilder
|
97
76
|
|
98
77
|
|
99
|
-
_MENU_ITEM_REQUESTS: dict[str, IsAuthorizedRequest] = {
|
100
|
-
RESOURCE_AUDIT_LOG: {
|
101
|
-
"method": "GET",
|
102
|
-
"entity_type": AvpEntities.DAG,
|
103
|
-
"context": {
|
104
|
-
"dag_entity": {
|
105
|
-
"string": DagAccessEntity.AUDIT_LOG.value,
|
106
|
-
},
|
107
|
-
},
|
108
|
-
},
|
109
|
-
RESOURCE_CLUSTER_ACTIVITY: {
|
110
|
-
"method": "GET",
|
111
|
-
"entity_type": AvpEntities.VIEW,
|
112
|
-
"entity_id": AccessView.CLUSTER_ACTIVITY.value,
|
113
|
-
},
|
114
|
-
RESOURCE_CONFIG: {
|
115
|
-
"method": "GET",
|
116
|
-
"entity_type": AvpEntities.CONFIGURATION,
|
117
|
-
},
|
118
|
-
RESOURCE_CONNECTION: {
|
119
|
-
"method": "GET",
|
120
|
-
"entity_type": AvpEntities.CONNECTION,
|
121
|
-
},
|
122
|
-
RESOURCE_DAG: {
|
123
|
-
"method": "GET",
|
124
|
-
"entity_type": AvpEntities.DAG,
|
125
|
-
},
|
126
|
-
RESOURCE_DAG_CODE: {
|
127
|
-
"method": "GET",
|
128
|
-
"entity_type": AvpEntities.DAG,
|
129
|
-
"context": {
|
130
|
-
"dag_entity": {
|
131
|
-
"string": DagAccessEntity.CODE.value,
|
132
|
-
},
|
133
|
-
},
|
134
|
-
},
|
135
|
-
RESOURCE_DAG_DEPENDENCIES: {
|
136
|
-
"method": "GET",
|
137
|
-
"entity_type": AvpEntities.DAG,
|
138
|
-
"context": {
|
139
|
-
"dag_entity": {
|
140
|
-
"string": DagAccessEntity.DEPENDENCIES.value,
|
141
|
-
},
|
142
|
-
},
|
143
|
-
},
|
144
|
-
RESOURCE_DAG_RUN: {
|
145
|
-
"method": "GET",
|
146
|
-
"entity_type": AvpEntities.DAG,
|
147
|
-
"context": {
|
148
|
-
"dag_entity": {
|
149
|
-
"string": DagAccessEntity.RUN.value,
|
150
|
-
},
|
151
|
-
},
|
152
|
-
},
|
153
|
-
RESOURCE_DATASET: {
|
154
|
-
"method": "GET",
|
155
|
-
"entity_type": AvpEntities.DATASET,
|
156
|
-
},
|
157
|
-
RESOURCE_DOCS: {
|
158
|
-
"method": "GET",
|
159
|
-
"entity_type": AvpEntities.VIEW,
|
160
|
-
"entity_id": AccessView.DOCS.value,
|
161
|
-
},
|
162
|
-
RESOURCE_PLUGIN: {
|
163
|
-
"method": "GET",
|
164
|
-
"entity_type": AvpEntities.VIEW,
|
165
|
-
"entity_id": AccessView.PLUGINS.value,
|
166
|
-
},
|
167
|
-
RESOURCE_JOB: {
|
168
|
-
"method": "GET",
|
169
|
-
"entity_type": AvpEntities.VIEW,
|
170
|
-
"entity_id": AccessView.JOBS.value,
|
171
|
-
},
|
172
|
-
RESOURCE_POOL: {
|
173
|
-
"method": "GET",
|
174
|
-
"entity_type": AvpEntities.POOL,
|
175
|
-
},
|
176
|
-
RESOURCE_PROVIDER: {
|
177
|
-
"method": "GET",
|
178
|
-
"entity_type": AvpEntities.VIEW,
|
179
|
-
"entity_id": AccessView.PROVIDERS.value,
|
180
|
-
},
|
181
|
-
RESOURCE_SLA_MISS: {
|
182
|
-
"method": "GET",
|
183
|
-
"entity_type": AvpEntities.DAG,
|
184
|
-
"context": {
|
185
|
-
"dag_entity": {
|
186
|
-
"string": DagAccessEntity.SLA_MISS.value,
|
187
|
-
},
|
188
|
-
},
|
189
|
-
},
|
190
|
-
RESOURCE_TASK_INSTANCE: {
|
191
|
-
"method": "GET",
|
192
|
-
"entity_type": AvpEntities.DAG,
|
193
|
-
"context": {
|
194
|
-
"dag_entity": {
|
195
|
-
"string": DagAccessEntity.TASK_INSTANCE.value,
|
196
|
-
},
|
197
|
-
},
|
198
|
-
},
|
199
|
-
RESOURCE_TASK_RESCHEDULE: {
|
200
|
-
"method": "GET",
|
201
|
-
"entity_type": AvpEntities.DAG,
|
202
|
-
"context": {
|
203
|
-
"dag_entity": {
|
204
|
-
"string": DagAccessEntity.TASK_RESCHEDULE.value,
|
205
|
-
},
|
206
|
-
},
|
207
|
-
},
|
208
|
-
RESOURCE_TRIGGER: {
|
209
|
-
"method": "GET",
|
210
|
-
"entity_type": AvpEntities.VIEW,
|
211
|
-
"entity_id": AccessView.TRIGGERS.value,
|
212
|
-
},
|
213
|
-
RESOURCE_VARIABLE: {
|
214
|
-
"method": "GET",
|
215
|
-
"entity_type": AvpEntities.VARIABLE,
|
216
|
-
},
|
217
|
-
RESOURCE_XCOM: {
|
218
|
-
"method": "GET",
|
219
|
-
"entity_type": AvpEntities.DAG,
|
220
|
-
"context": {
|
221
|
-
"dag_entity": {
|
222
|
-
"string": DagAccessEntity.XCOM.value,
|
223
|
-
},
|
224
|
-
},
|
225
|
-
},
|
226
|
-
}
|
227
|
-
|
228
|
-
|
229
78
|
class AwsAuthManager(BaseAuthManager):
|
230
79
|
"""
|
231
80
|
AWS auth manager.
|
@@ -239,6 +88,7 @@ class AwsAuthManager(BaseAuthManager):
|
|
239
88
|
def __init__(self, appbuilder: AirflowAppBuilder) -> None:
|
240
89
|
super().__init__(appbuilder)
|
241
90
|
enable = conf.getboolean(CONF_SECTION_NAME, CONF_ENABLE_KEY)
|
91
|
+
self._check_avp_schema_version()
|
242
92
|
if not enable:
|
243
93
|
raise NotImplementedError(
|
244
94
|
"The AWS auth manager is currently being built. It is not finalized. It is not intended to be used yet."
|
@@ -356,6 +206,16 @@ class AwsAuthManager(BaseAuthManager):
|
|
356
206
|
entity_id=access_view.value,
|
357
207
|
)
|
358
208
|
|
209
|
+
def is_authorized_custom_view(
|
210
|
+
self, *, method: ResourceMethod, resource_name: str, user: BaseUser | None = None
|
211
|
+
):
|
212
|
+
return self.avp_facade.is_authorized(
|
213
|
+
method=method,
|
214
|
+
entity_type=AvpEntities.CUSTOM,
|
215
|
+
user=user or self.get_user(),
|
216
|
+
entity_id=resource_name,
|
217
|
+
)
|
218
|
+
|
359
219
|
def batch_is_authorized_connection(
|
360
220
|
self,
|
361
221
|
requests: Sequence[IsAuthorizedConnectionRequest],
|
@@ -443,6 +303,60 @@ class AwsAuthManager(BaseAuthManager):
|
|
443
303
|
]
|
444
304
|
return self.avp_facade.batch_is_authorized(requests=facade_requests, user=self.get_user())
|
445
305
|
|
306
|
+
def filter_permitted_dag_ids(
|
307
|
+
self,
|
308
|
+
*,
|
309
|
+
dag_ids: set[str],
|
310
|
+
methods: Container[ResourceMethod] | None = None,
|
311
|
+
user=None,
|
312
|
+
):
|
313
|
+
"""
|
314
|
+
Filter readable or writable DAGs for user.
|
315
|
+
|
316
|
+
:param dag_ids: the list of DAG ids
|
317
|
+
:param methods: whether filter readable or writable
|
318
|
+
:param user: the current user
|
319
|
+
"""
|
320
|
+
if not methods:
|
321
|
+
methods = ["PUT", "GET"]
|
322
|
+
|
323
|
+
if not user:
|
324
|
+
user = self.get_user()
|
325
|
+
|
326
|
+
requests: dict[str, dict[ResourceMethod, IsAuthorizedRequest]] = defaultdict(dict)
|
327
|
+
requests_list: list[IsAuthorizedRequest] = []
|
328
|
+
for dag_id in dag_ids:
|
329
|
+
for method in ["GET", "PUT"]:
|
330
|
+
if method in methods:
|
331
|
+
request: IsAuthorizedRequest = {
|
332
|
+
"method": cast(ResourceMethod, method),
|
333
|
+
"entity_type": AvpEntities.DAG,
|
334
|
+
"entity_id": dag_id,
|
335
|
+
}
|
336
|
+
requests[dag_id][cast(ResourceMethod, method)] = request
|
337
|
+
requests_list.append(request)
|
338
|
+
|
339
|
+
batch_is_authorized_results = self.avp_facade.get_batch_is_authorized_results(
|
340
|
+
requests=requests_list, user=user
|
341
|
+
)
|
342
|
+
|
343
|
+
def _has_access_to_dag(request: IsAuthorizedRequest):
|
344
|
+
result = self.avp_facade.get_batch_is_authorized_single_result(
|
345
|
+
batch_is_authorized_results=batch_is_authorized_results, request=request, user=user
|
346
|
+
)
|
347
|
+
return result["decision"] == "ALLOW"
|
348
|
+
|
349
|
+
return {
|
350
|
+
dag_id
|
351
|
+
for dag_id in dag_ids
|
352
|
+
if (
|
353
|
+
"GET" in methods
|
354
|
+
and _has_access_to_dag(requests[dag_id]["GET"])
|
355
|
+
or "PUT" in methods
|
356
|
+
and _has_access_to_dag(requests[dag_id]["PUT"])
|
357
|
+
)
|
358
|
+
}
|
359
|
+
|
446
360
|
def filter_permitted_menu_items(self, menu_items: list[MenuItem]) -> list[MenuItem]:
|
447
361
|
"""
|
448
362
|
Filter menu items based on user permissions.
|
@@ -465,19 +379,25 @@ class AwsAuthManager(BaseAuthManager):
|
|
465
379
|
requests=list(requests.values()), user=user
|
466
380
|
)
|
467
381
|
|
382
|
+
def _has_access_to_menu_item(request: IsAuthorizedRequest):
|
383
|
+
result = self.avp_facade.get_batch_is_authorized_single_result(
|
384
|
+
batch_is_authorized_results=batch_is_authorized_results, request=request, user=user
|
385
|
+
)
|
386
|
+
return result["decision"] == "ALLOW"
|
387
|
+
|
468
388
|
accessible_items = []
|
469
389
|
for menu_item in menu_items:
|
470
390
|
if menu_item.childs:
|
471
391
|
accessible_children = []
|
472
392
|
for child in menu_item.childs:
|
473
|
-
if
|
393
|
+
if _has_access_to_menu_item(requests[child.name]):
|
474
394
|
accessible_children.append(child)
|
475
395
|
menu_item.childs = accessible_children
|
476
396
|
|
477
397
|
# Display the menu if the user has access to at least one sub item
|
478
398
|
if len(accessible_children) > 0:
|
479
399
|
accessible_items.append(menu_item)
|
480
|
-
elif
|
400
|
+
elif _has_access_to_menu_item(requests[menu_item.name]):
|
481
401
|
accessible_items.append(menu_item)
|
482
402
|
|
483
403
|
return accessible_items
|
@@ -504,20 +424,21 @@ class AwsAuthManager(BaseAuthManager):
|
|
504
424
|
]
|
505
425
|
|
506
426
|
@staticmethod
|
507
|
-
def _get_menu_item_request(
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
def
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
427
|
+
def _get_menu_item_request(resource_name: str) -> IsAuthorizedRequest:
|
428
|
+
return {
|
429
|
+
"method": "MENU",
|
430
|
+
"entity_type": AvpEntities.MENU,
|
431
|
+
"entity_id": resource_name,
|
432
|
+
}
|
433
|
+
|
434
|
+
def _check_avp_schema_version(self):
|
435
|
+
if not self.avp_facade.is_policy_store_schema_up_to_date():
|
436
|
+
self.log.warning(
|
437
|
+
"The Amazon Verified Permissions policy store schema is different from the latest version "
|
438
|
+
"(https://github.com/apache/airflow/blob/main/airflow/providers/amazon/aws/auth_manager/avp/schema.json). "
|
439
|
+
"Please update it to its latest version. "
|
440
|
+
"See doc: https://airflow.apache.org/docs/apache-airflow-providers-amazon/stable/auth-manager/setup/amazon-verified-permissions.html#update-the-policy-store-schema."
|
441
|
+
)
|
521
442
|
|
522
443
|
|
523
444
|
def get_parser() -> argparse.ArgumentParser:
|
@@ -15,6 +15,7 @@
|
|
15
15
|
# specific language governing permissions and limitations
|
16
16
|
# under the License.
|
17
17
|
"""User sub-commands."""
|
18
|
+
|
18
19
|
from __future__ import annotations
|
19
20
|
|
20
21
|
import json
|
@@ -63,8 +64,9 @@ def init_avp(args):
|
|
63
64
|
_set_schema(client, policy_store_id, args)
|
64
65
|
|
65
66
|
if not args.dry_run:
|
66
|
-
print(
|
67
|
-
|
67
|
+
print(
|
68
|
+
"Please set configs below in Airflow configuration under AIRFLOW__AWS_AUTH_MANAGER__<config name>."
|
69
|
+
)
|
68
70
|
print(json.dumps({"avp_policy_store_id": policy_store_id}, indent=4))
|
69
71
|
|
70
72
|
|
@@ -75,9 +77,6 @@ def update_schema(args):
|
|
75
77
|
client = _get_client()
|
76
78
|
_set_schema(client, args.policy_store_id, args)
|
77
79
|
|
78
|
-
if not args.dry_run:
|
79
|
-
print("Amazon Verified Permissions policy store schema updated successfully.")
|
80
|
-
|
81
80
|
|
82
81
|
def _get_client():
|
83
82
|
"""Return Amazon Verified Permissions client."""
|
@@ -121,7 +120,7 @@ def _create_policy_store(client: BaseClient, args) -> tuple[str | None, bool]:
|
|
121
120
|
|
122
121
|
response = client.create_policy_store(
|
123
122
|
validationSettings={
|
124
|
-
"mode": "
|
123
|
+
"mode": "STRICT",
|
125
124
|
},
|
126
125
|
description=args.policy_store_description,
|
127
126
|
)
|
@@ -139,20 +138,7 @@ def _set_schema(client: BaseClient, policy_store_id: str, args) -> None:
|
|
139
138
|
print(f"Dry run, not updating the schema of the policy store with ID '{policy_store_id}'.")
|
140
139
|
return
|
141
140
|
|
142
|
-
|
143
|
-
log.debug("Disabling schema validation before updating schema")
|
144
|
-
|
145
|
-
response = client.update_policy_store(
|
146
|
-
policyStoreId=policy_store_id,
|
147
|
-
validationSettings={
|
148
|
-
"mode": "OFF",
|
149
|
-
},
|
150
|
-
)
|
151
|
-
|
152
|
-
if args.verbose:
|
153
|
-
log.debug("Response from update_policy_store: %s", response)
|
154
|
-
|
155
|
-
schema_path = Path(__file__).parents[0].joinpath("schema.json").resolve()
|
141
|
+
schema_path = Path(__file__).parents[1] / "avp" / "schema.json"
|
156
142
|
with open(schema_path) as schema_file:
|
157
143
|
response = client.put_schema(
|
158
144
|
policyStoreId=policy_store_id,
|
@@ -164,15 +150,4 @@ def _set_schema(client: BaseClient, policy_store_id: str, args) -> None:
|
|
164
150
|
if args.verbose:
|
165
151
|
log.debug("Response from put_schema: %s", response)
|
166
152
|
|
167
|
-
|
168
|
-
log.debug("Enabling schema validation after updating schema")
|
169
|
-
|
170
|
-
response = client.update_policy_store(
|
171
|
-
policyStoreId=policy_store_id,
|
172
|
-
validationSettings={
|
173
|
-
"mode": "STRICT",
|
174
|
-
},
|
175
|
-
)
|
176
|
-
|
177
|
-
if args.verbose:
|
178
|
-
log.debug("Response from update_policy_store: %s", response)
|
153
|
+
print("Policy store schema updated.")
|
@@ -69,7 +69,7 @@ AWS_AUTH_MANAGER_COMMANDS = (
|
|
69
69
|
),
|
70
70
|
ActionCommand(
|
71
71
|
name="update-avp-schema",
|
72
|
-
help="Update Amazon Verified permissions policy store schema to the latest version in 'airflow/providers/amazon/aws/auth_manager/
|
72
|
+
help="Update Amazon Verified permissions policy store schema to the latest version in 'airflow/providers/amazon/aws/auth_manager/avp/schema.json'",
|
73
73
|
func=lazy_load_command("airflow.providers.amazon.aws.auth_manager.cli.avp_commands.update_schema"),
|
74
74
|
args=(ARG_POLICY_STORE_ID, ARG_DRY_RUN, ARG_VERBOSE),
|
75
75
|
),
|
@@ -93,7 +93,7 @@ class AwsAuthManagerAuthenticationViews(AirflowBaseView):
|
|
93
93
|
user_id=attributes["id"][0],
|
94
94
|
groups=attributes["groups"],
|
95
95
|
username=saml_auth.get_nameid(),
|
96
|
-
email=attributes["email"][0],
|
96
|
+
email=attributes["email"][0] if "email" in attributes else None,
|
97
97
|
)
|
98
98
|
session["aws_user"] = user
|
99
99
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
2
|
+
# or more contributor license agreements. See the NOTICE file
|
3
|
+
# distributed with this work for additional information
|
4
|
+
# regarding copyright ownership. The ASF licenses this file
|
5
|
+
# to you under the Apache License, Version 2.0 (the
|
6
|
+
# "License"); you may not use this file except in compliance
|
7
|
+
# with the License. You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|