Skip to content

Commit 54200fd

Browse files
authored
Merge pull request #18 from IanMeyers/main
Version 1.0.16
2 parents 165aec4 + 489153d commit 54200fd

File tree

6 files changed

+116
-24
lines changed

6 files changed

+116
-24
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ __pycache__
33
dist
44
.DS_Store
55
*egg-info*
6+
.DS_Store

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = aws-data-mesh-utils
3-
version = 1.0.15
3+
version = 1.0.16
44
author = Ian Meyers
55
author_email = [email protected]
66
license = Apache 2.0

src/data_mesh_util/DataMeshAdmin.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import sys
33
import logging
44
import botocore.session
5+
import json
56

67
from data_mesh_util.lib.constants import *
78
import data_mesh_util.lib.utils as utils
@@ -202,6 +203,51 @@ def _api_tuple(self, item_tuple: tuple):
202203
"GroupArn": item_tuple[2]
203204
}
204205

206+
def _allow_glue_ram_integration(self):
207+
# update the glue resource policy to allow RAM integration
208+
cf = {
209+
"region": self._region,
210+
"account_id": self._data_mesh_account_id
211+
}
212+
optin_fragment = json.loads(utils.generate_policy('glue_ram_optin.pystache', config=cf))
213+
214+
current_policy, current_hash = self._automator.get_current_glue_policy()
215+
216+
if current_policy is not None:
217+
# scan the existing glue policy to see if we've already added the clause
218+
optin_found = False
219+
for s in current_policy.get('Statement'):
220+
# break early if we see a condition - this is a tbac statement
221+
if 'Condition' in s:
222+
continue
223+
224+
if s.get('Action') == 'glue:ShareResource' and 'Service' in s.get('Principal') and \
225+
s.get('Principal').get('Service') == 'ram.amazonaws.com':
226+
optin_found = True
227+
self._logger.info("Already found Glue/RAM optin policy for LF sharing")
228+
break
229+
230+
# add the optin fragment if it's not already there
231+
if not optin_found:
232+
current_policy['Statement'].append(optin_fragment)
233+
234+
# update the policy
235+
self._automator.write_glue_catalog_resource_policy(
236+
policy=current_policy,
237+
current_hash=current_hash
238+
)
239+
self._logger.info("Created new Glue/RAM optin policy for LF sharing")
240+
else:
241+
# write a new policy
242+
glue_policy = {
243+
"Version": "2012-10-17",
244+
"Statement": optin_fragment
245+
}
246+
self._automator.write_glue_catalog_resource_policy(
247+
policy=glue_policy
248+
)
249+
self._logger.info("Created new Glue/RAM optin policy for LF sharing")
250+
205251
def initialize_mesh_account(self):
206252
'''
207253
Sets up an AWS Account to act as a Data Mesh central account. This method should be invoked by an Administrator
@@ -222,6 +268,9 @@ def initialize_mesh_account(self):
222268
# create a new IAM role in the Data Mesh Account to be used for future grants
223269
mgr_tuple = self._create_data_mesh_manager_role()
224270

271+
# setup the account to allow glue:ShareResource through RAM
272+
self._allow_glue_ram_integration()
273+
225274
return {
226275
"Manager": self._api_tuple(mgr_tuple),
227276
"ReadOnly": self._api_tuple(ro_tuple),

src/data_mesh_util/DataMeshProducer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,7 @@ def approve_access_request(self, request_id: str,
538538
)
539539

540540
def _add_principal_to_glue_resource_policy(self, database_name: str, tables: list, add_principal: str):
541-
self._mesh_automator.update_glue_catalog_resource_policy(
541+
self._mesh_automator.add_tbac_glue_catalog_resource_policy(
542542
region=self._current_region,
543543
database_name=database_name,
544544
tables=tables,

src/data_mesh_util/lib/ApiAutomator.py

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -520,15 +520,52 @@ def _no_data():
520520

521521
return all_tables
522522

523-
def update_glue_catalog_resource_policy(self, region: str, producer_account_id: str, consumer_account_id: str,
524-
database_name: str, tables: list):
523+
def write_glue_catalog_resource_policy(self, policy: dict, current_hash: str = None):
524+
'''
525+
Write a new glue catalog policy document. This is a low level interface that just performs the mechanic of
526+
correctly writing the supplied policy, including where a hash must be supplied.
527+
:param policy:
528+
:param current_hash:
529+
:return:
530+
'''
525531
glue_client = self._get_client('glue')
526-
new_resource_policy = None
527-
current_resource_policy = None
532+
528533
try:
529534
current_resource_policy = glue_client.get_resource_policy()
535+
536+
# if no external hash has been provided, then just use the current hash from the doc.
537+
if current_hash is None:
538+
current_hash = current_resource_policy.get('PolicyHash')
539+
540+
glue_client.put_resource_policy(
541+
PolicyInJson=json.dumps(policy),
542+
PolicyHashCondition=current_hash,
543+
PolicyExistsCondition='MUST_EXIST',
544+
EnableHybrid='TRUE'
545+
)
530546
except glue_client.exceptions.EntityNotFoundException:
531-
pass
547+
# write the resource policy as new
548+
glue_client.put_resource_policy(
549+
PolicyInJson=json.dumps(policy),
550+
PolicyExistsCondition='NOT_EXIST',
551+
EnableHybrid='TRUE'
552+
)
553+
554+
def get_current_glue_policy(self):
555+
glue_client = self._get_client('glue')
556+
557+
try:
558+
current_resource_policy = glue_client.get_resource_policy()
559+
glue_policy = json.loads(current_resource_policy.get('PolicyInJson'))
560+
current_hash = current_resource_policy.get('PolicyHash')
561+
562+
return glue_policy, current_hash
563+
except glue_client.exceptions.EntityNotFoundException:
564+
return None, None
565+
566+
def add_tbac_glue_catalog_resource_policy(self, region: str, producer_account_id: str, consumer_account_id: str,
567+
database_name: str, tables: list):
568+
current_resource_policy, current_hash = self.get_current_glue_policy()
532569

533570
cf = {
534571
'region': region,
@@ -542,44 +579,33 @@ def update_glue_catalog_resource_policy(self, region: str, producer_account_id:
542579
cf['table_list'] = tables
543580
policy = json.loads(utils.generate_policy('lf_cross_account_tbac.pystache', config=cf))
544581

545-
policy_condition = None
546582
if current_resource_policy is None:
547583
new_resource_policy = {
548584
"Version": "2012-10-17",
549585
"Statement": policy
550586
}
551-
glue_client.put_resource_policy(
552-
PolicyInJson=json.dumps(new_resource_policy),
553-
PolicyExistsCondition='NOT_EXIST',
554-
EnableHybrid='TRUE'
587+
self.write_glue_catalog_resource_policy(
588+
policy=new_resource_policy
555589
)
556590
self._logger.info(
557591
f"Created new Catalog Resource Policy on {producer_account_id} allowing Tag Based Access by {consumer_account_id}")
558592
else:
559-
new_resource_policy = json.loads(current_resource_policy.get('PolicyInJson'))
560-
current_hash = current_resource_policy.get('PolicyHash')
561-
562593
update_statement, policy_index, did_modification = self._get_glue_resource_policy_statement_to_modify(
563594
region=region,
564-
policy=new_resource_policy, producer_account_id=producer_account_id,
595+
policy=current_resource_policy, producer_account_id=producer_account_id,
565596
consumer_account_id=consumer_account_id,
566597
database_name=database_name, tables=tables
567598
)
568599

569600
# add the new statement
570601
if update_statement is None:
571-
new_resource_policy['Statement'].append(policy)
602+
current_resource_policy['Statement'].append(policy)
572603
did_modification = True
573604
elif update_statement is not None:
574-
new_resource_policy['Statement'][policy_index] = update_statement
605+
current_resource_policy['Statement'][policy_index] = update_statement
575606

576607
if did_modification is True:
577-
glue_client.put_resource_policy(
578-
PolicyInJson=json.dumps(new_resource_policy),
579-
PolicyHashCondition=current_hash,
580-
PolicyExistsCondition='MUST_EXIST',
581-
EnableHybrid='TRUE'
582-
)
608+
self.write_glue_catalog_resource_policy(current_hash=current_hash, policy=current_resource_policy)
583609
self._logger.info(
584610
f"Updated Catalog Resource Policy on {producer_account_id} allowing Tag Based Access by {consumer_account_id}")
585611

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"Effect": "Allow",
3+
"Action": [
4+
"glue:ShareResource"
5+
],
6+
"Principal": {
7+
"Service": [
8+
"ram.amazonaws.com"
9+
]
10+
},
11+
"Resource": [
12+
"arn:aws:glue:{{region}}:{{account_id}}:table/*/*",
13+
"arn:aws:glue:{{region}}:{{account_id}}:database/*",
14+
"arn:aws:glue:{{region}}:{{account_id}}:catalog"
15+
]
16+
}

0 commit comments

Comments
 (0)