apache-airflow-providers-amazon 8.19.0rc1__py3-none-any.whl → 8.20.0rc1__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.
Files changed (77) hide show
  1. airflow/providers/amazon/__init__.py +1 -1
  2. airflow/providers/amazon/aws/auth_manager/avp/entities.py +4 -2
  3. airflow/providers/amazon/aws/auth_manager/avp/facade.py +22 -7
  4. airflow/providers/amazon/aws/auth_manager/{cli → avp}/schema.json +34 -2
  5. airflow/providers/amazon/aws/auth_manager/aws_auth_manager.py +91 -170
  6. airflow/providers/amazon/aws/auth_manager/cli/avp_commands.py +7 -32
  7. airflow/providers/amazon/aws/auth_manager/cli/definition.py +1 -1
  8. airflow/providers/amazon/aws/auth_manager/cli/idc_commands.py +1 -0
  9. airflow/providers/amazon/aws/auth_manager/views/auth.py +1 -1
  10. airflow/providers/amazon/aws/executors/batch/__init__.py +16 -0
  11. airflow/providers/amazon/aws/executors/batch/batch_executor.py +420 -0
  12. airflow/providers/amazon/aws/executors/batch/batch_executor_config.py +87 -0
  13. airflow/providers/amazon/aws/executors/batch/boto_schema.py +67 -0
  14. airflow/providers/amazon/aws/executors/batch/utils.py +160 -0
  15. airflow/providers/amazon/aws/executors/ecs/ecs_executor.py +61 -18
  16. airflow/providers/amazon/aws/executors/ecs/utils.py +8 -13
  17. airflow/providers/amazon/aws/executors/utils/base_config_keys.py +25 -0
  18. airflow/providers/amazon/aws/hooks/athena.py +1 -0
  19. airflow/providers/amazon/aws/hooks/base_aws.py +1 -0
  20. airflow/providers/amazon/aws/hooks/batch_client.py +4 -3
  21. airflow/providers/amazon/aws/hooks/batch_waiters.py +1 -0
  22. airflow/providers/amazon/aws/hooks/bedrock.py +59 -0
  23. airflow/providers/amazon/aws/hooks/chime.py +1 -0
  24. airflow/providers/amazon/aws/hooks/cloud_formation.py +1 -0
  25. airflow/providers/amazon/aws/hooks/datasync.py +1 -0
  26. airflow/providers/amazon/aws/hooks/dynamodb.py +1 -0
  27. airflow/providers/amazon/aws/hooks/eks.py +1 -0
  28. airflow/providers/amazon/aws/hooks/glue.py +13 -5
  29. airflow/providers/amazon/aws/hooks/glue_catalog.py +1 -0
  30. airflow/providers/amazon/aws/hooks/kinesis.py +1 -0
  31. airflow/providers/amazon/aws/hooks/lambda_function.py +1 -0
  32. airflow/providers/amazon/aws/hooks/rds.py +1 -0
  33. airflow/providers/amazon/aws/hooks/s3.py +24 -30
  34. airflow/providers/amazon/aws/hooks/ses.py +1 -0
  35. airflow/providers/amazon/aws/hooks/sns.py +1 -0
  36. airflow/providers/amazon/aws/hooks/sqs.py +1 -0
  37. airflow/providers/amazon/aws/operators/athena.py +2 -2
  38. airflow/providers/amazon/aws/operators/base_aws.py +4 -1
  39. airflow/providers/amazon/aws/operators/batch.py +4 -2
  40. airflow/providers/amazon/aws/operators/bedrock.py +252 -0
  41. airflow/providers/amazon/aws/operators/cloud_formation.py +1 -0
  42. airflow/providers/amazon/aws/operators/datasync.py +1 -0
  43. airflow/providers/amazon/aws/operators/ecs.py +9 -10
  44. airflow/providers/amazon/aws/operators/eks.py +1 -0
  45. airflow/providers/amazon/aws/operators/emr.py +57 -7
  46. airflow/providers/amazon/aws/operators/s3.py +1 -0
  47. airflow/providers/amazon/aws/operators/sns.py +1 -0
  48. airflow/providers/amazon/aws/operators/sqs.py +1 -0
  49. airflow/providers/amazon/aws/secrets/secrets_manager.py +1 -0
  50. airflow/providers/amazon/aws/secrets/systems_manager.py +1 -0
  51. airflow/providers/amazon/aws/sensors/base_aws.py +4 -1
  52. airflow/providers/amazon/aws/sensors/bedrock.py +110 -0
  53. airflow/providers/amazon/aws/sensors/cloud_formation.py +1 -0
  54. airflow/providers/amazon/aws/sensors/eks.py +3 -4
  55. airflow/providers/amazon/aws/sensors/sqs.py +2 -1
  56. airflow/providers/amazon/aws/transfers/azure_blob_to_s3.py +4 -2
  57. airflow/providers/amazon/aws/transfers/base.py +1 -0
  58. airflow/providers/amazon/aws/transfers/exasol_to_s3.py +1 -0
  59. airflow/providers/amazon/aws/transfers/gcs_to_s3.py +1 -0
  60. airflow/providers/amazon/aws/transfers/google_api_to_s3.py +1 -0
  61. airflow/providers/amazon/aws/transfers/hive_to_dynamodb.py +1 -0
  62. airflow/providers/amazon/aws/transfers/http_to_s3.py +1 -0
  63. airflow/providers/amazon/aws/transfers/imap_attachment_to_s3.py +1 -0
  64. airflow/providers/amazon/aws/transfers/redshift_to_s3.py +21 -19
  65. airflow/providers/amazon/aws/triggers/bedrock.py +61 -0
  66. airflow/providers/amazon/aws/triggers/eks.py +1 -1
  67. airflow/providers/amazon/aws/triggers/redshift_cluster.py +1 -0
  68. airflow/providers/amazon/aws/triggers/s3.py +4 -2
  69. airflow/providers/amazon/aws/triggers/sagemaker.py +6 -4
  70. airflow/providers/amazon/aws/utils/emailer.py +1 -0
  71. airflow/providers/amazon/aws/waiters/bedrock.json +42 -0
  72. airflow/providers/amazon/get_provider_info.py +86 -1
  73. {apache_airflow_providers_amazon-8.19.0rc1.dist-info → apache_airflow_providers_amazon-8.20.0rc1.dist-info}/METADATA +11 -10
  74. {apache_airflow_providers_amazon-8.19.0rc1.dist-info → apache_airflow_providers_amazon-8.20.0rc1.dist-info}/RECORD +77 -66
  75. /airflow/providers/amazon/aws/executors/{ecs/Dockerfile → Dockerfile} +0 -0
  76. {apache_airflow_providers_amazon-8.19.0rc1.dist-info → apache_airflow_providers_amazon-8.20.0rc1.dist-info}/WHEEL +0 -0
  77. {apache_airflow_providers_amazon-8.19.0rc1.dist-info → apache_airflow_providers_amazon-8.20.0rc1.dist-info}/entry_points.txt +0 -0
@@ -27,7 +27,7 @@ import packaging.version
27
27
 
28
28
  __all__ = ["__version__"]
29
29
 
30
- __version__ = "8.19.0"
30
+ __version__ = "8.20.0"
31
31
 
32
32
  try:
33
33
  from airflow import __version__ as airflow_version
@@ -29,14 +29,16 @@ class AvpEntities(Enum):
29
29
  """Enum of Amazon Verified Permissions entities."""
30
30
 
31
31
  ACTION = "Action"
32
- ROLE = "Role"
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::Role, Airflow::Variable, Airflow::User.
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._get_user_role_entities(user)
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._get_user_role_entities(user)
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 _get_user_role_entities(user: AwsAuthManagerUser) -> list[dict]:
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.ROLE), "entityId": group}
245
+ {"entityType": get_entity_type(AvpEntities.GROUP), "entityId": group}
231
246
  for group in user.get_groups()
232
247
  ],
233
248
  }
234
- role_entities = [
235
- {"identifier": {"entityType": get_entity_type(AvpEntities.ROLE), "entityId": group}}
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, *role_entities]
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
- "Role": {},
193
+ "Group": {},
162
194
  "User": {
163
195
  "memberOfTypes": [
164
- "Role"
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 AirflowException, AirflowOptionalProviderFeatureException
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 self._has_access_to_menu_item(batch_is_authorized_results, requests[child.name], user):
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 self._has_access_to_menu_item(batch_is_authorized_results, requests[menu_item.name], user):
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(fab_resource_name: str) -> IsAuthorizedRequest:
508
- menu_item_request = _MENU_ITEM_REQUESTS.get(fab_resource_name)
509
- if menu_item_request:
510
- return menu_item_request
511
- else:
512
- raise AirflowException(f"Unknown resource name {fab_resource_name}")
513
-
514
- def _has_access_to_menu_item(
515
- self, batch_is_authorized_results: list[dict], request: IsAuthorizedRequest, user: AwsAuthManagerUser
516
- ):
517
- result = self.avp_facade.get_batch_is_authorized_single_result(
518
- batch_is_authorized_results=batch_is_authorized_results, request=request, user=user
519
- )
520
- return result["decision"] == "ALLOW"
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("Amazon Verified Permissions resources created successfully.")
67
- print("Please set them in Airflow configuration under AIRFLOW__AWS_AUTH_MANAGER__<config name>.")
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": "OFF",
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
- if args.verbose:
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
- if args.verbose:
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/cli/schema.json'",
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
  ),
@@ -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 logging
@@ -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.